diff --git a/.gitignore b/.gitignore index 6fde600..9e3c1c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.vscode +lib docs/javadoc/ lib/ src/c/libwaxeye.a diff --git a/src/haxe/build.hxml b/src/haxe/build.hxml new file mode 100644 index 0000000..cffc68f --- /dev/null +++ b/src/haxe/build.hxml @@ -0,0 +1,4 @@ +-cp src +-main org.waxeye.Main +-neko neko/Main.n +-cmd neko neko/Main diff --git a/src/haxe/org/waxeye/parser/AST.hx b/src/haxe/org/waxeye/parser/AST.hx new file mode 100644 index 0000000..a490e79 --- /dev/null +++ b/src/haxe/org/waxeye/parser/AST.hx @@ -0,0 +1,43 @@ +package org.waxeye.parser; + +/** + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + * + * An abstract syntax tree has one of three forms. + * AST_EMPTY represents a successful parse from a voided non-terminal. + * 'x' just holds a character. + * AST_TREE represents a successful parse from a non-terminal. It holds: + * - the non-terminal's name + * - a list of child asts + * + */ +class AST +{ + + public var form:String; + public var type:Any; + public var children:Array; + + public function new(form:String, ?type:Any, ?children:Any) + { + this.form = form; + this.type = type; + this.children = children; + } + + public static function EMPTY():AST + { + return new AST("EMPTY"); + } + + public static function TREE(str:Any, asts:Any):AST + { + return new AST("TREE", str, asts); + } + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/Continuations.hx b/src/haxe/org/waxeye/parser/Continuations.hx new file mode 100644 index 0000000..98e67b8 --- /dev/null +++ b/src/haxe/org/waxeye/parser/Continuations.hx @@ -0,0 +1,82 @@ +package org.waxeye.parser; +import org.waxeye.parser.Modes.Modes; +import org.waxeye.parser.Util.Assert; +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ +class Continuations +{ + public var type:String; + public var pos:Int; + public var expressions:Array; + public var expression:Exp; + public var asts:Array; + public var err:Any; + public var mode:Modes; + public var name:String; + public var nt:Dynamic; + + public function new(type:String, pos:Int, expressions:Array, expression:Exp, asts:Array, err:Any, mode:Modes, name:String, nt:Dynamic) + { + this.type = type; + this.pos = pos; + this.expressions = expressions; + this.expression = expression; + this.asts = asts; + this.err = err; + this.mode = mode; + this.name = name; + this.nt = nt; + } + + public static function CONT_SEQ(expressions:Array):Continuations + { + return new Continuations("CONT_SEQ", 0, expressions, null, null, null, null, null, null); + } + + public static function CONT_ALT(expressions: Array, pos:Int, asts:Array):Continuations + { + return new Continuations("CONT_ALT", pos, expressions, null, asts, null, null, null, null); + } + + public static function CONT_AND(pos:Int, asts:Array, err:Any):Continuations + { + return new Continuations("CONT_AND", pos, null, null, asts, err, null, null, null); + } + + public static function CONT_NOT( pos:Int, asts:Array, err:Any):Continuations + { + return new Continuations("CONT_NOT", pos, null, null, asts, err, null, null, null); + } + + public static function CONT_OPT(pos:Int, asts:Array):Continuations + { + return new Continuations("CONT_OPT", pos, null, null, asts, null, null, null, null); + } + + public static function CONT_STAR(exp: Dynamic, pos:Int, asts:Array):Continuations + { + return new Continuations("CONT_STAR", pos, null, exp, asts, null, null, null, null); + } + + public static function CONT_PLUS(exp: Dynamic):Continuations + { + return new Continuations("CONT_PLUS", 0, null, exp, null, null, null, null, null); + } + + public static function CONT_VOID(asts:Array):Continuations + { + return new Continuations("CONT_VOID", 0, null, null, asts, null, null, null, null); + } + + public static function CONT_NT(mode:Modes, name:String, asts:Array, nt:Dynamic):Continuations + { + return new Continuations("CONT_NT", 0, null, null, asts, null, mode, name, nt); + } + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/ErrAny.hx b/src/haxe/org/waxeye/parser/ErrAny.hx new file mode 100644 index 0000000..2e4f9dd --- /dev/null +++ b/src/haxe/org/waxeye/parser/ErrAny.hx @@ -0,0 +1,18 @@ +package org.waxeye.parser; +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ +class ErrAny +{ + + public function new() + { + + } + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/ErrCC.hx b/src/haxe/org/waxeye/parser/ErrCC.hx new file mode 100644 index 0000000..b650dde --- /dev/null +++ b/src/haxe/org/waxeye/parser/ErrCC.hx @@ -0,0 +1,20 @@ +package org.waxeye.parser; + +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ +class ErrCC +{ + public var charClasses:Dynamic; + + public function new(charClasses:Dynamic) + { + this.charClasses = charClasses; + } + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/ErrChar.hx b/src/haxe/org/waxeye/parser/ErrChar.hx new file mode 100644 index 0000000..518bf25 --- /dev/null +++ b/src/haxe/org/waxeye/parser/ErrChar.hx @@ -0,0 +1,22 @@ +package org.waxeye.parser; + +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ +class ErrChar +{ + public var char:Dynamic; + + public function new(char:Dynamic) + { + this.char = char; + } + + + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/Exp.hx b/src/haxe/org/waxeye/parser/Exp.hx new file mode 100644 index 0000000..c615f19 --- /dev/null +++ b/src/haxe/org/waxeye/parser/Exp.hx @@ -0,0 +1,100 @@ +package org.waxeye.parser; + +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ + +@:enum +abstract ExpType(String){ + var ANY = "ANY"; + var NT = "NT"; + var VOID = "VOID"; + var CHAR = "CHAR"; + var CHAR_CLASS = "CHAR_CLASS"; + var AND = "AND"; + var NOT = "NOT"; + var OPT = "OPT"; + var ALT = "ALT"; + var SEQ = "SEQ"; + var STAR = "STAR"; + var PLUS = "PLUS"; +} + +class Exp +{ + public var type:ExpType; + public var args:Array; + + public function new(type:ExpType, args:Array) + { + this.args = args; + this.type = type; + } + + + public static function ANY(args:Array):Exp + { + return new Exp(ExpType.ANY, args); + } + + public static function NT(args:Array):Exp + { + return new Exp(ExpType.NT, args); + } + + public static function VOID(args:Array):Exp + { + return new Exp(ExpType.VOID, args); + } + + public static function CHAR(args:Array):Exp + { + return new Exp(ExpType.CHAR, args); + } + + public static function CHAR_CLASS(args:Array):Exp + { + return new Exp(ExpType.CHAR_CLASS, args); + } + + public static function AND(args:Array):Exp + { + return new Exp(ExpType.AND, args); + } + + public static function NOT(args:Array):Exp + { + return new Exp(ExpType.NOT, args); + } + + public static function OPT(args:Array):Exp + { + return new Exp(ExpType.OPT, args); + } + + public static function ALT(args:Array):Exp + { + return new Exp(ExpType.ALT, args); + } + + public static function SEQ(args:Array):Exp + { + return new Exp(ExpType.SEQ, args); + } + + public static function STAR(args:Array):Exp + { + return new Exp(ExpType.STAR, args); + } + + public static function PLUS(args:Array):Exp + { + return new Exp(ExpType.PLUS, args); + } + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/MachineConfiguration.hx b/src/haxe/org/waxeye/parser/MachineConfiguration.hx new file mode 100644 index 0000000..28a201b --- /dev/null +++ b/src/haxe/org/waxeye/parser/MachineConfiguration.hx @@ -0,0 +1,44 @@ +package org.waxeye.parser; +import org.waxeye.parser.Util.Assert; + +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ +class MachineConfiguration +{ + public var type:String; + public var exp:Exp; + public var pos:Int; + public var asts:Array; + public var err:Dynamic; + public var continuations:Array; + public var value:Value; + + public function new(type:String, exp:Exp, pos:Int, asts:Array, err:Dynamic, continuations:Array, value:Value) + { + this.type = type; + this.exp = exp; + this.pos = pos; + this.asts = asts; + this.err = err; + this.continuations = continuations; + this.value = value; + } + + + public static function EVAL(exp:Exp, pos:Int, asts:Array, err:Dynamic, continuations:Array):MachineConfiguration + { + return new MachineConfiguration("EVAL", exp, pos, asts, err, continuations, null); + } + + + public static function APPLY(continuations:Array, value:Value):MachineConfiguration + { + return new MachineConfiguration("APPLY", null, 0, null, null, continuations, value); + } +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/MachineState.hx b/src/haxe/org/waxeye/parser/MachineState.hx new file mode 100644 index 0000000..9faf3d6 --- /dev/null +++ b/src/haxe/org/waxeye/parser/MachineState.hx @@ -0,0 +1,36 @@ +package org.waxeye.parser; +import haxe.DynamicAccess; + +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ +class MachineState +{ + public var type:String; + public var result:Dynamic; + public var configuration:MachineConfiguration; + + public function new(type:String = "", result:Dynamic, configuration:MachineConfiguration) + { + + this.type = type; + this.result = result; + this.configuration = configuration; + } + + public static function FINAL(result:Dynamic):MachineState + { + return new MachineState("FINAL", result, null); + } + + public static function INTER(configuration:MachineConfiguration):MachineState + { + return new MachineState("INTER", null, configuration); + } + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/Modes.hx b/src/haxe/org/waxeye/parser/Modes.hx new file mode 100644 index 0000000..ad7dc40 --- /dev/null +++ b/src/haxe/org/waxeye/parser/Modes.hx @@ -0,0 +1,19 @@ +package org.waxeye.parser; + +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ + +@:enum +abstract Modes(String) +{ + var NORMAL = "NORMAL"; + var PRUNING = "PRUNING"; + var VOIDING = "VOIDING"; + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/NonTerminal.hx b/src/haxe/org/waxeye/parser/NonTerminal.hx new file mode 100644 index 0000000..c8f1d0a --- /dev/null +++ b/src/haxe/org/waxeye/parser/NonTerminal.hx @@ -0,0 +1,28 @@ +package org.waxeye.parser; + +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ +class NonTerminal +{ + public var mode:Modes; + public var exp:Exp; + + public function new(mode:Modes, exp:Exp) + { + this.mode = mode; + this.exp = exp; + } + + + public static function nonterminal(mode:Modes, exp:Exp):NonTerminal + { + return new NonTerminal(mode, exp); + } + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/ParseError.hx b/src/haxe/org/waxeye/parser/ParseError.hx new file mode 100644 index 0000000..84b9e2f --- /dev/null +++ b/src/haxe/org/waxeye/parser/ParseError.hx @@ -0,0 +1,62 @@ +package org.waxeye.parser; +import haxe.Json; + +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ +class ParseError +{ + public var pos:Int; + public var line:Int; + public var col:Int; + public var nt:Array; + public var chars:Dynamic; + + public function new(pos:Int, line:Int, col:Int, nt:Array, chars:Dynamic) + { + this.pos = pos; + this.line = line; + this.col = col; + this.nt = nt; + this.chars = chars; + + } + + public function toString():String + { + + if (this.chars.charClasses != null) + { + this.chars = this.chars.charClasses; + } + + if (this.chars != null) + { + this.chars = this.chars.map(function(ch:Dynamic) + { + + var str:String = ''; + if(ch.char != null){ + str = ch.char; + }else if(ch.charClasses != null){ + str = ch.charClasses; + } + + return Json.stringify(str); + }); + } + + return "Parse Error: failed to match '" + this.nt.join(',') + "' at line=" + this.line+", col=" + this.col + ", pos=" + this.pos + " (expected '" + cast this.chars.map( + function(s:String) + { + return s.split("").slice(1, -1).join(""); + } + ).join(',') + "')"; + } + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/Parser.hx b/src/haxe/org/waxeye/parser/Parser.hx new file mode 100644 index 0000000..d2276a0 --- /dev/null +++ b/src/haxe/org/waxeye/parser/Parser.hx @@ -0,0 +1,457 @@ +package org.waxeye.parser; +import haxe.DynamicAccess; +import org.waxeye.parser.Exp.ExpType; +import org.waxeye.parser.Util.Assert; +import haxe.ds.StringMap; + +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ +class Parser +{ + public var env:DynamicAccess; + public var start:String; + + private var inputLen:Int; + private var input:String; + private var nt:String; + + private var state:MachineState; + + public function new(env:DynamicAccess, start:String = '') + { + + if (env == null) + { + throw "Please supply grammar definition."; + } + + this.env = env; + + this.start = start; + } + + public function match(nt:String, input:String):Dynamic + { + this.input = input; + this.nt = nt; + this.inputLen = input.length; + + //move from initial state to halting state + this.move(MachineConfiguration.EVAL(this.env.get(nt).exp, 0, [], new RawError(0, [nt], [], nt), [])); + if (state != null) + { + while (state.type != "FINAL") + { + this.move(state.configuration); + } + } + return state.result; + + } + + private function eof(pos:Int):Bool + { + return pos >= inputLen; + } + + //Move configuration -> state + private function move(conf:MachineConfiguration):Void + { + + var asts:Array = new Array(); + if (conf.asts == null) + { + asts = []; + } + else{ + asts = conf.asts; + } + var pos:Int = conf.pos; + var exp:Exp = conf.exp; + var err:Dynamic = conf.err; + + var k:Array = conf.continuations; + var firstExp:Exp = new Exp(null, []); + var restExp:Array= new Array(); + var es:Array = []; + + var kFirst = Util.first(k); + var kRest = Util.rest(k); + if (kFirst != null) + es = kFirst.expressions; + + if (es != null) + { + firstExp = Util.first(es); + restExp = Util.rest(es); + } + + + switch conf.type { + + case "EVAL": { + + switch exp.type + { + case ExpType.ANY: + { + + if (eof(pos)) + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(k, Value.FAIL(Util.updateError(err, pos, new ErrAny())))); + + } + else{ + this.state = MachineState.INTER(MachineConfiguration.APPLY(k, Value.VAL(pos + 1, Util.arrayPrepend(input.charAt(pos), asts), err))); + + } + + }; + case ExpType.ALT: + { + es = exp.args; + if (es.length > 0) + { + this.state = MachineState.INTER(MachineConfiguration.EVAL(Util.first(es), pos, asts, err, Util.arrayPrepend(Continuations.CONT_ALT(Util.rest(es), pos, asts), k))); + + } + else{ + this.state = MachineState.INTER(MachineConfiguration.APPLY(k, err)); + + } + }; + case ExpType.AND: + { + this.state = MachineState.INTER(MachineConfiguration.EVAL(exp.args[0], pos, [], err, Util.arrayPrepend(Continuations.CONT_AND(pos, asts, err), k))); + + }; + case ExpType.NOT: + { + this.state = MachineState.INTER(MachineConfiguration.EVAL(exp.args[0], pos, [], err, Util.arrayPrepend(Continuations.CONT_NOT(pos, asts, err), k))); + + }; + case ExpType.VOID: + { + this.state = MachineState.INTER(MachineConfiguration.EVAL(exp.args[0], pos, [], err, Util.arrayPrepend(Continuations.CONT_VOID(asts), k))); + + }; + case ExpType.CHAR: + { + var c = exp.args[0]; + var newval; + if (eof(pos) || c != input.charAt(pos)) + { + newval = Value.FAIL(Util.updateError(err, pos, new ErrChar(c))); + } + else{ + newval = Value.VAL(pos + 1, Util.arrayPrepend(input.charAt(pos), asts), err); + } + this.state = MachineState.INTER(MachineConfiguration.APPLY(k, newval)); + + }; + case ExpType.CHAR_CLASS: + { + var cc = exp.args; + + if (eof(pos)) + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(k, Value.FAIL(Util.updateError(err, pos, new ErrCC(cc))))); + + } + else{ + this.state = visit(cc, k, err, pos, cc, asts); + } + }; + case ExpType.SEQ:{ + /** a sequence is made up of a list of expressions + * we traverse the list, making sure each expression succeeds + * the rest of the string return by the expression is used + * as input to the next expression + */ + var exprs:Array = exp.args; + if (exprs == null) + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(k, Value.VAL(pos, asts, err))); + } + else{ + this.state = MachineState.INTER(MachineConfiguration.EVAL(Util.first(exprs), pos, asts, err, Util.arrayPrepend(Continuations.CONT_SEQ(Util.rest(exprs)), k))); + } + + }; + case ExpType.PLUS:{ + this.state = MachineState.INTER(MachineConfiguration.EVAL(exp.args[0], pos, asts, err, Util.arrayPrepend(Continuations.CONT_PLUS(exp.args[0]), k))); + + }; + case ExpType.STAR:{ + this.state = MachineState.INTER(MachineConfiguration.EVAL(exp.args[0], pos, asts, err, Util.arrayPrepend(Continuations.CONT_STAR(exp.args[0], pos, asts), k))); + + }; + case ExpType.OPT:{ + this.state = MachineState.INTER(MachineConfiguration.EVAL(exp.args[0], pos, asts, err, Util.arrayPrepend(Continuations.CONT_OPT(pos, asts), k))); + + }; + case ExpType.NT:{ + var name:String = exp.args[0]; + var mode:Modes = this.env.get(name).mode; + var e:Exp = this.env.get(name).exp; + var err:RawError = new RawError(err.pos, err.nonterminals, err.failedChars, name); + + this.state = MachineState.INTER(MachineConfiguration.EVAL(e, pos, [], err, Util.arrayPrepend(Continuations.CONT_NT(mode, name, asts, conf.err.currentNT), k))); + + }; + default: throw "unsupported 2"; + } + + }; + + case "APPLY":{ + + if ((conf.value != null && conf.value.type == "FAIL") && (kFirst != null && ["CONT_ALT"].indexOf(kFirst.type) == -1)) + { + if (["CONT_SEQ", "CONT_VOID", "CONT_PLUS"].indexOf(kFirst.type) != -1) + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), conf.value)); + + } + else if (["CONT_AND"].indexOf(kFirst.type) != -1) + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.FAIL(cast(kFirst.err, RawError)))); + + } + else if (["CONT_NOT"].indexOf(kFirst.type) != -1) + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.VAL(kFirst.pos, kFirst.asts, cast(conf.value.err, RawError)))); + + } + else if (["CONT_STAR", "CONT_OPT"].indexOf(kFirst.type) != -1) + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.VAL(kFirst.pos, kFirst.asts, cast(conf.value.err, RawError)))); + + } + else if (["CONT_NT"].indexOf(kFirst.type) != -1) + { + err = conf.value.err; + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.FAIL(new RawError(err.pos, err.nonterminals, err.failedChars, kFirst.nt)))); + + } + else + { + this.state = MachineState.FINAL(cast(conf.value.err, RawError).toParseError(input)); + + } + } + else if (kFirst!= null && kFirst.type == "CONT_SEQ") + { + if (es.length > 0) + { + this.state = MachineState.INTER(MachineConfiguration.EVAL(firstExp, conf.value.pos, conf.value.asts, cast(conf.value.err, RawError), Util.arrayPrepend(Continuations.CONT_SEQ(restExp), kRest))); + + } + else + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), conf.value)); + + } + + } + + //The second continuation used to evaluate PLUS + //is the same continuation as for STAR + else if (kFirst != null && (kFirst.type == "CONT_STAR" || kFirst.type == "CONT_PLUS")) + { + Assert.ok(Std.is(conf.value, Value)); + this.state = MachineState.INTER(MachineConfiguration.EVAL(kFirst.expression, conf.value.pos, conf.value.asts, cast(conf.value.err, RawError), Util.arrayPrepend(Continuations.CONT_STAR(kFirst.expression, conf.value.pos, conf.value.asts), kRest))); + + } + else if (kFirst != null && kFirst.type == "CONT_VOID") + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.VAL(conf.value.pos, kFirst.asts, cast(conf.value.err, RawError)))); + + } + else if (kFirst != null && kFirst.type == "CONT_ALT") + { + + if (conf.value.type == "FAIL" && es.length > 0) + { + this.state = MachineState.INTER(MachineConfiguration.EVAL(Util.first(es), kFirst.pos, kFirst.asts, cast(conf.value.err, RawError), Util.arrayPrepend(Continuations.CONT_ALT(Util.rest(es), kFirst.pos, kFirst.asts), kRest))); + + } + else + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), conf.value)); + + } + } + else if (kFirst != null && kFirst.type == "CONT_OPT") + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), conf.value)); + + } + else if (kFirst != null && kFirst.type == "CONT_AND") + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.VAL(kFirst.pos, kFirst.asts, kFirst.err))); + + } + else if (kFirst != null && kFirst.type == "CONT_NOT") + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.FAIL(kFirst.err))); + + } + else if (kFirst != null && kFirst.type == "CONT_NT") + { + var mode = kFirst.mode; + var name = kFirst.name; + var asts = kFirst.asts; + var nt = kFirst.nt; + + var value:Value = conf.value; + var valAsts:Array = value.asts; + var errPos:Int = cast(value.err, RawError).pos; + var errNts:Array = cast(value.err, RawError).nonterminals; + var errCcs:Array = cast(value.err, RawError).failedChars; + + var newErr = new RawError(errPos, errNts, errCcs, nt); + + switch mode + { + case Modes.NORMAL:{ + valAsts.reverse(); + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.VAL(value.pos, Util.arrayPrepend(AST.TREE(name, valAsts), asts), newErr))); + + }; + case Modes.PRUNING:{ + if (valAsts.length == 0) + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.VAL(value.pos, asts, newErr))); + + } + else if (valAsts.length == 1) + { + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.VAL(value.pos, Util.arrayPrepend(Util.first(valAsts), asts), newErr))); + + } + else{ + valAsts.reverse(); + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.VAL(value.pos, Util.arrayPrepend(AST.TREE(name, valAsts), asts), newErr))); + + } + }; + case Modes.VOIDING:{ + this.state = MachineState.INTER(MachineConfiguration.APPLY(cast(kRest), Value.VAL(value.pos, asts, newErr))); + + }; + } + } + else if (kFirst != null) + { + throw 'unsupported 4'; + } + else if (conf.value.type == "VAL") + { + var ts:Array = conf.value.asts; + + if (eof(conf.value.pos)) + { + if (this.env.get(this.start).mode == Modes.NORMAL) + { + ts.reverse(); + this.state = MachineState.FINAL(AST.TREE(this.start, ts)); + + } + else if (this.env.get(this.start).mode == Modes.PRUNING) + { + + if (ts.length == 0) + { + this.state = MachineState.FINAL(AST.EMPTY()); + + } + else if (ts.length == 1) + { + this.state = MachineState.FINAL(Util.first(ts)); + + } + else + { + ts.reverse(); + this.state = MachineState.FINAL(AST.TREE(this.start, ts)); + + } + } + else + { + this.state = MachineState.FINAL(AST.EMPTY()); + } + } + else if (cast(conf.value.err, RawError) != null && conf.value.pos == cast(conf.value.err, RawError).pos) + { + var err:RawError = conf.value.err; + this.state = MachineState.FINAL((new RawError(conf.value.pos, err.nonterminals, err.failedChars)).toParseError(input)); + + } + else + { + this.state = MachineState.FINAL((new RawError(conf.value.pos, [], [])).toParseError(input)); + + } + } + else if (conf.value.type == "FAIL") + { + this.state = MachineState.FINAL(cast(conf.value.err, RawError).toParseError(input)); + + } + else + { + throw 'unsupported 3'; + } + + }; + default: + + } + + } + + private function visit(charClasses:Dynamic, k, err, pos, cc, asts):MachineState + { + var c1 = null; + var c2 = null; + + if (charClasses.length == 0) + { + return MachineState.INTER(MachineConfiguration.APPLY(k, Value.FAIL(Util.updateError(err, pos, new ErrCC(cc))))); + } + else + { + if (Std.is(Util.first(charClasses), Array)) + { + c1 = Util.first(charClasses)[0]; + c2 = Util.first(charClasses)[1]; + } + else + { + c1 = c2 = Util.first(charClasses); + } + } + + if (c1 <= input.charAt(pos) && c2 >= input.charAt(pos)) + { + return MachineState.INTER(MachineConfiguration.APPLY(k, Value.VAL(pos + 1, Util.arrayPrepend(input.charAt(pos), asts), err))); + } + + return visit(Util.rest(charClasses), k, err, pos, cc, asts); + + } + + public function parse(input):Dynamic + { + return this.match(this.start, input); + } + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/RawError.hx b/src/haxe/org/waxeye/parser/RawError.hx new file mode 100644 index 0000000..ccb63c5 --- /dev/null +++ b/src/haxe/org/waxeye/parser/RawError.hx @@ -0,0 +1,39 @@ +package org.waxeye.parser; +import org.waxeye.parser.Util.Assert; +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ +class RawError +{ + public var pos:Int; + public var nonterminals:Array; + public var failedChars:Array; + public var currentNT:Dynamic; + + public function new(pos:Int, nonterminals:Array, failedChars:Array, ?currentNT:Dynamic) + { + this.pos = pos; + this.nonterminals = nonterminals; + this.failedChars = failedChars; + this.currentNT = currentNT; + + Assert.ok(Std.is(this.nonterminals, Array)); + Assert.ok(Std.is(this.failedChars, Array)); + } + + public function toParseError(input:String):ParseError{ + + var ref = Util.getLineCol(this.pos, input); + + var line = ref[0]; + var col = ref[0]; + this.failedChars.reverse(); + return new ParseError(this.pos, line, col, Util.uniq(this.nonterminals), this.failedChars); + } + +} \ No newline at end of file diff --git a/src/haxe/org/waxeye/parser/Util.hx b/src/haxe/org/waxeye/parser/Util.hx new file mode 100644 index 0000000..6c7142d --- /dev/null +++ b/src/haxe/org/waxeye/parser/Util.hx @@ -0,0 +1,143 @@ +package org.waxeye.parser; +import org.waxeye.parser.RawError; + +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ +class Util +{ + + public function new() {} + + public static function arrayPrepend(item:Any, ?a:Any):Any + { + + if (a != null) + { + a = cast(a, Array).slice(0); + } + + cast(a, Array).unshift(item); + + return a; + } + + public static function uniq(x:Array):Array + { + + var r:Array = []; + for (e in x){ + + if (r.indexOf(e) == -1){ + r.push(e); + } + } + return r; + + } + + + public static function getLineCol(pos:Int, input:String):Array{ + + var col = 0 , line = 0, lastLineBreak = 0; + + for (i in 0...pos){ + if (input.charAt(i) == '\r' && input.charAt(i + 1) == '\n'){ + continue; + } + + var rn:Array = ['\r', '\n']; + + if (rn.indexOf(input.charAt(i)) != -1){ + + line++; + lastLineBreak = i + 1; + + } + col = i - lastLineBreak; + } + + var ret:Array = []; + ret.push(line+1); + ret.push(col); + + return ret; + } + + public static function first(a:Array):Dynamic{ + if (a != null){ + return a[0]; + } + + return null; + } + + public static function rest(a:Array):Dynamic{ + if (a != null){ + return a.slice(1); + } + + return null; + } + + + public static function updateError(err:RawError, pos:Int, e:Any):RawError + { + if (err != null && pos > err.pos){ + + return new RawError(pos, [err.currentNT], [e], err.currentNT); + + } else if (err == null || pos == err.pos){ + + if(err.pos == 0){ + err.pos = 0; + } + + if(err.currentNT == null){ + err.currentNT = ""; + } + + if(err.nonterminals == null){ + err.nonterminals = []; + } + + if(err.failedChars == null){ + err.failedChars = []; + } + + return new RawError(err.pos, Util.arrayPrepend(err.currentNT, err.nonterminals), Util.arrayPrepend(e, err.failedChars), err.currentNT); + } else{ + return new RawError(err.pos, err.nonterminals, err.failedChars, err.currentNT); + } + + } + + +} + +class Assert +{ + + public function new(val:Bool) + { + + ok(val); + } + + public static function ok(val:Bool) + { + if (val == false) + { + + throw 'assertion error'; + } + } +} + + + diff --git a/src/haxe/org/waxeye/parser/Value.hx b/src/haxe/org/waxeye/parser/Value.hx new file mode 100644 index 0000000..e023b8c --- /dev/null +++ b/src/haxe/org/waxeye/parser/Value.hx @@ -0,0 +1,39 @@ +package org.waxeye.parser; +import org.waxeye.parser.Util.Assert; + +/* + * Waxeye Parser Generator + * www.waxeye.org + * Copyright (C) 2008-2010 Orlando Hill + * Copyright (c) 2015 Joshua Gross + * @author Damilare Akinlaja, 2017 + * Licensed under the MIT license. See 'LICENSE' for details. + */ + +class Value +{ + public var type:String; + public var err:Any; + public var pos:Int; + public var asts:Array; + + public function new(type:String, ?err:Any, ?pos:Int, ?asts:Array) + { + this.type = type; + this.err = err; + this.pos = pos; + this.asts = asts; + + } + + public static function FAIL(err:Any):Value + { + return new Value("FAIL", err); + } + + public static function VAL(pos:Int, asts:Array, err:Any):Value + { + return new Value("VAL", err, pos, asts); + } + +} \ No newline at end of file diff --git a/src/waxeye/haxe.rkt b/src/waxeye/haxe.rkt new file mode 100644 index 0000000..c80c29c --- /dev/null +++ b/src/waxeye/haxe.rkt @@ -0,0 +1,194 @@ +;; Waxeye Parser Generator +;; www.waxeye.org +;; Copyright (C) 2008-2010 Orlando Hill +;; Licensed under the MIT license. See 'LICENSE' for details. + +#lang racket/base +(require waxeye/ast + waxeye/fa + "code.rkt" "dfa.rkt" "gen.rkt") +(provide gen-haxe) + + +(define *haxe-parser-name* "") + + +(define (haxe-comment lines) + (comment-bookend "/*" " *" " */" lines)) + + +(define (haxe-doc . lines) + (comment-bookend "/**" " *" " */" lines)) + + +(define (haxe-header-comment) + (if *file-header* + (haxe-comment *file-header*) + (haxe-comment *default-header*))) + + +(define (gen-haxe-names) + (set! *haxe-parser-name* (if *name-prefix* + (string-append *name-prefix* "Parser") + "Parser"))) + + +(define (gen-haxe grammar path) + (gen-haxe-names) + (let ((parser-file (string-append path *haxe-parser-name* ".hx"))) + (dump-string (haxe-parser grammar) parser-file) + (list parser-file))) + + +(define (gen-trans a) + (define (gen-char t) + (format "'~a~a'" + (if (escape-for-java-char? t) "\\" "") + (cond + ((equal? t #\") "\\\"") + ((equal? t #\linefeed) "\\n") + ((equal? t #\tab) "\\t") + ((equal? t #\return) "\\r") + (else t)))) + (define (gen-char-class-item a) + (if (char? a) + (gen-char a) + (format "[~a, ~a]" + (gen-char (car a)) + (gen-char (cdr a))))) + (cond + ((symbol? a) "-1") ;; use -1 for wild card + ((list? a) (gen-array gen-char-class-item a)) + ((char? a) (gen-char a)) + (else a))) + +(define (gen-exp a) + (format "new Exp(~a, ~a)" + (case (ast-t a) + [(wildCard) "ExpType.ANY"] + [(identifier) "ExpType.NT"] + [(void) "ExpType.VOID"] + [(literal) (if (<= (length (ast-c a)) 1) + "ExpType.CHAR" + "ExpType.SEQ")] + [(charClass) "ExpType.CHAR_CLASS"] + [(and) "ExpType.AND"] + [(not) "ExpType.NOT"] + [(optional) "ExpType.OPT"] + [(alternation) "ExpType.ALT"] + [(sequence) "ExpType.SEQ"] + [(closure) "ExpType.STAR"] + [(plus) "ExpType.PLUS"] + [else (format "unknown:~a" (ast-t a))] + ) + (case (ast-t a) + [(wildCard) "[]"] + [(identifier) (format "['~a']" (list->string (ast-c a)))] + [(void) (gen-array gen-exp (ast-c a))] + [(literal) (if (<= (length (ast-c a)) 1) + (gen-trans (ast-c a)) + (gen-array gen-exp (map (lambda (b) + (ast 'literal (cons b '()) '())) + (ast-c a)) ))] + [(charClass) (gen-trans (ast-c a))] + [(and) (gen-array gen-exp (ast-c a))] + [(not) (gen-array gen-exp (ast-c a))] + [(optional) (gen-array gen-exp (ast-c a))] + [(alternation) (gen-array gen-exp (ast-c a))] + [(sequence) (gen-array gen-exp (ast-c a))] + [(closure) (gen-array gen-exp (ast-c a))] + [(plus) (gen-array gen-exp (ast-c a))] + [else (format "unknown:~a" (ast-t a))] + ))) + +(define (gen-def a) + (format "'~a' : {'mode' : Modes.~a, 'exp' : ~a }" + ; non-term name + (list->string (ast-c (list-ref (ast-c a) 0))) + ; mode + (case (ast-t (list-ref (ast-c a) 1)) + ((voidArrow) "VOIDING") + ((pruneArrow) "PRUNING") + ((leftArrow) "NORMAL")) + ; right-hand side expression + (gen-exp (list-ref (ast-c a) 2)) + )) + +(define (gen-defs a) + (gen-map gen-def (ast-c a))) + +(define (gen-map fn data) + (format "{~a}" + (indent (if (null? data) + "" + (string-append (fn (car data)) + (apply string-append (map (lambda (a) + (string-append ",\n" (ind) (fn a))) + (cdr data)))))))) +(define (gen-array fn data) + (format "[~a]" + (indent (if (null? data) + "" + (string-append (fn (car data)) + (apply string-append (map (lambda (a) + (string-append ",\n" (ind) (fn a))) + (cdr data)))))))) + + +(define (haxe-parser grammar) + (format "~a~a\n~a~aclass ~a extends org.waxeye.parser.Parser\n{\n~a}\n" + (haxe-header-comment) + (gen-haxe-package) + (gen-haxe-imports) + (haxe-doc "A parser generated by the Waxeye Parser Generator." "" "@author Waxeye Parser Generator") + *haxe-parser-name* + (indent (string-append (gen-constructor) "\n" (gen-make-def grammar))))) + + +(define (gen-haxe-package) + (if *module-name* + (format "package ~a;\n" *module-name*) + "package;")) + + +(define (gen-haxe-imports) +" +import org.waxeye.parser.*; +import org.waxeye.parser.Exp.ExpType; +import org.waxeye.parser.Modes; +") + + +(define (gen-constructor) + (format "~a~apublic function new()\n~a{\n~a~a}\n" + (haxe-doc (format "Creates a new ~a." *haxe-parser-name*)) + (ind) + (ind) + (indent + (format "~asuper(makeDefinition(), '~a');\n" + (ind) + *start-name*)) + (ind))) + + + +(define (gen-make-def grammar) + (format "~a~aprivate function makeDefinition():Dynamic\n~a{\n~a~a}\n~a\n" + (haxe-doc "Builds the grammar definitions for the parser." "" "@return The definitions grammar for the parser.") + (ind) + (ind) + (indent + (string-append + (format "~avar def:Dynamic = ~a\n" (ind) (gen-defs grammar)) + "\n" + (string-append (ind) "return def;\n"))) + (ind) + (ind) +)) + + + + + + + diff --git a/src/waxeye/javascript.rkt b/src/waxeye/javascript.rkt index bf7ea90..cf410ad 100644 --- a/src/waxeye/javascript.rkt +++ b/src/waxeye/javascript.rkt @@ -4,7 +4,7 @@ ;; Licensed under the MIT license. See 'LICENSE' for details. #lang racket/base -(require waxeye/ast +(require waxeye/ast waxeye/fa "code.rkt" "dfa.rkt" "gen.rkt") (provide gen-javascript) diff --git a/src/waxeye/main.rkt b/src/waxeye/main.rkt index 8ff7005..afb523d 100644 --- a/src/waxeye/main.rkt +++ b/src/waxeye/main.rkt @@ -12,6 +12,7 @@ "gen.rkt" "interp.rkt" "java.rkt" + "haxe.rkt" "javascript.rkt" "load.rkt" "python.rkt" @@ -74,6 +75,7 @@ (set! *target-lang* (case (string->symbol language) ((c) gen-c) ((java) gen-java) + ((haxe) gen-haxe) ((python) gen-python) ((ruby) gen-ruby) ((racket) gen-racket)