Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
205 changes: 201 additions & 4 deletions hscript/Parser.hx
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,21 @@ class Parser {
e = mk(EIdent(id));
return isBlock(e) ? e : parseExprNext(e);
case TConst(c):
return parseExprNext(mk(EConst(c)));
switch( c ) {
case CString(s, SingleQuotes):
var ex = makeInterpolatedStr(s);
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));
}
default:
return parseExprNext(mk(EConst(c)));
}
case TPOpen:
tk = token();
if( tk == TPClose ) {
Expand Down Expand Up @@ -644,6 +658,155 @@ class Parser {
}
}

function makeInterpolatedStr(s:String):Array<Expr> {
var ex = new Array();

inline function newBuf()
return new StringBuf();

var nextStr = newBuf();
function pushNextStr() {
if (nextStr.length > 0) {
ex.push(mk(EConst(CString(nextStr.toString())), tokenMin, tokenMax));
nextStr = newBuf();
}
}

var i = 0;
var c = StringTools.fastCodeAt(s, i);
while (true)
{
if (StringTools.isEof(c))
{
pushNextStr();
break;
}

if (c != "$".code)
{
nextStr.addChar(c);
c = StringTools.fastCodeAt(s, ++i);
continue;
}

c = StringTools.fastCodeAt(s, ++i);
if ((c >= "0".code && c <= "9".code) || c == "$".code)
{
nextStr.add("$");
nextStr.addChar(c);
c = StringTools.fastCodeAt(s, ++i);
continue;
}

if( idents[c] ) {
var id = String.fromCharCode(c);
while( true ) {
c = StringTools.fastCodeAt(s, ++i);
if( StringTools.isEof(c) ) c = 0;
if( !idents[c] ) {
break;
}
id += String.fromCharCode(c);
}
pushNextStr();
ex.push(mk(EIdent(id), tokenMin, tokenMax));
nextStr = newBuf();
}
else if (c == "{".code)
{
pushNextStr();
nextStr = newBuf();
var lastInput = input;
var lastReadPos = readPos;
var lastChar = char;
var lastTokens = tokens;
var lastOffset = offset;
var lastLine = line;
#if hscriptPos
var lastTokenMin = tokenMin;
var lastTokenMax = tokenMax;
var lastOldTokenMin = oldTokenMin;
var lastOldTokenMax = oldTokenMax;
#end

function reset()
{
input = s;
readPos = i;
char = -1;
offset = lastOffset;
#if hscriptPos
tokens = new List();
tokenMin = oldTokenMin = currentPos;
tokenMax = oldTokenMax = currentPos;
#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();
var startPos = ++i;
input = s.substring(startPos, endPos);
readPos = 0;
offset = lastOffset + startPos;
#if hscriptPos
tokenMin = oldTokenMin = currentPos;
tokenMax = oldTokenMax = currentPos;
#end
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 = newBuf();
input = lastInput;
i = endPos + 1;
c = StringTools.fastCodeAt(s, i);
readPos = lastReadPos;
char = lastChar;
tokens = lastTokens;
offset = lastOffset;
line = lastLine;
#if hscriptPos
tokenMin = lastTokenMin;
tokenMax = lastTokenMax;
oldTokenMin = lastOldTokenMin;
oldTokenMax = lastOldTokenMax;
#end
}
else
{
nextStr.add("$");
}
}
return ex;
}

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

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

function readString( until ) {
function readString( until , allowInterpolation = false ) {
var c = 0;
var b = new StringBuf();
var esc = false;
Expand Down Expand Up @@ -1438,7 +1605,36 @@ class Parser {
esc = true;
else if( c == until )
break;
else {
else if (allowInterpolation && c == '$'.code && peekChar() == '{'.code)
{
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 +1785,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, true), SingleQuotes) );
case '"'.code: return TConst( CString(readString(char, false), DoubleQuotes) );
case "?".code:
char = readChar();
if( char == ".".code )
Expand Down