waxeye-org / waxeye

Waxeye is a parser generator based on parsing expression grammars (PEGs). It supports C, Java, JavaScript, Python, Racket, and Ruby.
https://waxeye-org.github.io/waxeye/index.html
Other
235 stars 38 forks source link

Waxeye Parser Generator for the Haxe Language Target #43

Open darmie opened 7 years ago

darmie commented 7 years ago

I have written a parser generator for Haxe based on the Javascript implementation. Parser has been tested and it's working.

Please see https://github.com/darmie/waxeye-hx-sample for full implementation of the Calculator example.

Build Waxeye source and Generate Parser code

waxeye -g haxe . grammars/calc.waxeye

Generated Code

/*
 * Generated by the Waxeye Parser Generator - version 0.8.1
 * www.waxeye.org
 */
package;

import org.waxeye.parser.*;
import org.waxeye.parser.Exp.ExpType;
import org.waxeye.parser.Modes;
//import haxe.ds.StringMap;
/**
 * A parser generated by the Waxeye Parser Generator.
 *
 * @author Waxeye Parser Generator
 */
class Parser extends org.waxeye.parser.Parser
{
    /**
     * Creates a new Parser.
     */

    public function new()
    {
        super(makeDefinition(), 'calc');
    }

    /**
     * Builds the grammar definitions for the parser.
     *
     * @return The definitions grammar for the parser.
     */
    @:keep
    private function makeDefinition():Dynamic
    {
        var def:Dynamic = {calc : {'mode' : Modes.NORMAL, 'exp' : new Exp(ExpType.SEQ, [new Exp(ExpType.NT, ['ws']),
                new Exp(ExpType.NT, ['sum'])]) },
            sum : {'mode' : Modes.NORMAL, 'exp' : new Exp(ExpType.SEQ, [new Exp(ExpType.NT, ['prod']),
                new Exp(ExpType.STAR, [new Exp(ExpType.SEQ, [new Exp(ExpType.CHAR_CLASS, ['+',
                            '-']),
                        new Exp(ExpType.NT, ['ws']),
                        new Exp(ExpType.NT, ['prod'])])])]) },
            prod : {'mode' : Modes.NORMAL, 'exp' : new Exp(ExpType.SEQ, [new Exp(ExpType.NT, ['unary']),
                new Exp(ExpType.STAR, [new Exp(ExpType.SEQ, [new Exp(ExpType.CHAR_CLASS, ['*',
                            '/']),
                        new Exp(ExpType.NT, ['ws']),
                        new Exp(ExpType.NT, ['unary'])])])]) },
            unary : {'mode' : Modes.PRUNING, 'exp' : new Exp(ExpType.ALT, [new Exp(ExpType.SEQ, [new Exp(ExpType.CHAR, ['-']),
                    new Exp(ExpType.NT, ['ws']),
                    new Exp(ExpType.NT, ['unary'])]),
                new Exp(ExpType.SEQ, [new Exp(ExpType.VOID, [new Exp(ExpType.CHAR, ['('])]),
                    new Exp(ExpType.NT, ['ws']),
                    new Exp(ExpType.NT, ['sum']),
                    new Exp(ExpType.VOID, [new Exp(ExpType.CHAR, [')'])]),
                    new Exp(ExpType.NT, ['ws'])]),
                new Exp(ExpType.NT, ['num'])]) },
            num : {'mode' : Modes.NORMAL, 'exp' : new Exp(ExpType.SEQ, [new Exp(ExpType.PLUS, [new Exp(ExpType.CHAR_CLASS, [['0', '9']])]),
                new Exp(ExpType.OPT, [new Exp(ExpType.SEQ, [new Exp(ExpType.CHAR, ['.']),
                        new Exp(ExpType.PLUS, [new Exp(ExpType.CHAR_CLASS, [['0', '9']])])])]),
                new Exp(ExpType.NT, ['ws'])]) },
            ws : {'mode' : Modes.VOIDING, 'exp' : new Exp(ExpType.STAR, [new Exp(ExpType.CHAR_CLASS, [['\t', '\n'],
                    '\r',
                    ' '])]) }}

        return def;
    }

}

Implementation of the calculator

package;
import Parser;
import org.waxeye.parser.AST;
import org.waxeye.parser.ParseError;
import haxe.Json;

/**
 * ...
 * @author Damilare Akinlaja
 */
class Main 
{

    public static function main() 
    {

        var math = Sys.stdin();
        Sys.print("Calc: ");
        var calc = math.readLine();
        var ast = (new Parser()).parse(calc);
        if (Std.is(ast, AST)){
            Sys.print("=> "+calc+" = "+sum(ast.children[0]));
        }else if(Std.is(ast, ParseError)){
            Sys.print(ast.toString());
        }

    }

    private static function binOp(ast:AST, fn:AST->Dynamic, ch:String, op1:Dynamic, op2:Dynamic):String
    {
        var chil:Array<Any> = ast.children;
        // apply the visitor function to our first sub-tree
        var val = fn(chil[0]);

        var i = 1;

        while(i != chil.length){
            // choose our operator function
             var operator = chil[i] == ch ? op1 : op2;

             // apply the visitor function to our second sub-tree

             var operand = fn(chil[i + 1]);

             // apply the operator to our current value and the second sub-tree
             val = operator(val, operand);
             // move on to the next operator and sub-tree
             i += 2;
        }

        return val;

    }

    private static function sum(ast:AST):String
    {
        var add = function(a, b){return a + b; };
        var sub = function(a, b){return a - b; };

        return binOp(ast, prod, '+', add, sub);
    }

    private static function prod(ast:AST):String
    {
        var mult = function(a, b){return a * b; };
        var div = function(a, b){return a / b; };

        return binOp(ast, unary, '*', mult, div);
    }

    private static function unary(ast:AST):Dynamic
    {
        if (ast.type == 'unary') {
            // the unary rule is a pruning non-terminal
            // the only case we will see it is if we have negation
            return - unary(ast.children[1]);
        }
        else {
            if (ast.type == 'sum') {
                return sum(ast);
            }
            else {
                return num(ast);
            }
        }       

    }

    private static function num(ast:AST):Float
    {
        return Std.parseFloat(ast.children.join(''));
    }

}

How to run implementation

Download and Install Haxe

Install haxe binary from http://haxe.org

Clone Repo

git clone https://github.com/darmie/waxeye-hx-sample.git waxeye-sample

Build and Run Project

> haxe waxeye-sample/build.hxml
> neko neko/Main.n

Tested and Works on the following Haxe supported Targets

  1. C/C++
  2. Java
  3. NekoVM
  4. C#
  5. Lua It is expected to work on all Haxe supported targets
darmie commented 7 years ago

@jishi9 Thanks, those were not supposed to make it to the PR, I would make neccessary corrections and push.

glebm commented 7 years ago

Could you please add tests for Haxe similar to the JavaScript tests in test/ (added in #45)?

Also, I wonder if this support Unicode when compiled to JavaScript? For the native JavaScript runtime, Unicode support is added in #47.

darmie commented 7 years ago

@glebm I'll work on the /test and would look into how Unicode is supported in Haxe.

darmie commented 7 years ago

There's a Haxe API for ensuring Unicode support across target platforms.

http://api.haxe.org/haxe/Utf8.html

glebm commented 7 years ago

Using UTF-8 is a bit inefficient in languages that use UTF-16 natively (JavaScript, Java, C#).

I was hoping Haxe had a solution for Unicode support using native String types on each platform, but looks like that is still a work in progress.

https://github.com/HaxeFoundation/haxe/issues/3072 https://try.haxe.org/#d9D04

darmie commented 7 years ago

@glebm thanks for pointing this out. My knowledge of unicode is somewhat limited. I would sleep on it and see what solutions are out there. 👍

bkil commented 3 years ago

It seems that the linked Haxe issue had been solved in 2018, so it might be worthwhile to look into this question again. I'd really find support of either Haxe (or PHP directly via #104) useful, as it is a pretty common freely hosted server side language.

darmie commented 3 years ago

@bkil waxeye has really evolved since I opened this PR. There have been lots of changes in the Haxe ecosystem too. Maybe I'll work on this again.