Skip to content

Commit

Permalink
new fucntons: better format (using template) and tostring (#66)
Browse files Browse the repository at this point in the history
Current `format` function just transform all values to string and
concat. It has been removed in current form (breaking change).

Instead, a new `format` function is introduce which use a template:
* `format("{0}-{1}", "Debug", "x64")`: use indexes for replacement
* `format("{flavour}-{arch}", { flavour: "Debug" arch: "x64" })`: use
key from map for replacement

Also, `format` was a concat with value conversion instead: function
`tostring` has been introduced to convert a value to string. To convert
current `format` usage, following example: `format("toto", 42)`
can be rewritten as `"toto" + tostring(42)`
  • Loading branch information
pchalamet authored Dec 14, 2024
1 parent c5efff9 commit a61903a
Show file tree
Hide file tree
Showing 16 changed files with 888 additions and 748 deletions.
3 changes: 3 additions & 0 deletions src/Terrabuild.Common/String.fs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ let startsWith (start: string) (s: string) =

let trim (s: string) =
s.Trim()

let replace (substring: string) (value: string) (s: string) =
s.Replace(substring, value)
1 change: 1 addition & 0 deletions src/Terrabuild.Configuration.Tests/TestFiles/WORKSPACE2
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ configuration {
secret2: $list.2
secret3: !false + !true
secret4: format("1", 2, $toto, true, nothing)
secret5: tostring(42)
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/Terrabuild.Configuration.Tests/Workspace.fs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,9 @@ let parseWorkspace2() =
Expr.Number 2
Expr.Variable "toto"
Expr.Bool true
Expr.Nothing
]) ] }
Expr.Nothing ])
"secret5", Expr.Function (Function.ToString, [Expr.Number 42])
] }

let extDotnet =
{ Container = Some "mcr.microsoft.com/dotnet/sdk:8.0.101"
Expand Down
299 changes: 159 additions & 140 deletions src/Terrabuild.Configuration/Gen/ProjectLexer.fs

Large diffs are not rendered by default.

496 changes: 257 additions & 239 deletions src/Terrabuild.Configuration/Gen/ProjectParser.fs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/Terrabuild.Configuration/Gen/ProjectParser.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type token =
| COUNT
| VERSION
| FORMAT
| TOSTRING
| MINUS
| PLUS
| COMMA
Expand Down Expand Up @@ -84,6 +85,7 @@ type tokenId =
| TOKEN_COUNT
| TOKEN_VERSION
| TOKEN_FORMAT
| TOKEN_TOSTRING
| TOKEN_MINUS
| TOKEN_PLUS
| TOKEN_COMMA
Expand Down
285 changes: 152 additions & 133 deletions src/Terrabuild.Configuration/Gen/WorkspaceLexer.fs

Large diffs are not rendered by default.

454 changes: 236 additions & 218 deletions src/Terrabuild.Configuration/Gen/WorkspaceParser.fs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/Terrabuild.Configuration/Gen/WorkspaceParser.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type token =
| COUNT
| VERSION
| FORMAT
| TOSTRING
| MINUS
| PLUS
| COMMA
Expand Down Expand Up @@ -78,6 +79,7 @@ type tokenId =
| TOKEN_COUNT
| TOKEN_VERSION
| TOKEN_FORMAT
| TOKEN_TOSTRING
| TOKEN_MINUS
| TOKEN_PLUS
| TOKEN_COMMA
Expand Down
1 change: 1 addition & 0 deletions src/Terrabuild.Configuration/ProjectParser/Lexer.fsl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ rule token = parse
| "count" { COUNT }
| "version" { VERSION }
| "format" { FORMAT }
| "tostring" { TOSTRING }
| "??" { DOUBLE_QUESTION }
| "?" { QUESTION }
| ".?" { DOT_QUESTION }
Expand Down
3 changes: 2 additions & 1 deletion src/Terrabuild.Configuration/ProjectParser/Parser.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ let debugPrint s = ignore s
%token EQUAL DOUBLE_EQUAL NOT_EQUAL
%token COMMA
%token MINUS PLUS
%token TRIM UPPER LOWER REPLACE COUNT VERSION FORMAT
%token TRIM UPPER LOWER REPLACE COUNT VERSION FORMAT TOSTRING
%token DOUBLE_QUESTION QUESTION COLON BANG
%token EOF
%token PROJECT EXTENSION TARGET
Expand Down Expand Up @@ -149,6 +149,7 @@ Expr:
| COUNT ExprTuple { Expr.Function (Function.Count, $2)}
| VERSION ExprTuple { Expr.Function (Function.Version, $2) }
| FORMAT ExprTuple { Expr.Function (Function.Format, $2) }
| TOSTRING ExprTuple { Expr.Function (Function.ToString, $2) }
| Expr DOUBLE_QUESTION Expr { Expr.Function (Function.Coalesce, [$1; $3]) }
| Expr QUESTION Expr COLON Expr { Expr.Function (Function.Ternary, [$1; $3; $5] ) }
| BANG Expr { Expr.Function (Function.Not, [$2]) }
Expand Down
1 change: 1 addition & 0 deletions src/Terrabuild.Configuration/WorkspaceParser/Lexer.fsl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ rule token = parse
| "count" { COUNT }
| "version" { VERSION }
| "format" { FORMAT }
| "tostring" { TOSTRING }
| "??" { DOUBLE_QUESTION }
| "?" { QUESTION }
| ".?" { DOT_QUESTION }
Expand Down
3 changes: 2 additions & 1 deletion src/Terrabuild.Configuration/WorkspaceParser/Parser.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ let debugPrint s = ignore s
%token EQUAL DOUBLE_EQUAL NOT_EQUAL
%token COMMA
%token MINUS PLUS
%token TRIM UPPER LOWER REPLACE COUNT VERSION FORMAT
%token TRIM UPPER LOWER REPLACE COUNT VERSION FORMAT TOSTRING
%token DOUBLE_QUESTION QUESTION COLON BANG
%token EOF
%token WORKSPACE TARGET CONFIGURATION EXTENSION
Expand Down Expand Up @@ -134,6 +134,7 @@ Expr:
| COUNT ExprTuple { Expr.Function (Function.Count, $2)}
| VERSION ExprTuple { Expr.Function (Function.Version, $2) }
| FORMAT ExprTuple { Expr.Function (Function.Format, $2) }
| TOSTRING ExprTuple { Expr.Function (Function.ToString, $2) }
| Expr DOUBLE_QUESTION Expr { Expr.Function (Function.Coalesce, [$1; $3]) }
| Expr QUESTION Expr COLON Expr { Expr.Function (Function.Ternary, [$1; $3; $5] ) }
| BANG Expr { Expr.Function (Function.Not, [$2]) }
Expand Down
25 changes: 23 additions & 2 deletions src/Terrabuild.Expressions.Tests/Eval.fs
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,17 @@ let version() =
result |> should equal expected

[<Test>]
let format() =
let expected = Value.String "\\o/42truetiti"
let formatList() =
let expected = Value.String "\\o/THIS42ISAtrueTEMPLATEtiti"
let expectedUsedVars = Set [ "toto" ]

let context = { evaluationContext
with Variables = Map ["toto", Value.String "\\o/" |> mkVar] }

// format("{0}THIS{1}IS{2}A{3}TEMPLATE{4}", $toto, 42, nothing, true, "titi")
let result, varUsed =
eval context (Expr.Function (Function.Format, [
Expr.String "{0}THIS{1}IS{2}A{3}TEMPLATE{4}"
Expr.Variable "toto"
Expr.Number 42
Expr.Nothing
Expand All @@ -183,6 +185,25 @@ let format() =
varUsed |> should equal expectedUsedVars
result |> should equal expected

[<Test>]
let formatMap() =
let expected = Value.String "THIS\\o/IS42ATEMPLATEtrue"
let expectedUsedVars = Set [ "args" ]

let context = { evaluationContext
with Variables = Map ["args", Value.Map (Map [ "string", Value.String "\\o/"
"number", Value.Number 42
"nothing", Value.Nothing
"bool", Value.Bool true ]) |> mkVar ] }

// format("THIS{string}IS{number}A{nothing}TEMPLATE{bool}", $args)
let result, varUsed =
eval context (Expr.Function (Function.Format, [
Expr.String "THIS{string}IS{number}A{nothing}TEMPLATE{bool}"
Expr.Variable "args" ]))
varUsed |> should equal expectedUsedVars
result |> should equal expected


[<Test>]
let listItem() =
Expand Down
55 changes: 43 additions & 12 deletions src/Terrabuild.Expressions/Eval.fs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ type EvaluationContext = {
}

let rec eval (context: EvaluationContext) (expr: Expr) =
let valueToString v =
match v with
| Value.Nothing -> ""
| Value.Bool b -> if b then "true" else "false"
| Value.Number n -> $"{n}"
| Value.String s -> s
| _ -> TerrabuildException.Raise($"Unsupported type for format {v}")

let rec eval (expr: Expr) =
match expr with
| Expr.Nothing -> Value.Nothing, Set.empty
Expand Down Expand Up @@ -76,18 +84,41 @@ let rec eval (context: EvaluationContext) (expr: Expr) =
| Some version -> Value.String version
| _ -> TerrabuildException.Raise($"Unknown project reference '{str}'")

| Function.Format, values ->
let formatValue v =
match v with
| Value.Nothing -> ""
| Value.Bool b -> if b then "true" else "false"
| Value.Number n -> $"{n}"
| Value.String s -> s
| _ -> TerrabuildException.Raise($"Unsupported type for format {v}")

values
|> List.fold (fun acc value -> $"{acc}{formatValue value}") ""
|> Value.String
| Function.ToString, [value] -> valueToString value |> Value.String

| Function.Format, [Value.String template; Value.Map values] ->
let rec replaceAll template =
match template with
| String.Regex "{([^}]+)}" [name] ->
let value =
match values |> Map.tryFind name with
| Some value -> valueToString value
| _ -> TerrabuildException.Raise($"Field {name} does not exist")
template
|> String.replace $"{{{name}}}" value
|> replaceAll
| _ -> template

replaceAll template |> Value.String

| Function.Format, Value.String template :: values ->
let values = values |> List.map valueToString

let rec replaceAll template =
match template with
| String.Regex "{([^}]+)}" [index] ->
let value =
match System.Int32.TryParse index with
| (true, index) ->
if 0 <= index && index < values.Length then values[index]
else TerrabuildException.Raise($"Format index is out of range")
| _ -> TerrabuildException.Raise($"Format index is not a number")
template
|> String.replace $"{{{index}}}" value
|> replaceAll
| _ -> template

replaceAll template |> Value.String

| Function.Item, [Value.Map map; Value.String key] ->
match map |> Map.tryFind key with
Expand Down
1 change: 1 addition & 0 deletions src/Terrabuild.Expressions/Expr.fs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Function =
| Count
| Version
| Format
| ToString
| Item
| TryItem
| Coalesce
Expand Down

0 comments on commit a61903a

Please sign in to comment.