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
321 changes: 229 additions & 92 deletions hscript/Parser.hx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
* DEALINGS IN THE SOFTWARE.
*/
package hscript;

import hscript.thx_semver.Version;
import hscript.Expr;

using StringTools;
Expand Down Expand Up @@ -68,6 +70,11 @@ class Parser {
**/
public var preprocessorValues : Map<String,Dynamic> = new Map();

/**
defines preprocessor variables that will be considered false
**/
public var invalidPreprocessValues : Array<Dynamic> = ["null", null, "0", 0, "false", false];

/**
activate JSON compatiblity
**/
Expand Down Expand Up @@ -207,6 +214,10 @@ class Parser {
push(tk);
parseFullExpr(a);
}
if(preprocStack.length > 0)
{
error(EInvalidPreprocessor("Unclosed"), tokenMin, tokenMax);
}
return if( a.length == 1 ) a[0] else mk(EBlock(a),0);
}

Expand Down Expand Up @@ -1771,6 +1782,10 @@ class Parser {
push(tk);
decls.push(parseModuleDecl());
}
if(preprocStack.length > 0)
{
error(EInvalidPreprocessor("Unclosed"), tokenMin, tokenMax);
}
return decls;
}

Expand Down Expand Up @@ -2379,91 +2394,213 @@ class Parser {

function parsePreproCond():Expr {
var tk = token();
return switch( tk ) {
case TPOpen:
push(TPOpen);
parseExpr();
case TId(id):
while(true) {
var tk = token();
if(tk == TDot) {
id += ".";
switch( tk ) {
case TPOpen:
push(TPOpen);
return parseExpr();
case TId(id):
var tk;
while(true) {
tk = token();
switch(tk) {
case TId(id2):
id += id2;
default: unexpected(tk);
if(tk == TDot) {
id += ".";
tk = token();
switch(tk) {
case TId(id2):
id += id2;
default: unexpected(tk);
}
} else {
push(tk);
break;
}
} else {
push(tk);
break;
}
}
mk(EIdent(id), tokenMin, tokenMax);
case TOp("!"):
mk(EUnop("!", true, parsePreproCond()), tokenMin, tokenMax);
default:
unexpected(tk);
return mk(EIdent(id), tokenMin, tokenMax);
case TConst(c):
return mk(EConst(c), tokenMin, tokenMax);
case TOp("!"):
return mk(EUnop("!", true, parsePreproCond()), tokenMin, tokenMax);
default:
return unexpected(tk);
}
}

function evalPreproCond( e : Expr ): Bool {
switch( expr(e) ) {
case EIdent(id):
return preprocValue(id) != null;
case EField(e2, f):
switch(expr(e2)) {
case EIdent(id):
return preprocValue(id + "." + f) != null;
default:
error(EInvalidPreprocessor("Can't eval " + expr(e).getName() + " with " + expr(e2).getName()), readPos, readPos);
return false;
}
case EUnop("!", _, e):
return !evalPreproCond(e);
case EParent(e):
return evalPreproCond(e);
case EBinop("&&", e1, e2):
return evalPreproCond(e1) && evalPreproCond(e2);
case EBinop("||", e1, e2):
return evalPreproCond(e1) || evalPreproCond(e2);
default:
error(EInvalidPreprocessor("Can't eval " + expr(e).getName()), readPos, readPos);
return false;
function getValFromPreproExpr( e : Expr ) : Dynamic
{
var edef:ExprDef = expr(e);
switch( edef )
{
case EParent(e):
return getValFromPreproExpr(e);
case EIdent(id):
return preprocValue(id);
case EConst(c):
return switch (c) {
case CInt(v): v;
case CFloat(f): f;
case CString(s): s;
}
case ECall(expr(_) => EIdent("version"), expr(_[0]) => EConst(CString(s))):
try
{
(s : Version);
}
catch(_)
{
error(EInvalidPreprocessor('Invalid version string $s. Should follow SemVer.'), readPos, readPos);
}
return s;

default:
error(EInvalidPreprocessor(edef.getName()), readPos, readPos);
return null;
}
}

function evalPreproCond( e : Expr ):Bool {
var edef:ExprDef = expr(e);
switch( edef ) {
case EConst(c):
return !invalidPreprocessValues.contains(switch (c) {
case CInt(v): v;
case CFloat(f): f;
case CString(s): s;
});
case EIdent(id):
return !invalidPreprocessValues.contains(preprocValue(id));
case EField(e1, f1):
switch(expr(e1)) {
case EIdent(id):
return !invalidPreprocessValues.contains(preprocValue('$id.$f1'));
case edef2:
error(EInvalidPreprocessor("Can't eval " + edef.getName() + " with " + edef2.getName()), readPos, readPos);
return false;
}
case EUnop("!", _, e):
return !evalPreproCond(e);
case EParent(e):
return evalPreproCond(e);
case EBinop(op, e1, e2):
switch (op)
{
case "&&":
return evalPreproCond(e1) && evalPreproCond(e2);
case "||":
return evalPreproCond(e1) || evalPreproCond(e2);
case op:
var e1:Dynamic = getValFromPreproExpr(e1);
var e2:Dynamic = getValFromPreproExpr(e2);
try {
var vers1:Version = Std.string(e1);
var vers2:Version = Std.string(e2);
switch (op) {
case "==":
return vers1 == vers2;
case "!=":
return vers1 != vers2;
case ">=":
return vers1 >= vers2;
case ">":
return vers1 > vers2;
case "<=":
return vers1 <= vers2;
case "<":
return vers1 < vers2;
default:
throw null;
}
} catch(e:String) { // doesn't capture SemVer error
} catch(e) {
error(EInvalidPreprocessor('Invalid operator \'$op\' for SemVer'), readPos, readPos);
return false;
}
try {
switch (op) {
case "==":
return e1 == e2;
case "!=":
return e1 != e2;
case ">=":
return e1 >= e2;
case ">":
return e1 > e2;
case "<=":
return e1 <= e2;
case "<":
return e1 < e2;
default:
}
} catch(_) {
error(EInvalidPreprocessor('Invalid operator \'$op\''), readPos, readPos);
return false;
}
error(EInvalidPreprocessor('Uncorrected operator \'$op\''), readPos, readPos);
return false;
}
default:
error(EInvalidPreprocessor("Can't eval " + edef.getName()), readPos, readPos);
return false;
}
}

var _inIfPrepocess:Bool = false;
function preprocess( id : String ) : Token {
switch( id ) {
case "if":
var e = parsePreproCond();
if( evalPreproCond(e) ) {
preprocStack.push({r: true});
return token();
inline function returnToken() {
return switch (token()) {
case TPrepro(id = "if" | "else" | "elseif" | "end" | "error"):
preprocess(id);
case t: t;
}
preprocStack.push({r: false});
skipTokens();
return token();
case "else", "elseif" if( preprocStack.length > 0 ):
if( preprocStack[preprocStack.length - 1].r ) {
preprocStack[preprocStack.length - 1].r = false;
skipTokens();
return token();
} else if( id == "else" ) {
preprocStack.pop();
preprocStack.push({r: true});
return token();
} else {
// elseif
preprocStack.pop();
return preprocess("if");
}
if ( !_inIfPrepocess ) {
switch( id ) {
case "if":
_inIfPrepocess = true;
var result = evalPreproCond(parsePreproCond());
_inIfPrepocess = false;
preprocStack.push({ r : result });
if(!result) {
skipTokens();
}
return returnToken();
case "else", "elseif":
if ( preprocStack.length > 0 ) {
var last = preprocStack[preprocStack.length - 1];
if( last.r ) {
skipTokens();
last.r = false;
return returnToken();
} else if( id == "else" ) {
preprocStack.pop();
preprocStack.push({ r : true });
return returnToken();
} else {
// elseif
preprocStack.pop();
return preprocess("if");
}
}
else
{
return unexpected(TPrepro(id));
}
case "end":
if( preprocStack.length > 0 )
{
preprocStack.pop();
return returnToken();
}
else
{
return unexpected(TPrepro(id));
}
case "error" if ( preprocStack.length == 0 || preprocStack[preprocStack.length - 1].r):
var tokenMin = tokenMin;
error(ECustom(Std.string(getValFromPreproExpr(parsePreproCond()))), tokenMin,tokenMax);
return returnToken();
}
case "end" if( preprocStack.length > 0 ):
preprocStack.pop();
return token();
default:
return TPrepro(id);
}
return TPrepro(id);
}

function skipTokens():Void {
Expand Down Expand Up @@ -2539,25 +2676,25 @@ class Parser {

function tokenString( t:Token ):String {
return switch( t ) {
case TEof: "<eof>";
case TConst(c): constString(c);
case TId(s): s;
case TOp(s): s;
case TPOpen: "(";
case TPClose: ")";
case TBrOpen: "{";
case TBrClose: "}";
case TDot: ".";
case TQuestionDot: "?.";
case TComma: ",";
case TSemicolon: ";";
case TBkOpen: "[";
case TBkClose: "]";
case TQuestion: "?";
case TDoubleDot: ":";
case TMeta(id): "@" + id;
case TPrepro(id): "#" + id;
case TRegex(e, f): '~/$e/$f';
case TEof: "<eof>";
case TConst(c): constString(c);
case TId(s): s;
case TOp(s): s;
case TPOpen: "(";
case TPClose: ")";
case TBrOpen: "{";
case TBrClose: "}";
case TDot: ".";
case TQuestionDot: "?.";
case TComma: ",";
case TSemicolon: ";";
case TBkOpen: "[";
case TBkClose: "]";
case TQuestion: "?";
case TDoubleDot: ":";
case TMeta(id): "@" + id;
case TPrepro(id): "#" + id;
case TRegex(e, f): '~/$e/$f';
}
}

Expand All @@ -2569,4 +2706,4 @@ class Parser {
@:structInit
final class PreprocValue {
public var r : Bool;
}
}
Loading