added -D hscriptPos support, added Macro to convert hscript expr to haxe.macro.Expr

This commit is contained in:
Nicolas Cannasse
2011-03-19 17:52:46 +00:00
parent 78a18acf07
commit 46f157bb39
3 changed files with 408 additions and 57 deletions
+23
View File
@@ -31,7 +31,16 @@ enum Const {
CInt32( v : haxe.Int32 );
}
#if hscriptPos
typedef Expr = {
var e : ExprDef;
var pmin : Int;
var pmax : Int;
}
enum ExprDef {
#else
enum Expr {
#end
EConst( c : Const );
EIdent( v : String );
EVar( n : String, ?t : CType, ?e : Expr );
@@ -63,7 +72,21 @@ enum CType {
CTParent( t : CType );
}
#if hscriptPos
class Error {
public var e : ErrorDef;
public var pmin : Int;
public var pmax : Int;
public function new(e, pmin, pmax) {
this.e = e;
this.pmin = pmin;
this.pmax = pmax;
}
}
enum ErrorDef {
#else
enum Error {
#end
EInvalidChar( c : Int );
EUnexpected( s : String );
EUnterminatedString;
+204
View File
@@ -0,0 +1,204 @@
/*
* Copyright (c) 2011, Nicolas Cannasse
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package hscript;
import hscript.Expr.Error;
#if hscriptPos
import hscript.Expr.ErrorDef;
#end
import haxe.macro.Expr;
class Macro {
var p : Position;
var binops : Hash<Binop>;
var unops : Hash<Unop>;
public function new(pos) {
p = pos;
binops = new Hash();
for( c in Type.getEnumConstructs(Binop) ) {
if( c == "OpAssignOp" ) continue;
var op = Type.createEnum(Binop, c);
var assign = false;
var str = switch( op ) {
case OpAdd: assign = true; "+";
case OpMult: assign = true; "*";
case OpDiv: assign = true; "/";
case OpSub: assign = true; "-";
case OpAssign: "=";
case OpEq: "==";
case OpNotEq: "!=";
case OpGt: ">";
case OpGte: ">=";
case OpLt: "<";
case OpLte: "<=";
case OpAnd: assign = true; "&";
case OpOr: assign = true; "|";
case OpXor: assign = true; "^";
case OpBoolAnd: "&&";
case OpBoolOr: "||";
case OpShl: assign = true; "<<";
case OpShr: assign = true; ">>";
case OpUShr: assign = true; ">>>";
case OpMod: assign = true; "%";
case OpAssignOp(_): "";
case OpInterval: "...";
};
binops.set(str, op);
if( assign )
binops.set(str + "=", OpAssignOp(op));
}
unops = new Hash();
for( c in Type.getEnumConstructs(Unop) ) {
var op = Type.createEnum(Unop, c);
var str = switch( op ) {
case OpNot: "!";
case OpNeg: "-";
case OpNegBits: "~";
case OpIncrement: "++";
case OpDecrement: "--";
}
unops.set(str, op);
}
}
function isType(v:String) {
var c0 = v.charCodeAt(0);
return c0 >= 'A'.code && c0 <= 'Z'.code;
}
function map < T, R > ( a : Array<T>, f : T -> R ) : Array<R> {
var b = new Array();
for( x in a )
b.push(f(x));
return b;
}
function convertType( t : Expr.CType ) : ComplexType {
return switch( t ) {
case CTPath(pack, args):
var params = [];
if( args != null )
for( t in args )
params.push(TPType(convertType(t)));
TPath({
pack : pack,
name : pack.pop(),
params : params,
sub : null,
});
case CTParent(t): TParent(convertType(t));
case CTFun(args, ret):
TFunction(map(args,convertType), convertType(ret));
case CTAnon(fields):
var tf = [];
for( f in fields )
tf.push( { name : f.name, isPublic : null, type : FVar(convertType(f.t)), pos : p } );
TAnonymous(tf);
};
}
public function convert( e : hscript.Expr ) : Expr {
return { expr : switch( #if hscriptPos e.e #else e #end ) {
case EConst(c):
EConst(switch(c) {
case CInt(v): CInt(Std.string(v));
case CFloat(f): CFloat(Std.string(f));
case CString(s): CString(s);
case CInt32(v): CInt(Std.string(v));
});
case EIdent(v):
if( isType(v) )
EConst(CType(v));
else
EConst(CIdent(v));
case EVar(n, t, e):
EVars([ { name : n, expr : if( e == null ) null else convert(e), type : if( t == null ) null else convertType(t) } ]);
case EParent(e):
EParenthesis(convert(e));
case EBlock(el):
EBlock(map(el,convert));
case EField(e, f):
if( isType(f) )
EType(convert(e), f);
else
EField(convert(e), f);
case EBinop(op, e1, e2):
var b = binops.get(op);
if( b == null ) throw EInvalidOp(op);
EBinop(b, convert(e1), convert(e2));
case EUnop(op, prefix, e):
var u = unops.get(op);
if( u == null ) throw EInvalidOp(op);
EUnop(u, !prefix, convert(e));
case ECall(e, params):
ECall(convert(e), map(params, convert));
case EIf(c, e1, e2):
EIf(convert(c), convert(e1), e2 == null ? null : convert(e2));
case EWhile(c, e):
EWhile(convert(c), convert(e), true);
case EFor(v, it, e):
EFor(v, convert(it), convert(e));
case EBreak:
EBreak;
case EContinue:
EContinue;
case EFunction(args, e, name, ret):
var targs = [];
for( a in args )
targs.push( {
name : a.name,
type : a.t == null ? null : convertType(a.t),
opt : false,
value : null,
});
EFunction( {
name : name,
args : targs,
expr : convert(e),
ret : ret == null ? null : convertType(ret),
});
case EReturn(e):
EReturn(e == null ? null : convert(e));
case EArray(e, index):
EArray(convert(e), convert(index));
case EArrayDecl(el):
EArrayDecl(map(el,convert));
case ENew(cl, params):
var pack = cl.split(".");
ENew( { pack : pack, name : pack.pop(), params : [], sub : null }, map(params, convert));
case EThrow(e):
EThrow(convert(e));
case ETry(e, v, t, ec):
ETry(convert(e), [ { type : convertType(t), name : v, expr : convert(ec) } ]);
case EObject(fields):
var tf = [];
for( f in fields )
tf.push( { field : f.name, expr : convert(f.e) } );
EObjectDecl(tf);
}, pos : #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end }
}
}
+181 -57
View File
@@ -65,10 +65,25 @@ class Parser {
// implementation
var input : haxe.io.Input;
var char : Null<Int>;
var char : Int;
var ops : Array<Bool>;
var idents : Array<Bool>;
// only used for hscriptPos, but needs to be defined
var readPos : Int;
var tokenMin : Int;
var tokenMax : Int;
#if hscriptPos
var oldTokenMin : Int;
var oldTokenMax : Int;
var tokens : List<{ min : Int, max : Int, t : Token }>;
#else
var tokens : haxe.FastList<Token>;
#end
public function new() {
line = 1;
@@ -86,7 +101,19 @@ class Parser {
"%"
];
unopsPrefix = ["!","++","--","-","~"];
unopsSuffix = ["++","--"];
unopsSuffix = ["++", "--"];
}
public inline function error( err, pmin, pmax ) {
#if hscriptPos
throw new Error(err, pmin, pmax);
#else
throw err;
#end
}
public function invalidChar(c) {
error(EInvalidChar(c), readPos, readPos);
}
public function parseString( s : String ) {
@@ -95,11 +122,18 @@ class Parser {
}
public function parse( s : haxe.io.Input ) {
char = null;
#if hscriptPos
readPos = 0;
tokenMin = oldTokenMin = 0;
tokenMax = oldTokenMax = 0;
tokens = new List();
#else
tokens = new haxe.FastList<Token>();
#end
char = -1;
input = s;
ops = new Array();
idents = new Array();
tokens = new haxe.FastList<Token>();
for( i in 0...opChars.length )
ops[opChars.charCodeAt(i)] = true;
for( i in 0...identChars.length )
@@ -111,25 +145,65 @@ class Parser {
push(tk);
a.push(parseFullExpr());
}
return if( a.length == 1 ) a[0] else EBlock(a);
return if( a.length == 1 ) a[0] else mk(EBlock(a),0);
}
function unexpected( tk ) : Dynamic {
throw Error.EUnexpected(tokenString(tk));
error(EUnexpected(tokenString(tk)),tokenMin,tokenMax);
return null;
}
inline function push(tk) {
#if hscriptPos
tokens.push( { t : tk, min : tokenMin, max : tokenMax } );
tokenMin = oldTokenMin;
tokenMax = oldTokenMax;
#else
tokens.add(tk);
#end
}
inline function ensure(tk) {
var t = token();
if( t != tk ) unexpected(t);
}
inline function expr(e:Expr) {
#if hscriptPos
return e.e;
#else
return e;
#end
}
inline function pmin(e:Expr) {
#if hscriptPos
return e.pmin;
#else
return 0;
#end
}
inline function pmax(e:Expr) {
#if hscriptPos
return e.pmax;
#else
return 0;
#end
}
inline function mk(e,?pmin,?pmax) : Expr {
#if hscriptPos
if( pmin == null ) pmin = tokenMin;
if( pmax == null ) pmax = tokenMax;
return { e : e, pmin : pmin, pmax : pmax };
#else
return e;
#end
}
function isBlock(e) {
return switch( e ) {
return switch( expr(e) ) {
case EBlock(_), EObject(_): true;
case EFunction(_,e,_,_): isBlock(e);
case EVar(_,_,e): e != null && isBlock(e);
@@ -155,7 +229,7 @@ class Parser {
return e;
}
function parseObject() {
function parseObject(p1) {
// parse object
var fl = new Array();
while( true ) {
@@ -186,35 +260,36 @@ class Parser {
unexpected(tk);
}
}
return parseExprNext(EObject(fl));
return parseExprNext(mk(EObject(fl),p1));
}
function parseExpr() {
var tk = token();
var p1 = tokenMin;
switch( tk ) {
case TId(id):
var e = parseStructure(id);
if( e == null )
e = EIdent(id);
e = mk(EIdent(id));
return parseExprNext(e);
case TConst(c):
return parseExprNext(EConst(c));
return parseExprNext(mk(EConst(c)));
case TPOpen:
var e = parseExpr();
ensure(TPClose);
return parseExprNext(EParent(e));
return parseExprNext(mk(EParent(e),p1,pmax(e)));
case TBrOpen:
tk = token();
switch( tk ) {
case TBrClose:
return parseExprNext(EObject([]));
return parseExprNext(mk(EObject([]),p1));
case TId(id):
var tk2 = token();
push(tk2);
push(tk);
switch( tk2 ) {
case TDoubleDot:
return parseExprNext(parseObject());
return parseExprNext(parseObject(p1));
default:
}
case TConst(c):
@@ -226,7 +301,7 @@ class Parser {
push(tk);
switch( tk2 ) {
case TDoubleDot:
return parseExprNext(parseObject());
return parseExprNext(parseObject(p1));
default:
}
default:
@@ -245,7 +320,7 @@ class Parser {
break;
push(tk);
}
return EBlock(a);
return mk(EBlock(a),p1);
case TOp(op):
var found;
for( x in unopsPrefix )
@@ -262,7 +337,7 @@ class Parser {
if( tk == TComma )
tk = token();
}
return parseExprNext(EArrayDecl(a));
return parseExprNext(mk(EArrayDecl(a),p1));
default:
return unexpected(tk);
}
@@ -276,24 +351,26 @@ class Parser {
}
function makeUnop( op, e ) {
return switch( e ) {
case EBinop(bop,e1,e2): EBinop(bop,makeUnop(op,e1),e2);
default: EUnop(op,true,e);
return switch( expr(e) ) {
case EBinop(bop, e1, e2): mk(EBinop(bop, makeUnop(op, e1), e2),pmin(e1),pmax(e2));
default: mk(EUnop(op,true,e),pmin(e),pmax(e));
}
}
function makeBinop( op, e1, e ) {
return switch( e ) {
return switch( expr(e) ) {
case EBinop(op2,e2,e3):
if( priority(op) > priority(op2) )
EBinop(op2,makeBinop(op,e1,e2),e3);
mk(EBinop(op2,makeBinop(op,e1,e2),e3),pmin(e1),pmax(e3));
else
EBinop(op,e1,e);
default: EBinop(op,e1,e);
mk(EBinop(op, e1, e),pmin(e1),pmax(e));
default:
mk(EBinop(op,e1,e),pmin(e1),pmax(e));
}
}
function parseStructure(id) {
var p1 = tokenMin;
return switch( id ) {
case "if":
var cond = parseExpr();
@@ -311,7 +388,7 @@ class Parser {
push(tk);
if( semic ) push(TSemicolon);
}
EIf(cond,e1,e2);
mk(EIf(cond,e1,e2),p1,(e2 == null) ? tokenMax : pmax(e2));
case "var":
var tk = token();
var ident = null;
@@ -330,11 +407,11 @@ class Parser {
e = parseExpr();
else
push(tk);
EVar(ident,t,e);
mk(EVar(ident,t,e),p1,(e == null) ? tokenMax : pmax(e));
case "while":
var econd = parseExpr();
var e = parseExpr();
EWhile(econd,e);
mk(EWhile(econd,e),p1,pmax(e));
case "for":
ensure(TPOpen);
var tk = token();
@@ -347,9 +424,10 @@ class Parser {
if( !Type.enumEq(tk,TId("in")) ) unexpected(tk);
var eiter = parseExpr();
ensure(TPClose);
EFor(vname,eiter,parseExpr());
case "break": EBreak;
case "continue": EContinue;
var e = parseExpr();
mk(EFor(vname,eiter,e),p1,pmax(e));
case "break": mk(EBreak);
case "continue": mk(EContinue);
case "else": unexpected(TId(id));
case "function":
var tk = token();
@@ -391,11 +469,13 @@ class Parser {
else
ret = parseType();
}
EFunction(args, parseExpr(), name, ret);
var body = parseExpr();
mk(EFunction(args, body, name, ret),p1,pmax(body));
case "return":
var tk = token();
push(tk);
EReturn(if( tk == TSemicolon ) null else parseExpr());
var e = if( tk == TSemicolon ) null else parseExpr();
mk(EReturn(e),p1,if( e == null ) tokenMax else pmax(e));
case "new":
var a = new Array();
var tk = token();
@@ -418,9 +498,11 @@ class Parser {
unexpected(tk);
}
}
ENew(a.join("."),parseExprList(TPClose));
var args = parseExprList(TPClose);
mk(ENew(a.join("."),args),p1);
case "throw":
EThrow( parseExpr() );
var e = parseExpr();
mk(EThrow(e),p1,pmax(e));
case "try":
var e = parseExpr();
var tk = token();
@@ -440,7 +522,8 @@ class Parser {
if( !Type.enumEq(tk, TId("Dynamic")) ) unexpected(tk);
}
ensure(TPClose);
ETry(e,vname,t,parseExpr());
var ec = parseExpr();
mk(ETry(e,vname,t,ec),p1,pmax(ec));
default:
null;
}
@@ -452,11 +535,11 @@ class Parser {
case TOp(op):
for( x in unopsSuffix )
if( x == op ) {
if( isBlock(e1) || switch(e1) { case EParent(_): true; default: false; } ) {
if( isBlock(e1) || switch(expr(e1)) { case EParent(_): true; default: false; } ) {
push(tk);
return e1;
}
return parseExprNext(EUnop(op,false,e1));
return parseExprNext(mk(EUnop(op,false,e1),pmin(e1)));
}
return makeBinop(op,e1,parseExpr());
case TDot:
@@ -466,18 +549,18 @@ class Parser {
case TId(id): field = id;
default: unexpected(tk);
}
return parseExprNext(EField(e1,field));
return parseExprNext(mk(EField(e1,field),pmin(e1)));
case TPOpen:
return parseExprNext(ECall(e1,parseExprList(TPClose)));
return parseExprNext(mk(ECall(e1,parseExprList(TPClose)),pmin(e1)));
case TBkOpen:
var e2 = parseExpr();
ensure(TBkClose);
return parseExprNext(EArray(e1,e2));
return parseExprNext(mk(EArray(e1,e2),pmin(e1)));
case TQuestion:
var e2 = parseExpr();
ensure(TDoubleDot);
var e3 = parseExpr();
return EIf(e1,e2,e3);
return mk(EIf(e1,e2,e3),pmin(e1),pmax(e3));
default:
push(tk);
return e1;
@@ -577,7 +660,7 @@ class Parser {
var tk = token();
if( tk == etk )
return args;
tokens.add(tk);
push(tk);
while( true ) {
args.push(parseExpr());
tk = token();
@@ -590,8 +673,15 @@ class Parser {
}
return args;
}
inline function incPos() {
#if hscriptPos
readPos++;
#end
}
function readChar() {
incPos();
return try input.readByte() catch( e : Dynamic ) 0;
}
@@ -601,12 +691,14 @@ class Parser {
var esc = false;
var old = line;
var s = input;
var p1 = readPos - 1;
while( true ) {
try {
incPos();
c = s.readByte();
} catch( e : Dynamic ) {
line = old;
throw Error.EUnterminatedString;
error(EUnterminatedString, p1, p1);
}
if( esc ) {
esc = false;
@@ -615,15 +707,19 @@ class Parser {
case 'r'.code: b.writeByte(13);
case 't'.code: b.writeByte(9);
case "'".code, '"'.code, '\\'.code: b.writeByte(c);
case '/'.code: if( allowJSON ) b.writeByte(c) else throw Error.EInvalidChar(c);
case '/'.code: if( allowJSON ) b.writeByte(c) else invalidChar(c);
case "u".code:
if( !allowJSON ) throw Error.EInvalidChar(c);
if( !allowJSON ) throw invalidChar(c);
var code;
try {
incPos();
incPos();
incPos();
incPos();
code = s.readString(4);
} catch( e : Dynamic ) {
line = old;
throw Error.EUnterminatedString;
error(EUnterminatedString, p1, p1);
}
var k = 0;
for( i in 0...4 ) {
@@ -637,7 +733,7 @@ class Parser {
case 97,98,99,100,101,102: // a-f
k += char - 87;
default:
throw Error.EInvalidChar(char);
invalidChar(char);
}
}
// encode k in UTF8
@@ -651,7 +747,7 @@ class Parser {
b.writeByte( 0x80 | ((k >> 6) & 63) );
b.writeByte( 0x80 | (k & 63) );
}
default: throw Error.EInvalidChar(c);
default: invalidChar(c);
}
} else if( c == 92 )
esc = true;
@@ -666,20 +762,44 @@ class Parser {
}
function token() {
#if hscriptPos
var t = tokens.pop();
if( t != null ) {
tokenMin = t.min;
tokenMax = t.max;
return t.t;
}
oldTokenMin = tokenMin;
oldTokenMax = tokenMax;
tokenMin = (this.char < 0) ? readPos : readPos - 1;
var t = _token();
tokenMax = (this.char < 0) ? readPos - 1 : readPos - 2;
trace(t + ":" + tokenMin + ":" + tokenMax);
return t;
}
function _token() {
#else
if( !tokens.isEmpty() )
return tokens.pop();
#end
var char;
if( this.char == null )
if( this.char < 0 )
char = readChar();
else {
char = this.char;
this.char = null;
this.char = -1;
}
while( true ) {
switch( char ) {
case 0: return TEof;
case 32,9,13: // space, tab, CR
#if hscriptPos
tokenMin++;
#end
case 10: line++; // LF
#if hscriptPos
tokenMin++;
#end
case 48,49,50,51,52,53,54,55,56,57: // 0...9
var n = (char - 48) * 1.0;
var exp = 0;
@@ -693,16 +813,16 @@ class Parser {
if( exp > 0 ) {
// in case of '...'
if( exp == 10 && readChar() == 46 ) {
tokens.add(TOp("..."));
push(TOp("..."));
var i = Std.int(n);
return TConst( (i == n) ? CInt(i) : CFloat(n) );
}
throw Error.EInvalidChar(char);
invalidChar(char);
}
exp = 1;
case 120: // x
if( n > 0 || exp > 0 )
throw Error.EInvalidChar(char);
invalidChar(char);
// read hexa
var n = haxe.Int32.ofInt(0);
while( true ) {
@@ -753,7 +873,7 @@ class Parser {
case 46:
char = readChar();
if( char != 46 )
throw Error.EInvalidChar(char);
invalidChar(char);
return TOp("...");
default:
this.char = char;
@@ -792,7 +912,7 @@ class Parser {
id += String.fromCharCode(char);
}
}
throw Error.EInvalidChar(char);
invalidChar(char);
}
char = readChar();
}
@@ -804,8 +924,10 @@ class Parser {
var s = input;
if( c == 47 ) { // comment
try {
while( char != 10 && char != 13 )
while( char != 10 && char != 13 ) {
incPos();
char = s.readByte();
}
this.char = char;
} catch( e : Dynamic ) {
}
@@ -817,15 +939,17 @@ class Parser {
while( true ) {
while( char != 42 ) {
if( char == 10 ) line++;
incPos();
char = s.readByte();
}
incPos();
char = s.readByte();
if( char == 47 )
break;
}
} catch( e : Dynamic ) {
line = old;
throw Error.EUnterminatedComment;
error(EUnterminatedComment, tokenMin, tokenMin);
}
return token();
}