diff --git a/hscript/Expr.hx b/hscript/Expr.hx index 1571b613..2052e45b 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -43,6 +43,7 @@ enum Expr { EConst( c : Const ); EIdent( v : String ); EVar( n : String, ?t : CType, ?e : Expr ); + EFinal( n : String, ?t : CType, ?e : Expr ); EParent( e : Expr ); EBlock( e : Array ); EField( e : Expr, f : String ); @@ -174,4 +175,5 @@ typedef VarDecl = { var set : Null; var expr : Null; var type : Null; + var isfinal : Null; } diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 1e7c73e4..82d8cdea 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -33,12 +33,12 @@ private enum Stop { class Interp { public var variables : Map; - var locals : Map; + var locals : Map; var binops : Map Expr -> Dynamic >; var depth : Int; var inTry : Bool; - var declared : Array<{ n : String, old : { r : Dynamic } }>; + var declared : Array<{ n : String, old : { r : Dynamic, ?isfinal : Bool } }>; var returnValue : Dynamic; #if hscriptPos @@ -122,6 +122,7 @@ class Interp { switch( Tools.expr(e1) ) { case EIdent(id): var l = locals.get(id); + if( l != null && l.isfinal && l.r != null) return error(EInvalidAccess(id)); if( l == null ) setVar(id,v) else @@ -155,6 +156,7 @@ class Interp { case EIdent(id): var l = locals.get(id); v = fop(expr(e1),expr(e2)); + if( l != null && l.isfinal && l.r != null) return error(EInvalidAccess(id)); if( l == null ) setVar(id,v) else @@ -189,6 +191,7 @@ class Interp { case EIdent(id): var l = locals.get(id); var v : Dynamic = (l == null) ? resolve(id) : l.r; + if( l != null && l.isfinal && l.r != null) return error(EInvalidAccess(id)); if( prefix ) { v += delta; if( l == null ) setVar(id,v) else l.r = v; @@ -311,6 +314,10 @@ class Interp { declared.push({ n : n, old : locals.get(n) }); locals.set(n,{ r : (e == null)?null:expr(e) }); return null; + case EFinal(n,_,e): + declared.push({ n : n, old : locals.get(n) }); + locals.set(n,{ r : (e == null)?null:expr(e), isfinal : true }); + return null; case EParent(e): return expr(e); case EBlock(exprs): diff --git a/hscript/Parser.hx b/hscript/Parser.hx index d3c85604..881c141b 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -267,7 +267,7 @@ class Parser { return switch( expr(e) ) { case EBlock(_), EObject(_), ESwitch(_): true; case EFunction(_,e,_,_): isBlock(e); - case EVar(_, t, e): e != null ? isBlock(e) : t != null ? t.match(CTAnon(_)) : false; + case EVar(_, t, e) | EFinal(_, t, e): e != null ? isBlock(e) : t != null ? t.match(CTAnon(_)) : false; case EIf(_,e1,e2): if( e2 != null ) isBlock(e2) else isBlock(e1); case EBinop(_,_,e): isBlock(e); case EUnop(_,prefix,e): !prefix && isBlock(e); @@ -614,6 +614,27 @@ class Parser { } mk(EVar(ident,t,e),p1,(e == null) ? tokenMax : pmax(e)); + case "final": + var ident = getIdent(); + var tk = token(); + var t = null; + if( tk == TDoubleDot && allowTypes ) { + t = parseType(); + tk = token(); + } + var e = null; + + switch (tk) + { + case TOp("="): e = parseExpr(); + case TOp(_): unexpected(tk); + case TComma | TSemicolon: push(tk); + // Above case should be enough but semicolon is not mandatory after } + case _ if (t != null): push(tk); + default: unexpected(tk); + } + + mk(EFinal(ident,t,e),p1,(e == null) ? tokenMax : pmax(e)); case "while": var econd = parseExpr(); var e = parseExpr(); @@ -1225,7 +1246,7 @@ class Parser { ret : inf.ret, }), }; - case "var": + case "var" | "final": var name = getIdent(); var get = null, set = null; if( maybe(TPOpen) ) { @@ -1256,6 +1277,7 @@ class Parser { set : set, type : type, expr : expr, + isfinal : (id == "final") }), }; default: diff --git a/hscript/Printer.hx b/hscript/Printer.hx index 3c5b61cc..0a06306b 100644 --- a/hscript/Printer.hx +++ b/hscript/Printer.hx @@ -129,6 +129,13 @@ class Printer { add(" = "); expr(e); } + case EFinal(n, t, e): + add("final " + n); + addType(t); + if( e != null ) { + add(" = "); + expr(e); + } case EParent(e): add("("); expr(e); add(")"); case EBlock(el): diff --git a/hscript/Tools.hx b/hscript/Tools.hx index e0dc2359..d69f2e06 100644 --- a/hscript/Tools.hx +++ b/hscript/Tools.hx @@ -27,7 +27,7 @@ class Tools { public static function iter( e : Expr, f : Expr -> Void ) { switch( expr(e) ) { case EConst(_), EIdent(_): - case EVar(_, _, e): if( e != null ) f(e); + case EVar(_, _, e) | EFinal(_, _, e): if( e != null ) f(e); case EParent(e): f(e); case EBlock(el): for( e in el ) f(e); case EField(e, _): f(e); @@ -64,6 +64,7 @@ class Tools { var edef = switch( expr(e) ) { case EConst(_), EIdent(_), EBreak, EContinue: expr(e); case EVar(n, t, e): EVar(n, t, if( e != null ) f(e) else null); + case EFinal(n, t, e): EFinal(n, t, if( e != null ) f(e) else null); case EParent(e): EParent(f(e)); case EBlock(el): EBlock([for( e in el ) f(e)]); case EField(e, fi): EField(f(e),fi);