feat: implement string interpolation with %(expr) syntax in compiler and core library
Add lexer support for `TOKEN_INTERPOLATION` to split string literals at interpolation points, introduce `MAX_INTERPOLATION_NESTING` limit of 8, and compile interpolated strings by emitting calls to `String.interpolate_()`. Optimize list and map literal construction with new `addCore_` primitives to reduce stack churn. Update `String`, `List`, and `Map` `toString` methods in core library to use interpolation syntax, and migrate all benchmark and test files from explicit concatenation to interpolated strings.
This commit is contained in:
+2
-2
@@ -8,7 +8,7 @@ class Api {
|
||||
}
|
||||
|
||||
static one(one) {
|
||||
System.print("one " + one.toString)
|
||||
System.print("one %(one)")
|
||||
}
|
||||
|
||||
static two(one, two) {
|
||||
@@ -17,7 +17,7 @@ class Api {
|
||||
two = two.bytes.toList
|
||||
}
|
||||
|
||||
System.print("two " + one.toString + " " + two.toString)
|
||||
System.print("two %(one) %(two)")
|
||||
}
|
||||
|
||||
static getValue(value) {
|
||||
|
||||
@@ -30,7 +30,7 @@ foreign class Point is PointBase {
|
||||
}
|
||||
|
||||
construct new(x, y, z) {
|
||||
System.print(x.toString + ", " + y.toString + ", " + z.toString)
|
||||
System.print("%(x), %(y), %(z)")
|
||||
}
|
||||
|
||||
foreign translate(x, y, z)
|
||||
|
||||
@@ -26,8 +26,8 @@ var stretchDepth = maxDepth + 1
|
||||
|
||||
var start = System.clock
|
||||
|
||||
System.print("stretch tree of depth " + stretchDepth.toString + " check: " +
|
||||
Tree.new(0, stretchDepth).check.toString)
|
||||
System.print("stretch tree of depth %(stretchDepth) check: " +
|
||||
"%(Tree.new(0, stretchDepth).check)")
|
||||
|
||||
var longLivedTree = Tree.new(0, maxDepth)
|
||||
|
||||
@@ -44,12 +44,11 @@ while (depth < stretchDepth) {
|
||||
check = check + Tree.new(i, depth).check + Tree.new(-i, depth).check
|
||||
}
|
||||
|
||||
System.print((iterations * 2).toString + " trees of depth " +
|
||||
depth.toString + " check: " + check.toString)
|
||||
System.print("%(iterations * 2) trees of depth %(depth) check: %(check)")
|
||||
iterations = iterations / 4
|
||||
depth = depth + 2
|
||||
}
|
||||
|
||||
System.print("long lived tree of depth " + maxDepth.toString + " check: " +
|
||||
longLivedTree.check.toString)
|
||||
System.print("elapsed: " + (System.clock - start).toString)
|
||||
System.print(
|
||||
"long lived tree of depth %(maxDepth) check: %(longLivedTree.check)")
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
|
||||
@@ -26,8 +26,8 @@ var stretchDepth = maxDepth + 1
|
||||
|
||||
var start = System.clock
|
||||
|
||||
System.print("stretch tree of depth " + stretchDepth.toString + " check: " +
|
||||
Tree.new(0, stretchDepth).check.toString)
|
||||
System.print("stretch tree of depth %(stretchDepth) check: " +
|
||||
"%(Tree.new(0, stretchDepth).check)")
|
||||
for (i in 1...1000) System.gc()
|
||||
|
||||
var longLivedTree = Tree.new(0, maxDepth)
|
||||
@@ -45,16 +45,15 @@ while (depth < stretchDepth) {
|
||||
check = check + Tree.new(i, depth).check + Tree.new(-i, depth).check
|
||||
}
|
||||
|
||||
System.print((iterations * 2).toString + " trees of depth " +
|
||||
depth.toString + " check: " + check.toString)
|
||||
System.print("%(iterations * 2) trees of depth %(depth) check: %(check)")
|
||||
for (i in 1...1000) System.gc()
|
||||
|
||||
iterations = iterations / 4
|
||||
depth = depth + 2
|
||||
}
|
||||
|
||||
System.print("long lived tree of depth " + maxDepth.toString + " check: " +
|
||||
longLivedTree.check.toString)
|
||||
System.print(
|
||||
"long lived tree of depth %(maxDepth) check: %(longLivedTree.check)")
|
||||
for (i in 1...1000) System.gc()
|
||||
|
||||
System.print("elapsed: " + (System.clock - start).toString)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
|
||||
@@ -696,4 +696,4 @@ for (i in 0...40) {
|
||||
}
|
||||
|
||||
System.print(total)
|
||||
System.print("elapsed: " + (System.clock - start).toString)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
|
||||
@@ -9,4 +9,4 @@ var start = System.clock
|
||||
for (i in 1..5) {
|
||||
System.print(Fib.get(28))
|
||||
}
|
||||
System.print("elapsed: " + (System.clock - start).toString)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
|
||||
@@ -13,4 +13,4 @@ for (i in 0...100000) {
|
||||
|
||||
fibers[0].call()
|
||||
System.print(sum)
|
||||
System.print("elapsed: " + (System.clock - start).toString)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
|
||||
@@ -7,4 +7,4 @@ var sum = 0
|
||||
for (i in list) sum = sum + i
|
||||
|
||||
System.print(sum)
|
||||
System.print("elapsed: " + (System.clock - start).toString)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
|
||||
@@ -16,4 +16,4 @@ for (i in 1..1000000) {
|
||||
map.remove(i)
|
||||
}
|
||||
|
||||
System.print("elapsed: " + (System.clock - start).toString)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
|
||||
@@ -97,4 +97,4 @@ for (key in keys) {
|
||||
}
|
||||
|
||||
System.print(sum)
|
||||
System.print("elapsed: " + (System.clock - start).toString)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
|
||||
@@ -65,4 +65,4 @@ for (i in 0...n) {
|
||||
}
|
||||
|
||||
System.print(ntoggle.value)
|
||||
System.print("elapsed: " + (System.clock - start).toString)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
|
||||
@@ -18,7 +18,7 @@ for i in range(0, 1000000):
|
||||
count = count + 1
|
||||
if "abc" == "abcd":
|
||||
count = count + 1
|
||||
if "changed one character" == "changed %ne character":
|
||||
if "changed one character" == "changed !ne character":
|
||||
count = count + 1
|
||||
if "123" == 123: count = count + 1
|
||||
if "a slightly longer string" == \
|
||||
|
||||
@@ -10,7 +10,7 @@ for (i in 1..1000000) {
|
||||
|
||||
if ("" == "abc") count = count + 1
|
||||
if ("abc" == "abcd") count = count + 1
|
||||
if ("changed one character" == "changed %ne character") count = count + 1
|
||||
if ("changed one character" == "changed !ne character") count = count + 1
|
||||
if ("123" == 123) count = count + 1
|
||||
if ("a slightly longer string" ==
|
||||
"a slightly longer string!") count = count + 1
|
||||
@@ -21,4 +21,4 @@ for (i in 1..1000000) {
|
||||
}
|
||||
|
||||
System.print(count)
|
||||
System.print("elapsed: " + (System.clock - start).toString)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
var f0 = Fn.new { System.print("zero") }
|
||||
var f1 = Fn.new {|a| System.print("one " + a) }
|
||||
var f2 = Fn.new {|a, b| System.print("two " + a + " " + b) }
|
||||
var f3 = Fn.new {|a, b, c| System.print("three " + a + " " + b + " " + c) }
|
||||
var f1 = Fn.new {|a| System.print("one %(a)") }
|
||||
var f2 = Fn.new {|a, b| System.print("two %(a) %(b)") }
|
||||
var f3 = Fn.new {|a, b, c| System.print("three %(a) %(b) %(c)") }
|
||||
|
||||
f0.call("a") // expect: zero
|
||||
f0.call("a", "b") // expect: zero
|
||||
|
||||
@@ -19,10 +19,10 @@ System.print({1: Foo.new()}) // expect: {1: Foo.toString}
|
||||
// will be.
|
||||
var s = {1: 2, 3: 4, 5: 6}.toString
|
||||
System.print(s == "{1: 2, 3: 4, 5: 6}" ||
|
||||
s == "{1: 2, 5: 6, 3: 4}" ||
|
||||
s == "{3: 4, 1: 2, 5: 6}" ||
|
||||
s == "{3: 4, 5: 6, 1: 2}" ||
|
||||
s == "{5: 6, 1: 2, 3: 4}" ||
|
||||
s == "{5: 6, 3: 4, 1: 2}") // expect: true
|
||||
s == "{1: 2, 5: 6, 3: 4}" ||
|
||||
s == "{3: 4, 1: 2, 5: 6}" ||
|
||||
s == "{3: 4, 5: 6, 1: 2}" ||
|
||||
s == "{5: 6, 1: 2, 3: 4}" ||
|
||||
s == "{5: 6, 3: 4, 1: 2}") // expect: true
|
||||
|
||||
// TODO: Handle maps that contain themselves.
|
||||
// TODO: Handle maps that contain themselves.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import "io" for Stdin
|
||||
|
||||
System.write("> ")
|
||||
System.print("1 " + Stdin.readLine())
|
||||
System.print("1 %(Stdin.readLine())")
|
||||
System.write("> ")
|
||||
System.print("2 " + Stdin.readLine())
|
||||
System.print("2 %(Stdin.readLine())")
|
||||
|
||||
// stdin: first
|
||||
// stdin: second
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
for (i in 0..2) {
|
||||
System.print("outer " + i.toString)
|
||||
System.print("outer %(i)")
|
||||
if (i > 1) break
|
||||
|
||||
for (j in 0..2) {
|
||||
System.print("inner " + j.toString)
|
||||
System.print("inner %(j)")
|
||||
if (j > 1) break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
var i = 0
|
||||
while (true) {
|
||||
System.print("outer " + i.toString)
|
||||
System.print("outer %(i)")
|
||||
if (i > 1) break
|
||||
|
||||
var j = 0
|
||||
while (true) {
|
||||
System.print("inner " + j.toString)
|
||||
System.print("inner %(j)")
|
||||
if (j > 1) break
|
||||
|
||||
j = j + 1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class A {
|
||||
construct new(arg) {
|
||||
System.print("new A " + arg)
|
||||
System.print("new A %(arg)")
|
||||
_field = arg
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ class A {
|
||||
class B is A {
|
||||
construct new(arg1, arg2) {
|
||||
super(arg2)
|
||||
System.print("new B " + arg1)
|
||||
System.print("new B %(arg1)")
|
||||
_field = arg1
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// expect error line 3
|
||||
" %() "
|
||||
@@ -0,0 +1,15 @@
|
||||
// Full string.
|
||||
System.print("%(1 + 2)") // expect: 3
|
||||
|
||||
// Multiple in one string.
|
||||
System.print("str%(1 + 2)(%(3 + 4)\%%(5 + 6)") // expect: str3(7%11
|
||||
|
||||
// Nested.
|
||||
System.print("[%("{%("in" + "ner")}")]") // expect: [{inner}]
|
||||
|
||||
// Ignore newlines in template.
|
||||
System.print("[%(
|
||||
|
||||
"template"
|
||||
|
||||
)]") // expect: [template]
|
||||
@@ -0,0 +1 @@
|
||||
System.print("%(123.badMethod)") // expect runtime error: Num does not implement 'badMethod'.
|
||||
@@ -0,0 +1,8 @@
|
||||
var fiber = Fiber.new {
|
||||
System.print("in fiber")
|
||||
Fiber.yield("result")
|
||||
}
|
||||
|
||||
System.print("outer %(fiber.call()) string")
|
||||
// expect: in fiber
|
||||
// expect: outer result string
|
||||
@@ -0,0 +1,2 @@
|
||||
" %(
|
||||
// expect error
|
||||
@@ -0,0 +1,2 @@
|
||||
// expect error line 2
|
||||
" %(123"
|
||||
@@ -1,7 +1,7 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
method(a, b) { "method " + a + " " + b }
|
||||
[a, b] { "subscript " + a + " " + b }
|
||||
method(a, b) { "method %(a) %(b)" }
|
||||
[a, b] { "subscript %(a) %(b)" }
|
||||
}
|
||||
|
||||
var foo = Foo.new()
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
+(other) { "infix + " + other }
|
||||
-(other) { "infix - " + other }
|
||||
*(other) { "infix * " + other }
|
||||
/(other) { "infix / " + other }
|
||||
%(other) { "infix % " + other }
|
||||
<(other) { "infix < " + other }
|
||||
>(other) { "infix > " + other }
|
||||
<=(other) { "infix <= " + other }
|
||||
>=(other) { "infix >= " + other }
|
||||
==(other) { "infix == " + other }
|
||||
!=(other) { "infix != " + other }
|
||||
&(other) { "infix & " + other }
|
||||
|(other) { "infix | " + other }
|
||||
is(other) { "infix is " + other }
|
||||
+(other) { "infix + %(other)" }
|
||||
-(other) { "infix - %(other)" }
|
||||
*(other) { "infix * %(other)" }
|
||||
/(other) { "infix / %(other)" }
|
||||
%(other) { "infix \% %(other)" }
|
||||
<(other) { "infix < %(other)" }
|
||||
>(other) { "infix > %(other)" }
|
||||
<=(other) { "infix <= %(other)" }
|
||||
>=(other) { "infix >= %(other)" }
|
||||
==(other) { "infix == %(other)" }
|
||||
!=(other) { "infix != %(other)" }
|
||||
&(other) { "infix & %(other)" }
|
||||
|(other) { "infix | %(other)" }
|
||||
is(other) { "infix is %(other)" }
|
||||
|
||||
! { "prefix !" }
|
||||
~ { "prefix ~" }
|
||||
|
||||
@@ -3,8 +3,8 @@ class Foo {
|
||||
bar { "on instance" }
|
||||
static bar { "on metaclass" }
|
||||
|
||||
bar(arg) { "on instance " + arg }
|
||||
static bar(arg) { "on metaclass " + arg }
|
||||
bar(arg) { "on instance %(arg)" }
|
||||
static bar(arg) { "on metaclass %(arg)" }
|
||||
}
|
||||
|
||||
System.print(Foo.new().bar) // expect: on instance
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
[a] { "1-subscript " + a }
|
||||
[a, b] { "2-subscript " + a + " " + b }
|
||||
[a, b, c] { "3-subscript " + a + " " + b + " " + c }
|
||||
[a]=(value) { "1-subscript setter " + a + " = " + value }
|
||||
[a, b]=(value) { "2-subscript setter " + a + " " + b + " = " + value }
|
||||
[a, b, c]=(value) { "3-subscript setter " + a + " " + b + " " + c + " = " + value }
|
||||
[a] { "1-subscript %(a)" }
|
||||
[a, b] { "2-subscript %(a) %(b)" }
|
||||
[a, b, c] { "3-subscript %(a) %(b) %(c)" }
|
||||
[a]=(value) { "1-subscript setter %(a) = %(value)" }
|
||||
[a, b]=(value) { "2-subscript setter %(a) %(b) = %(value)" }
|
||||
[a, b, c]=(value) { "3-subscript setter %(a) %(b) %(c) = %(value)" }
|
||||
}
|
||||
|
||||
var foo = Foo.new()
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
System.print("start a")
|
||||
|
||||
var A = "a value"
|
||||
System.print("a defined " + A)
|
||||
System.print("a defined %(A)")
|
||||
import "b" for B
|
||||
System.print("a imported " + B)
|
||||
System.print("a imported %(B)")
|
||||
|
||||
System.print("end a")
|
||||
System.print("end a")
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
System.print("start b")
|
||||
|
||||
var B = "b value"
|
||||
System.print("b defined " + B)
|
||||
System.print("b defined %(B)")
|
||||
import "a" for A
|
||||
System.print("b imported " + A)
|
||||
System.print("b imported %(A)")
|
||||
|
||||
System.print("end b")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// nontest
|
||||
System.print("a")
|
||||
import "shared" for Shared
|
||||
var A = "a " + Shared
|
||||
var A = "a %(Shared)"
|
||||
System.print("a done")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// nontest
|
||||
System.print("b")
|
||||
import "shared" for Shared
|
||||
var B = "b " + Shared
|
||||
var B = "b %(Shared)"
|
||||
System.print("b done")
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
System.print("\"") // expect: "
|
||||
System.print("\\") // expect: \
|
||||
System.print("(\n)") // expect: (
|
||||
// expect: )
|
||||
// expect: )
|
||||
System.print("\%") // expect: %
|
||||
|
||||
// TODO: Non-printing escapes like \t.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
System.print("0 %("1 %("2 %("3 %("4 %("5 %("6 %("7 %(8)")")")")")")")") // expect: 0 1 2 3 4 5 6 7 8
|
||||
@@ -0,0 +1 @@
|
||||
System.print("0 %("1 %("2 %("3 %("4 %("5 %("6 %("7 %("8 %(9)")")")")")")")")") // expect error
|
||||
Reference in New Issue
Block a user