Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions TestHScript.hx
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,14 @@ class TestHScript extends TestCase {
assertScript('var newMap = [{a:"a"}=>"foo", objKey=>"bar"]; newMap[objKey];', 'bar', vars);
}

function testStringInterpolation():Void {
assertScript("var a = 5; 'a is ${a}'", 'a is 5');
assertScript("var a = 5; 'a is ${a + 1}'", 'a is 6');
assertScript("var a = 5; 'a is ${if (a > 3) \"big\" else \"small\"}'", 'a is big');
assertScript("var a = 5; 'a is ${switch (a) { case 0: \"zero\"; case 5: \"five\"; default: \"other\"; }}'", 'a is five');
assertScript("'Hello, ${{var val = false; if (val) \"world\" else {var num = 5 + 3; 'userid_${num}';}}}!'", 'Hello, userid_8!');
}

static function main() {
#if ((haxe_ver < 4) && php)
// uncaught exception: The each() function is deprecated. This message will be suppressed on further calls (errno: 8192)
Expand Down
7 changes: 6 additions & 1 deletion hscript/Expr.hx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@
*/
package hscript;

enum StringKind {
DoubleQuotes;
SingleQuotes;
}

enum Const {
CInt( v : Int );
CFloat( f : Float );
CString( s : String );
CString( s : String , ?kind : StringKind);
}

#if hscriptPos
Expand Down
188 changes: 186 additions & 2 deletions hscript/Parser.hx
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,19 @@ class Parser {
e = mk(EIdent(id));
return isBlock(e) ? e : parseExprNext(e);
case TConst(c):
if (c.match(CString(_, SingleQuotes)))
{
var ex = makeInterpolatedStr(tk);
Comment thread
Davvex87 marked this conversation as resolved.
Outdated
if (ex.length == 1)
return parseExprNext(ex[0]);
else
{
var result = ex[0];
for (i in 1...ex.length)
result = makeBinop("+", result, ex[i]);
return parseExprNext(mk(EParent(result), p1));
}
}
return parseExprNext(mk(EConst(c)));
case TPOpen:
tk = token();
Expand Down Expand Up @@ -644,6 +657,143 @@ class Parser {
}
}

function makeInterpolatedStr(tk:Token):Array<Expr> {
var ex = new Array();
var s = switch(tk)
{
case TConst(c):
switch(c)
{
case CString(s):
s;
case _:
null;
}
case _:
null;
}
Comment thread
Davvex87 marked this conversation as resolved.
Outdated

if (s == null)
error(ECustom("Invalid string literal"), tokenMin, tokenMax);

var nextStr = "";
function pushNextStr() {
if (nextStr != "") {
ex.push(mk(EConst(CString(nextStr)), tokenMin, tokenMax));
nextStr = "";
}
}

var i = 0;
var c = s.charCodeAt(i);
Comment thread
Davvex87 marked this conversation as resolved.
Outdated
while (true)
{
if (StringTools.isEof(c))
{
pushNextStr();
break;
}

if (c != "$".code)
{
nextStr += String.fromCharCode(c);
Comment thread
Davvex87 marked this conversation as resolved.
Outdated
c = s.charCodeAt(++i);
continue;
}

c = s.charCodeAt(++i);
if (c >= 48 && c <= 57 || c == "$".code)
Comment thread
Davvex87 marked this conversation as resolved.
Outdated
{
nextStr += "$" + String.fromCharCode(c);
c = s.charCodeAt(++i);
continue;
}

if( idents[c] ) {
var id = String.fromCharCode(c);
while( true ) {
c = s.charCodeAt(++i);
if( StringTools.isEof(c) ) c = 0;
if( !idents[c] ) {
break;
}
id += String.fromCharCode(c);
}
pushNextStr();
ex.push(mk(EIdent(id), tokenMin, tokenMax));
nextStr = "";
}
else if (c == "{".code)
{
pushNextStr();
nextStr = "";
var lastInput = input;
var lastReadPos = readPos;
var lastChar = char;
var lastTokens = tokens;

function reset()
{
input = s;
readPos = i;
char = -1;
#if hscriptPos
tokens = new List();
#else
tokens = new haxe.ds.GenericStack<Token>();
#end
}
reset();
var brOpens = -1;
while( true ) {
var tk = token();
if ( tk == TBrOpen )
{
if (brOpens == -1)
brOpens = 0;
brOpens++;
}
else if ( tk == TBrClose )
{
if (brOpens == -1)
unexpected(tk);
brOpens--;
if (brOpens <= 0)
break;
}
if( tk == TEof )
error(EUnterminatedString, currentPos, currentPos);
}
var endPos = readPos - 1;
reset();
// cool hack to quickly close expressions
input = s.substring(i, endPos) + ";}";
Comment thread
Davvex87 marked this conversation as resolved.
Outdated
readPos = 0;
Comment thread
Davvex87 marked this conversation as resolved.
var a = new Array();
while( true ) {
var tk = token();
if( tk == TEof ) break;
push(tk);
parseFullExpr(a);
}
pushNextStr();
ex.push(mk(EBlock(a),0));
nextStr = "";
input = lastInput;
i = endPos + 1;
c = s.charCodeAt(i);
readPos = lastReadPos;
char = lastChar;
tokens = lastTokens;
}
else
{
nextStr += "$";
}
}
return ex;
}

function parseStructure(id) {
#if hscriptPos
var p1 = tokenMin;
Expand Down Expand Up @@ -1385,6 +1535,10 @@ class Parser {
inline function readChar() {
return StringTools.fastCodeAt(input, readPos++);
}

inline function peekChar() {
return StringTools.fastCodeAt(input, readPos);
}

function readString( until ) {
var c = 0;
Expand Down Expand Up @@ -1438,7 +1592,36 @@ class Parser {
esc = true;
else if( c == until )
break;
else {
else if (c == '$'.code && peekChar() == '{'.code)
Comment thread
Davvex87 marked this conversation as resolved.
Outdated
{
b.addChar('$'.code);
var brOpens = -1;
while (true)
{
c = readChar();
if (StringTools.isEof(c))
{
line = old;
error(EUnterminatedString, p1, p1);
break;
}
b.addChar(c);
if ( c == '{'.code )
{
if (brOpens == -1)
brOpens = 0;
brOpens++;
}
else if ( c == '}'.code )
{
if (brOpens == -1)
invalidChar(c);
brOpens--;
if (brOpens <= 0)
break;
}
}
} else {
if( c == 10 ) line++;
b.addChar(c);
}
Expand Down Expand Up @@ -1589,7 +1772,8 @@ class Parser {
case "}".code: return TBrClose;
case "[".code: return TBkOpen;
case "]".code: return TBkClose;
case "'".code, '"'.code: return TConst( CString(readString(char)) );
case "'".code: return TConst( CString(readString(char), SingleQuotes) );
case '"'.code: return TConst( CString(readString(char), DoubleQuotes) );
case "?".code:
char = readChar();
if( char == ".".code )
Expand Down