Added String.fromCharCode, "a".code
Added a preprocessor
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C)2008-2017 Haxe Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package hscript;
|
||||
|
||||
#if hscriptPos
|
||||
class Error {
|
||||
public var e : ErrorDef;
|
||||
public var pmin : Int;
|
||||
public var pmax : Int;
|
||||
public var origin : String;
|
||||
public var line : Int;
|
||||
public function new(e, pmin, pmax, origin, line) {
|
||||
this.e = e;
|
||||
this.pmin = pmin;
|
||||
this.pmax = pmax;
|
||||
this.origin = origin;
|
||||
this.line = line;
|
||||
}
|
||||
public function toString(): String {
|
||||
return Printer.errorToString(this);
|
||||
}
|
||||
}
|
||||
enum ErrorDef
|
||||
#else
|
||||
enum Error
|
||||
#end
|
||||
{
|
||||
EInvalidChar( c : Int );
|
||||
EUnexpected( s : String );
|
||||
EUnterminatedString;
|
||||
EUnterminatedComment;
|
||||
EInvalidPreprocessor( msg : String );
|
||||
EUnknownVariable( v : String );
|
||||
EInvalidIterator( v : String );
|
||||
EInvalidOp( op : String );
|
||||
EInvalidAccess( f : String );
|
||||
ECustom( msg : String );
|
||||
EPreset( msg : ErrorMessage );
|
||||
EInvalidClass( className : String);
|
||||
EAlreadyExistingClass( className : String);
|
||||
EInvalidEscape( s : String );
|
||||
}
|
||||
|
||||
enum abstract ErrorMessage(String) to String {
|
||||
final INVALID_CHAR_CODE_MULTI = "'char'.code only works on single characters";
|
||||
final FROM_CHAR_CODE_NON_INT = "String.fromCharCode only works on integers";
|
||||
final EMPTY_INTERPOLATION = "Invalid interpolation: Expression cannot be empty";
|
||||
}
|
||||
@@ -139,44 +139,6 @@ enum CType {
|
||||
CTNamed( n : String, t : CType );
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
class Error {
|
||||
public var e : ErrorDef;
|
||||
public var pmin : Int;
|
||||
public var pmax : Int;
|
||||
public var origin : String;
|
||||
public var line : Int;
|
||||
public function new(e, pmin, pmax, origin, line) {
|
||||
this.e = e;
|
||||
this.pmin = pmin;
|
||||
this.pmax = pmax;
|
||||
this.origin = origin;
|
||||
this.line = line;
|
||||
}
|
||||
public function toString(): String {
|
||||
return Printer.errorToString(this);
|
||||
}
|
||||
}
|
||||
enum ErrorDef
|
||||
#else
|
||||
enum Error
|
||||
#end
|
||||
{
|
||||
EInvalidChar( c : Int );
|
||||
EUnexpected( s : String );
|
||||
EUnterminatedString;
|
||||
EUnterminatedComment;
|
||||
EInvalidPreprocessor( msg : String );
|
||||
EUnknownVariable( v : String );
|
||||
EInvalidIterator( v : String );
|
||||
EInvalidOp( op : String );
|
||||
EInvalidAccess( f : String );
|
||||
ECustom( msg : String );
|
||||
EInvalidClass( className : String);
|
||||
EAlreadyExistingClass( className : String);
|
||||
EInvalidEscape( s : String );
|
||||
}
|
||||
|
||||
enum ModuleDecl {
|
||||
DPackage( path : Array<String> );
|
||||
DImport( path : Array<String>, ?everything : Bool );
|
||||
|
||||
+6
-1
@@ -158,6 +158,11 @@ class Interp {
|
||||
variables.set("null", null);
|
||||
variables.set("true", true);
|
||||
variables.set("false", false);
|
||||
#if !NO_FROM_CHAR_CODE_FIX
|
||||
variables.set("__StringWorkaround__fromCharCode", function(a:Int) { // TODO: make hscript only add this if its used
|
||||
return String.fromCharCode(a);
|
||||
});
|
||||
#end
|
||||
variables.set("trace", Reflect.makeVarArgs(function(el) {
|
||||
var inf = posInfos();
|
||||
var v = el.shift();
|
||||
@@ -472,7 +477,7 @@ class Interp {
|
||||
}
|
||||
}
|
||||
|
||||
public inline function error(e:#if hscriptPos ErrorDef #else Error #end, rethrow = false):Dynamic {
|
||||
public inline function error(e:#if hscriptPos Error.ErrorDef #else Error #end, rethrow = false):Dynamic {
|
||||
#if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end
|
||||
|
||||
if (rethrow) {
|
||||
|
||||
@@ -163,6 +163,14 @@ class Parser {
|
||||
opPriority.set(x, x == "++" || x == "--" ? -1 : -2);
|
||||
}
|
||||
|
||||
public static inline function getBaseError( err ):Error {
|
||||
#if hscriptPos
|
||||
return new Error(err, 0, 0, "", 0);
|
||||
#else
|
||||
return err;
|
||||
#end
|
||||
}
|
||||
|
||||
public inline function error( err, pmin, pmax ) {
|
||||
if( !resumeErrors )
|
||||
#if hscriptPos
|
||||
@@ -215,6 +223,7 @@ class Parser {
|
||||
parseFullExpr(a);
|
||||
}
|
||||
var expr = if( a.length == 1 ) a[0] else mk(EBlock(a),0);
|
||||
expr = Preprocessor.process(expr);
|
||||
if(Parser.optimize) {
|
||||
expr = Optimizer.optimize(expr);
|
||||
var printer = new Printer();
|
||||
@@ -1880,6 +1889,9 @@ class Parser {
|
||||
interpString.push(arr);
|
||||
interpolation = false;
|
||||
|
||||
if(arr.length == 2) // If its just ()
|
||||
error(EPreset(EMPTY_INTERPOLATION), p1, p1);
|
||||
|
||||
if( StringTools.isEof(peekChar()) )
|
||||
break;
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package hscript;
|
||||
|
||||
import hscript.Expr;
|
||||
import hscript.Error;
|
||||
import hscript.Tools;
|
||||
import hscript.Parser;
|
||||
|
||||
@:access(hscript.Parser)
|
||||
class Preprocessor {
|
||||
static inline function expr(e:Expr) return Tools.expr(e);
|
||||
|
||||
/**
|
||||
* Preprocesses the expression, like 'a'.code => 97
|
||||
* Also for transforming any abstracts into their implementations (TODO)
|
||||
* Also for transforming any static extensions into their real form (TODO)
|
||||
**/
|
||||
public static function process(e:Expr, doBlock:Bool = true):Expr {
|
||||
if(e == null)
|
||||
return null;
|
||||
|
||||
trace(expr(e));
|
||||
|
||||
e = Tools.map(e, function(e) {
|
||||
return process(e);
|
||||
});
|
||||
|
||||
switch(expr(e)) {
|
||||
case EField(expr(_) => EConst(CString(s)), "code", _): // Transform string.code into charCode
|
||||
if(s.length != 1) {
|
||||
throw Parser.getBaseError(EPreset(INVALID_CHAR_CODE_MULTI));
|
||||
}
|
||||
return mk(EConst(CInt(s.charCodeAt(0))), e);
|
||||
case ECall(expr(_) => EField(expr(_) => EIdent("String"), "fromCharCode", _), [e]):
|
||||
switch(expr(e)) {
|
||||
case EConst(CInt(i)):
|
||||
return mk(EConst(CString(String.fromCharCode(i))), e);
|
||||
default:
|
||||
// __StringWorkaround__fromCharCode(i);
|
||||
#if !NO_FROM_CHAR_CODE_FIX
|
||||
return mk(ECall(mk(EIdent("__StringWorkaround__fromCharCode"), e), [e]), e);
|
||||
#else
|
||||
throw Parser.getBaseError(EPreset(FROM_CHAR_CODE_NON_INT));
|
||||
#end
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static function mk(e:ExprDef, s:Expr):Expr {
|
||||
#if hscriptPos
|
||||
return new Expr(e, s.pmin, s.pmax, s.origin, s.line);
|
||||
#else
|
||||
return e;
|
||||
#end
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -513,12 +513,16 @@ class Printer {
|
||||
}
|
||||
}
|
||||
|
||||
public static function toString( e : Expr ) {
|
||||
public inline static function toString( e : Expr ) {
|
||||
return new Printer().exprToString(e);
|
||||
}
|
||||
|
||||
public static function errorToString( e : Expr.Error ) {
|
||||
var message = switch( #if hscriptPos e.e #else e #end ) {
|
||||
public inline static function compareErrors( e1 : Error, e2 : Error ) {
|
||||
return Type.enumEq(Tools.cleanError(e1), Tools.cleanError(e2));
|
||||
}
|
||||
|
||||
public static function errorToString( e : Error ) {
|
||||
var message = switch( Tools.cleanError(e) ) {
|
||||
case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")";
|
||||
case EUnexpected(s): "Unexpected token: \""+s+"\"";
|
||||
case EUnterminatedString: "Unterminated string";
|
||||
@@ -529,6 +533,7 @@ class Printer {
|
||||
case EInvalidOp(op): "Invalid operator: "+op;
|
||||
case EInvalidAccess(f): "Invalid access to field " + f;
|
||||
case ECustom(msg): msg;
|
||||
case EPreset(msg): msg;
|
||||
case EInvalidClass(cla): "Invalid class: " + cla + " was not found.";
|
||||
case EAlreadyExistingClass(cla): 'Custom Class named $cla already exists.';
|
||||
case EInvalidEscape(s): "Invalid escape sequence: " + s;
|
||||
|
||||
@@ -107,6 +107,14 @@ class Tools {
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function cleanError( e : Error ) {
|
||||
#if hscriptPos
|
||||
return e.e;
|
||||
#else
|
||||
return e;
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function mk( e : ExprDef, p : Expr ):Expr {
|
||||
#if hscriptPos
|
||||
return new Expr(e, p.pmin, p.pmax, p.origin, p.line);
|
||||
|
||||
@@ -51,4 +51,24 @@ class HScriptRunner {
|
||||
else
|
||||
return interp.exprReturn(lastExpr);
|
||||
}
|
||||
|
||||
public function executeUnsafe(script:String):Dynamic {
|
||||
var interp = clearPrevious ? getNewInterp() : this.interp;
|
||||
lastExpr = Util.parseUnsafe(headerCode + script + tailCode);
|
||||
if (clearPrevious)
|
||||
return interp.execute(lastExpr);
|
||||
else
|
||||
return interp.exprReturn(lastExpr);
|
||||
}
|
||||
|
||||
public function executeWithVarsUnsafe(script:String, vars:Dynamic):Dynamic {
|
||||
var interp = clearPrevious ? getNewInterp() : this.interp;
|
||||
lastExpr = Util.parseUnsafe(headerCode + script + tailCode);
|
||||
for(v in Reflect.fields(vars))
|
||||
interp.variables.set(v, Reflect.field(vars, v));
|
||||
if (clearPrevious)
|
||||
return interp.execute(lastExpr);
|
||||
else
|
||||
return interp.exprReturn(lastExpr);
|
||||
}
|
||||
}
|
||||
+36
-45
@@ -1,4 +1,4 @@
|
||||
import hscript.Expr.Error;
|
||||
import hscript.Error;
|
||||
import hscript.Printer;
|
||||
import haxe.Constraints.IMap;
|
||||
import haxe.EnumTools.EnumValueTools;
|
||||
@@ -97,85 +97,65 @@ class Util {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static inline function passed() {
|
||||
passedTestUnits++;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static inline function failed() {
|
||||
failedTestUnits++;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static function assert(value:Bool, message:String, ?pos:haxe.PosInfos) {
|
||||
if (value) {
|
||||
passedTestUnits++;
|
||||
return true;
|
||||
return passed();
|
||||
} else {
|
||||
Sys.println("Assertion failed: " + message);
|
||||
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
|
||||
failedTestUnits++;
|
||||
return false;
|
||||
return failed();
|
||||
}
|
||||
}
|
||||
|
||||
public static function assertEq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
|
||||
var equals = deepEqual(value, expected);
|
||||
var passed = equals;
|
||||
//if (Std.isOfType(value, Array) && Std.isOfType(expected, Array)) {
|
||||
// if (deepCompareArrays(value, expected))
|
||||
// passed = true;
|
||||
//}
|
||||
//else if (Std.isOfType(value, Enum) && Std.isOfType(expected, Enum)) {
|
||||
// if(Type.enumEq(value, expected))
|
||||
// passed = true;
|
||||
//}
|
||||
var _passed = equals;
|
||||
|
||||
if (passed) {
|
||||
passedTestUnits++;
|
||||
return true;
|
||||
if (_passed) {
|
||||
return passed();
|
||||
} else {
|
||||
Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value);
|
||||
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
|
||||
failedTestUnits++;
|
||||
return false;
|
||||
return failed();
|
||||
}
|
||||
}
|
||||
|
||||
public static function assertEqPrintable(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
|
||||
//var passed = value == expected;
|
||||
//if (Std.isOfType(value, Array) && Std.isOfType(expected, Array)) {
|
||||
// if (deepCompareArrays(value, expected))
|
||||
// passed = true;
|
||||
//}
|
||||
//else if (Std.isOfType(value, Enum) && Std.isOfType(expected, Enum)) {
|
||||
// if(Type.enumEq(value, expected))
|
||||
// passed = true;
|
||||
//}
|
||||
var equals = deepEqual(value, expected);
|
||||
var passed = equals;
|
||||
var _passed = equals;
|
||||
|
||||
if (passed) {
|
||||
passedTestUnits++;
|
||||
return true;
|
||||
if (_passed) {
|
||||
return passed();
|
||||
} else {
|
||||
Sys.println("Assertion failed: " + message + " Expected: " + Printer.getEscapedString(expected) + " Got: " + Printer.getEscapedString(value));
|
||||
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
|
||||
failedTestUnits++;
|
||||
return false;
|
||||
return failed();
|
||||
}
|
||||
}
|
||||
|
||||
public static function assertNeq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
|
||||
//var passed = value != expected;
|
||||
//if (Std.isOfType(value, Array) && Std.isOfType(expected, Array)) {
|
||||
// if (!deepCompareArrays(value, expected))
|
||||
// passed = true;
|
||||
//}
|
||||
var equals = deepEqual(value, expected);
|
||||
var passed = !equals;
|
||||
var _passed = !equals;
|
||||
|
||||
// WARNING THIS MIGHT NOT WORK
|
||||
|
||||
if (passed) {
|
||||
passedTestUnits++;
|
||||
return true;
|
||||
if (_passed) {
|
||||
return passed();
|
||||
} else {
|
||||
Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value);
|
||||
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
|
||||
failedTestUnits++;
|
||||
return false;
|
||||
return failed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,6 +214,17 @@ class Util {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string without checking for errors
|
||||
*/
|
||||
public static function parseUnsafe(str:String) {
|
||||
var p = new Parser();
|
||||
p.allowTypes = true;
|
||||
p.allowMetadata = true;
|
||||
p.allowJSON = true;
|
||||
return p.parseString(str);
|
||||
}
|
||||
|
||||
public static function getInterp() {
|
||||
var interp = new Interp();
|
||||
//interp.importEnabled = true;
|
||||
|
||||
@@ -60,30 +60,6 @@ class StringCase extends TestCase {
|
||||
|
||||
headerCode = '';
|
||||
|
||||
Util.runKnownBug("string.code doesnt work", () -> {
|
||||
assertEq('"f".code', "f".code);
|
||||
assertEq('"o".code', "o".code);
|
||||
assertEq('"1".code', "1".code);
|
||||
assertEq('"b".code', "b".code);
|
||||
assertEq('"a".code', "a".code);
|
||||
assertEq('"r".code', "r".code);
|
||||
assertEq('"foo".code', null); // multiple chars causes error
|
||||
assertEq('"bar".code', null); // multiple chars causes error
|
||||
});
|
||||
|
||||
Util.runKnownBug("String.fromCharCode doesnt work", () -> {
|
||||
assertEq('String.fromCharCode(65)', String.fromCharCode(65));
|
||||
assertEq('String.fromCharCode(97)', String.fromCharCode(97));
|
||||
assertEq('String.fromCharCode(98)', String.fromCharCode(98));
|
||||
assertEq('String.fromCharCode(99)', String.fromCharCode(99));
|
||||
assertEq('String.fromCharCode(100)', String.fromCharCode(100));
|
||||
assertEq('String.fromCharCode(101)', String.fromCharCode(101));
|
||||
assertEq('String.fromCharCode(102)', String.fromCharCode(102));
|
||||
assertEq('String.fromCharCode(103)', String.fromCharCode(103));
|
||||
});
|
||||
|
||||
headerCode = '';
|
||||
|
||||
assertEq('("3" > "11")', ("3" > "11"));
|
||||
assertEq('(" 3" < "3")', (" 3" < "3"));
|
||||
assertEq('("a" < "b")', ("a" < "b"));
|
||||
@@ -153,6 +129,41 @@ class StringCase extends TestCase {
|
||||
|
||||
assertEq("'hello ${5} world'", 'hello ${5} world');
|
||||
assertEq("'hello ${5}'", 'hello ${5}');
|
||||
|
||||
//Util.runKnownBug("string.code doesnt work", () -> {
|
||||
assertEq('"f".code', "f".code);
|
||||
assertEq('"o".code', "o".code);
|
||||
assertEq('"1".code', "1".code);
|
||||
assertEq('"b".code', "b".code);
|
||||
assertEq('"a".code', "a".code);
|
||||
assertEq('"r".code', "r".code);
|
||||
assertEq('"r".code', "r".code);
|
||||
assertError('"foo".code', Parser.getBaseError(EPreset(INVALID_CHAR_CODE_MULTI))); // multiple chars causes error
|
||||
assertError('"bar".code', Parser.getBaseError(EPreset(INVALID_CHAR_CODE_MULTI))); // multiple chars causes error
|
||||
//});
|
||||
|
||||
headerCode = '';
|
||||
var a = 103;
|
||||
|
||||
//Util.runKnownBug("String.fromCharCode doesnt work", () -> {
|
||||
assertEq('String.fromCharCode(65)', String.fromCharCode(65));
|
||||
assertEq('String.fromCharCode(97)', String.fromCharCode(97));
|
||||
assertEq('String.fromCharCode(98)', String.fromCharCode(98));
|
||||
assertEq('String.fromCharCode(99)', String.fromCharCode(99));
|
||||
assertEq('String.fromCharCode(100)', String.fromCharCode(100));
|
||||
assertEq('String.fromCharCode(101)', String.fromCharCode(101));
|
||||
assertEq('String.fromCharCode(102)', String.fromCharCode(102));
|
||||
assertEq('String.fromCharCode(103)', String.fromCharCode(103));
|
||||
assertEq('var a = 103; String.fromCharCode(a)', String.fromCharCode(test));
|
||||
//});
|
||||
|
||||
assertError("'hello ${}'", Parser.getBaseError(EPreset(EMPTY_INTERPOLATION)));
|
||||
}
|
||||
|
||||
static var test = 103;
|
||||
|
||||
static function fromCharCode(a:Int) {
|
||||
return String.fromCharCode(a);
|
||||
}
|
||||
|
||||
override function teardown() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package tests;
|
||||
|
||||
import hscript.Printer;
|
||||
import hscript.Tools;
|
||||
|
||||
@:access(hscript.Interp)
|
||||
@:access(hscript.Parser)
|
||||
@@ -19,6 +20,32 @@ class TestCase extends HScriptRunner {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function assertError(script:String, expectedError:hscript.Error, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
|
||||
if(message == null)
|
||||
message = script;
|
||||
try {
|
||||
var result = if(vars != null)
|
||||
executeWithVarsUnsafe(script, vars);
|
||||
else
|
||||
executeUnsafe(script);
|
||||
Sys.println("# For script: " + script);
|
||||
Sys.println("## Expected error: " + Tools.cleanError(expectedError));
|
||||
Sys.println("## Got result: " + result);
|
||||
Sys.println("> " + Printer.convertExprToString(lastExpr));
|
||||
return Util.failed();
|
||||
} catch(e:hscript.Error) {
|
||||
if(Printer.compareErrors(e, expectedError)) {
|
||||
return Util.passed();
|
||||
} else {
|
||||
Sys.println("# For script: " + script);
|
||||
Sys.println("## Expected error: " + Tools.cleanError(expectedError));
|
||||
Sys.println("## Actual error: " + Tools.cleanError(e));
|
||||
return Util.failed();
|
||||
}
|
||||
}
|
||||
return Util.failed();
|
||||
}
|
||||
|
||||
public function assertEqPrintable(script:String, expected:Dynamic, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
|
||||
if(message == null)
|
||||
message = script;
|
||||
|
||||
Reference in New Issue
Block a user