FormatString = Object.subtype(#FormatString); FormatString.new(elements) { self = super.new(); self.elements = elements; self; } FormatString.__eval__(env) { local result = ""; for(elem in self.elements) { local elemval = elem.__eval__(env); if (elemval.__name__ == #String) { result.push(elemval); } else { elemval.__codeon__(result); } } result; } FormatString.__codeon__(str) { str.push("f\""); for(elem in self.elements) { if (elem.__name__ == #String) { str.push(elem.escaped()); } else { str.push("$"); elem.__codeon__(str); } } str.push("\""); } nonSpaceEatingId = parseDefinition( "nonSpaceEatingId = < LETTER ALNUM* > { $$ = intern(yytext) } "); nonSpaceEatingBlock = parseDefinition( "nonSpaceEatingBlock = LBRACE b:mkobj ( e:stmt { $$ = b.push(e) } )* \"}\" { $$ = b } "); fStringChar = parseDefinition( "fStringChar = < (!\"\\\"\" !\"$\" char )+ > { $$ = yytext.unescaped() } | \"\\\\$\" { $$ = \"$\" } "); fStringRule = parseDefinition( "fstring = \"f\\\"\" elements:mkobj ( \"$\" i:nonSpaceEatingId { $$ = elements.push(GetVar.new(name: i)) } | \"$\" b:nonSpaceEatingBlock { $$ = elements.push(Block.new(body: b)) } | c:fStringChar { $$ = elements.push(c) } )* \"\\\"\" { $$ = FormatString.new(elements) } "); // regenerate the expression to be executed metaGrammar.addRule(nonSpaceEatingBlock.name, nonSpaceEatingBlock.expression); metaGrammar.addRule(nonSpaceEatingId.name, nonSpaceEatingId.expression); metaGrammar.addRule(fStringChar.name, fStringChar.expression); metaGrammar.addRule(fStringRule.name, fStringRule.expression); metaGrammar.primary.prepend("fstring"); formatInt(integer) { if (integer > 1000) { return f"${formatInt((integer - integer % 1000) / 1000)},${integer % 1000}"; } return f"$integer"; } a = 69; print(f"Reimu has $a power and ${6*7} point items and \$${ formatInt(1 << 32) } money.\n"); n = 100; print(f"Sum of integers from 1 to $n : ${local sum = 0; for (i in n + 1) sum += i}\n"); print(f"${formatInt.function}\n"); //{ // print(f"${__env__()}\n"); //}