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("\""); } __namespaces__.metaLanguage.primary.prepend("fstring"); nonSpaceEatingId = parseDefinition( "nonSpaceEatingId = < LETTER ALNUM* > { intern(yytext) } "); nonSpaceEatingBlock = parseDefinition( "nonSpaceEatingBlock = LBRACE b:mkobj ( e:stmt { b.push(e) } )* \"}\" { b } "); fStringChar = parseDefinition( "fStringChar = < (!\"\\\"\" !\"$\" char )+ > { yytext } | \"\\\\$\" { \"$\" } "); 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 metaLanguage.addRule(nonSpaceEatingBlock.name, nonSpaceEatingBlock.expression); metaLanguage.addRule(nonSpaceEatingId.name, nonSpaceEatingId.expression); metaLanguage.addRule(fStringChar.name, fStringChar.expression); metaLanguage.addRule(fStringRule.name, fStringRule.expression); metaLanguage.addRule(#primary, __namespaces__.metaLanguage.primary); 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 << 24) } 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"); //}