From 430e40a7b365e47b8994c6c49256e16ebacebe8d Mon Sep 17 00:00:00 2001 From: odino Date: Mon, 26 Aug 2019 17:30:17 +0400 Subject: [PATCH] Bas-style interpolation in strings, closes #169 Now you can embed variables inside strings by prefixing them with `$`. Literal `$` simply need a `\`. This aligns the string interpolation syntax with commands, and makes it easier to switch between running a command (`` `$some $command` ``) and echoing it (`echo("$some $command")`). --- README.md | 2 +- docs/README.md | 2 +- docs/types/string.md | 19 +++++++++++++++++++ evaluator/evaluator.go | 2 +- evaluator/evaluator_test.go | 16 ++++++++++++++++ examples/ip-sum.abs | 2 +- scripts/release.abs | 4 ++-- 7 files changed, 41 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index adca0011..63684f62 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ if !res.ok { ip = res.json().ip total = ip.split(".").map(int).sum() if total > 100 { - echo("The sum of [%s] is a large number, %s.", ip, total) + echo("The sum of [$ip] is a large number, $total.") } ``` diff --git a/docs/README.md b/docs/README.md index 97384915..e2cb260e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -80,7 +80,7 @@ if !res.ok { ip = res.json().ip total = ip.split(".").map(int).sum() if total > 100 { - echo("The sum of [%s] is a large number, %s.", ip, total) + echo("The sum of [$ip] is a large number, $total.") } ``` diff --git a/docs/types/string.md b/docs/types/string.md index ca1adc43..4266628a 100644 --- a/docs/types/string.md +++ b/docs/types/string.md @@ -83,6 +83,25 @@ To test for the existence of substrings within strings use the `in` operator: "xyz" in "string" # false ``` +## Interpolation + +You can also replace parts of the string with variables +declared within your program using the `$` symbol: + +``` bash +file = "/etc/hosts" +x = "File name is: $file" +echo(x) # "File name is: /etc/hosts" +``` + +If you need `$` literals in your command, you +simply need to escape them with a `\`: + +``` bash +"$non_existing_var" # "" since the ABS variable 'non_existing_var' doesn't exist +"\$non_existing_var" # "$non_existing_var" +``` + ## Special characters embedded in strings Double and single quoted strings behave differently if the string contains diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index c6f8bb0d..2eac6cfd 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -93,7 +93,7 @@ func Eval(node ast.Node, env *object.Environment) object.Object { return NULL case *ast.StringLiteral: - return &object.String{Token: node.Token, Value: node.Value} + return &object.String{Token: node.Token, Value: util.InterpolateStringVars(node.Value, env)} case *ast.Boolean: return nativeBoolToBooleanObject(node.Value) diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go index e2adb047..c970b986 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -365,6 +365,22 @@ func TestStringWriters(t *testing.T) { } } +func TestStringInterpolation(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {`a = "123"; "abc$a"`, "abc123"}, + {`a = "123"; "abc\$a"`, "abc$a"}, + {`a = "123"; "$$a$$a$$a"`, "$123$123$123"}, + } + + for _, tt := range tests { + evaluated := testEval(tt.input) + testStringObject(t, evaluated, tt.expected) + } +} + func TestForInExpressions(t *testing.T) { tests := []struct { input string diff --git a/examples/ip-sum.abs b/examples/ip-sum.abs index 5248021f..2c1d01ea 100644 --- a/examples/ip-sum.abs +++ b/examples/ip-sum.abs @@ -8,5 +8,5 @@ if !res.ok { ip = res.json().ip total = ip.split(".").map(int).sum() if total > 100 { - echo("The sum of [%s] is a large number, %s.", ip, total) + echo("The sum of [$ip] is a large number, $total.") } \ No newline at end of file diff --git a/scripts/release.abs b/scripts/release.abs index d115bbda..1c7dd021 100644 --- a/scripts/release.abs +++ b/scripts/release.abs @@ -54,7 +54,7 @@ if !rm.ok { version = `cat ./main.go | grep "var Version"` version = version.slice(15, len(version) - 1) -echo("Running builds for version %s, confirm by typing \"y\"".fmt(version)) +echo("Running builds for version $version, confirm by typing \"y\"") selection = stdin() if selection != "y" { @@ -63,7 +63,7 @@ if selection != "y" { for platform in platforms { goos, goarch = platform.split("/") - output_name = "builds/abs-%s-%s-%s".fmt(version, goos, goarch) + output_name = "builds/abs-$version-$goos-$goarch" entry_point = "main.go" if goos == "windows" {