<commit_before>package spinat.plsqlparser;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import spinat.plsqlparser.Ast.Expression;
import spinat.plsqlparser.Ast.Statement;

public class Parser {

    static final List<String> badwords = Arrays.asList(new String[]{"insert", "update", "select", "declare", "loop", "end", "while",
        "begin", "null", "in", "out", "exception", "constant", "cursor",
        "pragma", "procedure", "function", "if", "for", "exception", "when", "elsif", "raise", "return", "else", "like",
        "case", "table", "cast"});

    static final Combinator c = new Combinator();

    void must(Res r, Seq s, String msg) {
        if (r == null) {
            throw new ParseException("can not parse: " + msg, s);
        }
    }

    Pa<String> pkw_or = c.forkw("or");
    Pa<String> pkw_and = c.forkw("and");
    Pa<String> pkw_not = c.forkw("not");
    Pa<String> pkw_like = c.forkw("like");
    Pa<String> pkw_between = c.forkw("between");
    Pa<String> pkw_type = c.forkw("type");
    Pa<String> pkw_subtype = c.forkw("subtype");
    Pa<String> pkw_procedure = c.forkw("procedure");
    Pa<String> pkw_function = c.forkw("function");
    Pa<String> pkw_exception = c.forkw("excpetion");
    Pa<String> pkw_is = c.forkw("is");
    Pa<String> pkw_index = c.forkw("index");
    Pa<String> pkw_by = c.forkw("by");
    Pa<String> pkw_not_null = c.forkw2("not", "null");
    Pa<String> pkw_varray = c.forkw("varray");
    Pa<String> pkw_varying_array = c.forkw2("varying", "array");
    Pa<String> pkw_of = c.forkw("of");
    Pa<String> pkw_return = c.forkw("return");
    Pa<String> pkw_pragma = c.forkw("pragma");
    Pa<String> pkw_language = c.forkw("language");
    Pa<String> pkw_begin = c.forkw("begin");
    Pa<String> pkw_end = c.forkw("end");
    Pa<String> pkw_declare = c.forkw("declare");
    Pa<String> pkw_when = c.forkw("when");
    Pa<String> pkw_others = c.forkw("others");
    Pa<String> pkw_then = c.forkw("then");
    Pa<String> pkw_if = c.forkw("if");
    Pa<String> pkw_else = c.forkw("else");
    Pa<String> pkw_elsif = c.forkw("elsif");
    Pa<String> pkw_loop = c.forkw("loop");
    Pa<String> pkw_end_loop = c.forkw2("end", "loop");
    Pa<String> pkw_while = c.forkw("while");
    Pa<String> pkw_for = c.forkw("for");
    Pa<String> pkw_select = c.forkw("select");
    Pa<String> pkw_reverse = c.forkw("reverse");
    Pa<String> pkw_case = c.forkw("case");
    Pa<String> pkw_end_case = c.forkw2("end", "case");
    Pa<String> pkw_raise = c.forkw("raise");
    Pa<String> pkw_open = c.forkw("open");
    Pa<String> pkw_using = c.forkw("using");
    Pa<String> pkw_close = c.forkw("close");
    Pa<String> pkw_end_if = c.forkw2("end", "if");
    Pa<String> pkw_fetch = c.forkw("fetch");
    Pa<String> pkw_into = c.forkw("into");
    Pa<String> pkw_exit = c.forkw("exit");
    Pa<String> pkw_continue = c.forkw("continue");
    Pa<String> pkw_pipe_row = c.forkw2("pipe", "row");
    Pa<String> pkw_execute_immediate = c.forkw2("execute", "immediate");
    Pa<String> pkw_bulk_collect = c.forkw2("bulk", "collect");
    Pa<String> pkw_returning = c.forkw("returning");
    Pa<String> pkw_package_body = c.forkw2("package", "body");
    Pa<String> pkw_cursor = c.forkw("cursor");
    Pa<String> pkw_forall = c.forkw("forall");
    Pa<String> pkw_goto = c.forkw("goto");
    Pa<String> pkw_restrict_references = c.forkw("restrict_references");
    Pa<String> pkw2_pragma_restrict_references = c.forkw2("pragma", "restrict_references");
    Pa<String> pkw_default = c.forkw("default");
    Pa<String> pkw_with = c.forkw("with");
    Pa<String> pkw_limit = c.forkw("limit");
    Pa<String> pkw_range = c.forkw("range");
    Pa<String> pkw_in = c.forkw("in");
    Pa<String> pkw_out = c.forkw("out");
    Pa<String> pkw_in_out = c.forkw2("in", "out");
    Pa<String> pkw_nocopy = c.forkw("nocopy");
    Pa<String> pkw_from = c.forkw("from");
    // do not care about the string, what operators are there else?
    Pa pkw_multiset_union_all = c.seq2(c.forkw2("multiset", "union"), c.forkw("all"));

    Pa<Integer> pNatural = new Pa<Integer>() {

        final Pa<String> p = c.token(TokenType.Int);

        @Override
        public Res<Integer> par(Seq s) {
            Res<String> r = p.pa(s);
            if (r != null) {
                return new Res<>(new Integer(r.v), r.next);
            } else {
                return null;
            }
        }

        @Override
        public String toString() {
            return "pNatural";
        }
    };

    Pa<Integer> pInteger = new Pa<Integer>() {
        final Pa<String> p = c.token(TokenType.Int);

        @Override
        public Res<Integer> par(Seq s) {
            Res rm = c.token(TokenType.Minus).pa(s);
            int sign = 1;
            if (rm != null) {
                s = rm.next;
                sign = -1;
            } else {
                Res rp = c.token(TokenType.Plus).pa(s);
                if (rp != null) {
                    s = rp.next;
                }
            }
            Res<String> r = p.pa(s);
            if (r != null) {
                return new Res<>(new Integer(r.v) * sign, r.next);
            } else {
                return null;
            }
        }

        @Override
        public String toString() {
            return "pNatural";
        }
    };

    final Pa<Ast.Ident> pIdent = new Pa<Ast.Ident>() {

        @Override
        public Res<Ast.Ident> par(Seq s) {
            Token t = s.head();
            if (t.ttype == TokenType.Ident) {
                if (badwords.contains(t.str.toLowerCase())) {
                    return null;
                } else {
                    return new Res<>(new Ast.Ident(t.str.toUpperCase()), s.tail());
                }
            } else if (t.ttype == TokenType.QIdent) {
                return new Res<>(new Ast.Ident(t.str.substring(1, t.str.length() - 1)), s.tail());
            } else {
                return null;
            }
        }

        @Override
        public String toString() {
            return "pIdent";
        }
    };

    final Pa<String> justkw = new Pa<String>() {

        @Override
        public Res<String> par(Seq s) {
            if (s.head().ttype == TokenType.Ident) {
                return new Res<>(s.head().str.toLowerCase(), s.tail());
            } else {
                return null;
            }
        }
    };

    final Pa<Ast.CmpOperator> pCmpOp = new Pa<Ast.CmpOperator>() {

        @Override
        public Res<Ast.CmpOperator> par(Seq s) {
            final Ast.CmpOperator o;
            switch (s.head().ttype) {
                case Less:
                    o = Ast.CmpOperator.LTH;
                    break;
                case LEqual:
                    o = Ast.CmpOperator.LEQ;
                    break;
                case Greater:
                    o = Ast.CmpOperator.GTH;
                    break;
                case GEqual:
                    o = Ast.CmpOperator.GEQ;
                    break;
                case Equal:
                    o = Ast.CmpOperator.EQ;
                    break;
                case NEqual:
                    o = Ast.CmpOperator.NOT_EQ;
                    break;
                default:
                    return null;
            }
            return new Res<>(o, s.tail());
        }
    };

    final Pa<BigInteger> pPlainInt = new Pa<BigInteger>() {

        @Override
        public Res<BigInteger> par(Seq s) {
            if (s.head().ttype == TokenType.Int) {
                return new Res<>(new BigInteger(s.head().str), s.tail());
            } else {
                return null;
            }
        }
    };

    Res<Ast.Expression> paExpr(Seq s) {
        return paOrExpr(s);
    }

    final Pa<Expression> pExpr = new Pa<Expression>() {

        @Override
        public Res<Expression> par(Seq s) {
            return paExpr(s);
        }

        @Override
        public String toString() {
            return "pExpr";
        }
    };

    public Res<Ast.Expression> paOrExpr(Seq s) {
        Res<List<Ast.Expression>> r = c.sep1(pAndExpr, pkw_or).pa(s);
        if (r == null) {
            return null;
        } else {
            return new Res<Ast.Expression>(new Ast.OrExpr(r.v), r.next);
        }
    }

    final Pa<Ast.Expression> pOrExpr = new Pa<Expression>() {
        @Override
        public Res<Expression> par(Seq s) {
            return paOrExpr(s);
        }
    };

    Res<Ast.Expression> paAndExpr(Seq s) {
        Res<List<Ast.Expression>> r = c.sep1(pNotExpression, pkw_and).pa(s);
        if (r == null) {
            return null;
        } else {
            return new Res<Ast.Expression>(new Ast.AndExpr(r.v), r.next);
        }
    }

    final Pa<Ast.Expression> pAndExpr = new Pa<Expression>() {
        @Override
        public Res<Expression> par(Seq s) {
            return paAndExpr(s);
        }
    };

    final Pa<Expression> pNotExpression = new Pa<Expression>() {

        @Override
        public Res<Expression> par(Seq s) {
            Res<String> r = c.opt(pkw_not).pa(s);
            Res<Expression> r2 = paCompExpr(r.next);
            if (r.v == null) {
                return r2;
            } else {
                return new Res<Ast.Expression>(new Ast.NotExpr(r2.v), r2.next);
            }
        }

    };

    final Res<Expression> paCompExpr(Seq s) {
        Res<Ast.Expression> r = paIsNullExpr(s);
        if (r == null) {
            return null;
        }
        Res<Ast.CmpOperator> r2 = pCmpOp.pa(r.next);
        if (r2 == null) {
            return r;
        } else {
            Res<Ast.Expression> r3 = paIsNullExpr(r2.next);
            return new Res<Expression>(new Ast.CompareExpr(r2.v, r.v, r3.v), r3.next);
        }
    }

    Res<Expression> paIsNullExpr(Seq s) {
        Res<Expression> r = paLikeExpr(s);
        if (r == null) {
            return null;
        }
        Res<String> r2 = pkw_is.pa(r.next);
        if (r2 == null) {
            return r;
        }
        Res<String> r3 = c.opt(pkw_not).pa(r2.next);
        Res<Expression> r4 = paLikeExpr(r3.next);
        must(r4, r3.next, "expression");
        return new Res<Expression>(new Ast.IsNullExpr(r4.v, r2.v != null), r4.next);

    }

    Res<Expression> paLikeExpr(Seq s) {
        Res<Expression> r = paBetweenExpr(s);
        if (r == null) {
            return null;
        }
        Res<Boolean> rnot = c.bopt(pkw_not).pa(r.next);
        Res<String> r2 = pkw_like.pa(rnot.next);
        if (r2 == null) {
            return r;
        }
        Res<Expression> r3 = paBetweenExpr(r2.next);
        must(r3, r2.next, "expression");
        Res<String> r4 = c.forkw("escape").pa(r3.next);
        if (r4 == null) {
            return new Res<Expression>(new Ast.LikeExpression(r.v, r3.v, null, rnot.v), r3.next);
        } else {
            Res<Expression> r5 = paBetweenExpr(r4.next);
            must(r5, r4.next, "expression");
            return new Res<Expression>(new Ast.LikeExpression(r.v, r3.v, r5.v, rnot.v), r5.next);
        }
    }

    Res<Expression> paBetweenExpr(Seq s) {
        Res<Expression> r = paInExpression(s);
        if (r == null) {
            return null;
        }
        Res<String> r2 = c.opt(pkw_not).pa(r.next);
        Res<String> r3 = pkw_between.pa(r2.next);
        if (r3 == null) {
            return r;
        }
        Res<Expression> re1 = paInExpression(r3.next);
        Res<String> rand = pkw_and.pa(re1.next);
        Res<Expression> re2 = paInExpression(rand.next);
        if (r2.v == null) {
            return new Res<Expression>(new Ast.BetweenExpression(r.v, re1.v, re2.v), re2.next);
        } else {
            // fixme:  x not between a and b versus not (x between a and b)
            return new Res<Expression>(new Ast.NotExpr(new Ast.BetweenExpression(r.v, re1.v, re2.v)), re2.next);
        }
    }

    Res<Expression> paInExpression(Seq s) {
        // multiset !
        Res<Expression> r = paMultisetExpression(s);
        if (r == null) {
            return null;
        }
        Res<String> r2 = c.opt(c.forkw("not")).pa(r.next);
        Res<String> r3 = c.forkw("in").pa(r2.next);
        if (r3 == null) {
            return r;
        }
        Res<List<Expression>> r4 = c.withParensCommit(c.sep1(pExpr, c.pComma), r3.next);
        Ast.Expression e = new Ast.InExpression(r.v, r4.v);
        if (r2.v == null) {
            return new Res<>(e, r4.next);
        } else {
            return new Res<Expression>(new Ast.NotExpr(e), r4.next);
        }

    }

    Res<Expression> paMultisetExpression(Seq s) {
        // fixme
        Res<Expression> r = paAddExpression(s);
        if (r == null) {
            return null;
        }
        Expression e = r.v;
        Seq ss = r.next;
        while (true) {
            Res rmu = pkw_multiset_union_all.pa(ss);
            if (rmu == null) {
                return new Res<Expression>(e, ss);
            }
            Res<Expression> r2 = paAddExpression(rmu.next);
            must(r2, rmu.next, "expecting an expression");
            e = new Ast.MultisetExpr("multi", e, r2.v);
            ss = r2.next;
        }
    }

    Res<Expression> paAddExpression(Seq s) {
        Res<Expression> r = paMulExpression(s);
        if (r == null) {
            return null;
        }
        Expression e = r.v;
        Seq ss = r.next;
        while (true) {
            final Ast.Binop binop;
            switch (ss.head().ttype) {
                case Plus:
                    binop = Ast.Binop.ADD;
                    break;
                case Minus:
                    binop = Ast.Binop.MINUS;
                    break;
                case StringAdd:
                    binop = Ast.Binop.CONCAT;
                    break;
                default:
                    return new Res<>(e, ss);
            }
            Res<Expression> rx = paMulExpression(ss.tail());
            must(rx, ss.tail(), "expecting an expression");
            e = new Ast.BinopExpression(binop, e, rx.v);
            ss = rx.next;
        }
    }

    Res<Expression> paMulExpression(Seq s) {
        Res<Expression> r = paUnarySignExpr(s);
        if (r == null) {
            return null;
        }
        Expression e = r.v;
        Seq ss = r.next;
        while (true) {
            final Ast.Binop binop;
            switch (ss.head().ttype) {
                case Mul:
                    binop = Ast.Binop.ADD;
                    break;
                case Div:
                    binop = Ast.Binop.MINUS;
                    break;
                case Ident:
                    if (ss.head().str.equalsIgnoreCase("mod")) {
                        binop = Ast.Binop.MOD;
                        break;
                    } else {
                        return new Res<>(e, ss);
                    }
                default:
                    return new Res<>(e, ss);
            }
            Res<Expression> rx = paUnarySignExpr(ss.tail());
            must(rx, ss.tail(), "expecting an expression");
            e = new Ast.BinopExpression(binop, e, rx.v);
            ss = rx.next;
        }
    }

    Res<Expression> paUnarySignExpr(Seq s) {
        switch (s.head().ttype) {
            case Plus:
                Res<Expression> r1 = pExponentExpr(s.tail());
                must(r1, s.tail(), "exptecting expression");
                return new Res<Expression>(new Ast.UnaryPlusExpression(r1.v), r1.next);
            case Minus:
                Res<Expression> r2 = pExponentExpr(s.tail());
                must(r2, s.tail(), "exptecting expression");
                return new Res<Expression>(new Ast.UnaryPlusExpression(r2.v), r2.next);
            default:
                return pExponentExpr(s);
        }

    }

    Res<Expression> pExponentExpr(Seq s) {
        Res<Expression> r = paAtomExpr(s);
        if (r == null) {
            return null;
        }
        Res<String> rp = c.pPower.pa(r.next);
        if (rp == null) {
            return r;
        }
        Res<Expression> re = paAtomExpr(rp.next);
        must(re, rp.next, "Expression expected");
        return new Res<Expression>(new Ast.BinopExpression(Ast.Binop.POWER, r.v, re.v), re.next);
    }
    /* 
     and pAtom s = orn [
     pNumber,
     pString,
     (* true and false are just identifiers in pl/sql ... *)
     pFalse,
     pTrue,
     pParenExpr,
     pSQLAttribute,
     pNew, (* fixme : commit after new *)
     pVariableOrFunctionCall,
     pNull,
     pCaseBoolExpr,
     pCaseMatchExpr
     ] s
     */

    public Res<Expression> paAtomExpr(Seq s) {
        TokenType tt = s.head().ttype;
        if (tt == TokenType.Int) {
            return new Res<Expression>(new Ast.CNumber(new BigDecimal(s.head().str)), s.tail());<commit_msg>fix issue #2<commit_after>package spinat.plsqlparser;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import spinat.plsqlparser.Ast.Expression;
import spinat.plsqlparser.Ast.Statement;

public class Parser {

    static final List<String> badwords = Arrays.asList(new String[]{"insert", "update", "select", "declare", "loop", "end", "while",
        "begin", "null", "in", "out", "exception", "constant", "cursor",
        "pragma", "procedure", "function", "if", "for", "exception", "when", "elsif", "raise", "return", "else", "like",
        "case", "table", "cast"});

    static final Combinator c = new Combinator();

    void must(Res r, Seq s, String msg) {
        if (r == null) {
            throw new ParseException("can not parse: " + msg, s);
        }
    }

    Pa<String> pkw_or = c.forkw("or");
    Pa<String> pkw_and = c.forkw("and");
    Pa<String> pkw_not = c.forkw("not");
    Pa<String> pkw_like = c.forkw("like");
    Pa<String> pkw_between = c.forkw("between");
    Pa<String> pkw_type = c.forkw("type");
    Pa<String> pkw_subtype = c.forkw("subtype");
    Pa<String> pkw_procedure = c.forkw("procedure");
    Pa<String> pkw_function = c.forkw("function");
    Pa<String> pkw_exception = c.forkw("excpetion");
    Pa<String> pkw_is = c.forkw("is");
    Pa<String> pkw_index = c.forkw("index");
    Pa<String> pkw_by = c.forkw("by");
    Pa<String> pkw_not_null = c.forkw2("not", "null");
    Pa<String> pkw_varray = c.forkw("varray");
    Pa<String> pkw_varying_array = c.forkw2("varying", "array");
    Pa<String> pkw_of = c.forkw("of");
    Pa<String> pkw_return = c.forkw("return");
    Pa<String> pkw_pragma = c.forkw("pragma");
    Pa<String> pkw_language = c.forkw("language");
    Pa<String> pkw_begin = c.forkw("begin");
    Pa<String> pkw_end = c.forkw("end");
    Pa<String> pkw_declare = c.forkw("declare");
    Pa<String> pkw_when = c.forkw("when");
    Pa<String> pkw_others = c.forkw("others");
    Pa<String> pkw_then = c.forkw("then");
    Pa<String> pkw_if = c.forkw("if");
    Pa<String> pkw_else = c.forkw("else");
    Pa<String> pkw_elsif = c.forkw("elsif");
    Pa<String> pkw_loop = c.forkw("loop");
    Pa<String> pkw_end_loop = c.forkw2("end", "loop");
    Pa<String> pkw_while = c.forkw("while");
    Pa<String> pkw_for = c.forkw("for");
    Pa<String> pkw_select = c.forkw("select");
    Pa<String> pkw_reverse = c.forkw("reverse");
    Pa<String> pkw_case = c.forkw("case");
    Pa<String> pkw_end_case = c.forkw2("end", "case");
    Pa<String> pkw_raise = c.forkw("raise");
    Pa<String> pkw_open = c.forkw("open");
    Pa<String> pkw_using = c.forkw("using");
    Pa<String> pkw_close = c.forkw("close");
    Pa<String> pkw_end_if = c.forkw2("end", "if");
    Pa<String> pkw_fetch = c.forkw("fetch");
    Pa<String> pkw_into = c.forkw("into");
    Pa<String> pkw_exit = c.forkw("exit");
    Pa<String> pkw_continue = c.forkw("continue");
    Pa<String> pkw_pipe_row = c.forkw2("pipe", "row");
    Pa<String> pkw_execute_immediate = c.forkw2("execute", "immediate");
    Pa<String> pkw_bulk_collect = c.forkw2("bulk", "collect");
    Pa<String> pkw_returning = c.forkw("returning");
    Pa<String> pkw_package_body = c.forkw2("package", "body");
    Pa<String> pkw_cursor = c.forkw("cursor");
    Pa<String> pkw_forall = c.forkw("forall");
    Pa<String> pkw_goto = c.forkw("goto");
    Pa<String> pkw_restrict_references = c.forkw("restrict_references");
    Pa<String> pkw2_pragma_restrict_references = c.forkw2("pragma", "restrict_references");
    Pa<String> pkw_default = c.forkw("default");
    Pa<String> pkw_with = c.forkw("with");
    Pa<String> pkw_limit = c.forkw("limit");
    Pa<String> pkw_range = c.forkw("range");
    Pa<String> pkw_in = c.forkw("in");
    Pa<String> pkw_out = c.forkw("out");
    Pa<String> pkw_in_out = c.forkw2("in", "out");
    Pa<String> pkw_nocopy = c.forkw("nocopy");
    Pa<String> pkw_from = c.forkw("from");
    // do not care about the string, what operators are there else?
    Pa pkw_multiset_union_all = c.seq2(c.forkw2("multiset", "union"), c.forkw("all"));

    Pa<Integer> pNatural = new Pa<Integer>() {

        final Pa<String> p = c.token(TokenType.Int);

        @Override
        public Res<Integer> par(Seq s) {
            Res<String> r = p.pa(s);
            if (r != null) {
                return new Res<>(new Integer(r.v), r.next);
            } else {
                return null;
            }
        }

        @Override
        public String toString() {
            return "pNatural";
        }
    };

    Pa<Integer> pInteger = new Pa<Integer>() {
        final Pa<String> p = c.token(TokenType.Int);

        @Override
        public Res<Integer> par(Seq s) {
            Res rm = c.token(TokenType.Minus).pa(s);
            int sign = 1;
            if (rm != null) {
                s = rm.next;
                sign = -1;
            } else {
                Res rp = c.token(TokenType.Plus).pa(s);
                if (rp != null) {
                    s = rp.next;
                }
            }
            Res<String> r = p.pa(s);
            if (r != null) {
                return new Res<>(new Integer(r.v) * sign, r.next);
            } else {
                return null;
            }
        }

        @Override
        public String toString() {
            return "pNatural";
        }
    };

    final Pa<Ast.Ident> pIdent = new Pa<Ast.Ident>() {

        @Override
        public Res<Ast.Ident> par(Seq s) {
            Token t = s.head();
            if (t.ttype == TokenType.Ident) {
                if (badwords.contains(t.str.toLowerCase())) {
                    return null;
                } else {
                    return new Res<>(new Ast.Ident(t.str.toUpperCase()), s.tail());
                }
            } else if (t.ttype == TokenType.QIdent) {
                return new Res<>(new Ast.Ident(t.str.substring(1, t.str.length() - 1)), s.tail());
            } else {
                return null;
            }
        }

        @Override
        public String toString() {
            return "pIdent";
        }
    };

    final Pa<String> justkw = new Pa<String>() {

        @Override
        public Res<String> par(Seq s) {
            if (s.head().ttype == TokenType.Ident) {
                return new Res<>(s.head().str.toLowerCase(), s.tail());
            } else {
                return null;
            }
        }
    };

    final Pa<Ast.CmpOperator> pCmpOp = new Pa<Ast.CmpOperator>() {

        @Override
        public Res<Ast.CmpOperator> par(Seq s) {
            final Ast.CmpOperator o;
            switch (s.head().ttype) {
                case Less:
                    o = Ast.CmpOperator.LTH;
                    break;
                case LEqual:
                    o = Ast.CmpOperator.LEQ;
                    break;
                case Greater:
                    o = Ast.CmpOperator.GTH;
                    break;
                case GEqual:
                    o = Ast.CmpOperator.GEQ;
                    break;
                case Equal:
                    o = Ast.CmpOperator.EQ;
                    break;
                case NEqual:
                    o = Ast.CmpOperator.NOT_EQ;
                    break;
                default:
                    return null;
            }
            return new Res<>(o, s.tail());
        }
    };

    final Pa<BigInteger> pPlainInt = new Pa<BigInteger>() {

        @Override
        public Res<BigInteger> par(Seq s) {
            if (s.head().ttype == TokenType.Int) {
                return new Res<>(new BigInteger(s.head().str), s.tail());
            } else {
                return null;
            }
        }
    };

    Res<Ast.Expression> paExpr(Seq s) {
        return paOrExpr(s);
    }

    final Pa<Expression> pExpr = new Pa<Expression>() {

        @Override
        public Res<Expression> par(Seq s) {
            return paExpr(s);
        }

        @Override
        public String toString() {
            return "pExpr";
        }
    };

    public Res<Ast.Expression> paOrExpr(Seq s) {
        Res<List<Ast.Expression>> r = c.sep1(pAndExpr, pkw_or).pa(s);
        if (r == null) {
            return null;
        } else {
            return new Res<Ast.Expression>(new Ast.OrExpr(r.v), r.next);
        }
    }

    final Pa<Ast.Expression> pOrExpr = new Pa<Expression>() {
        @Override
        public Res<Expression> par(Seq s) {
            return paOrExpr(s);
        }
    };

    Res<Ast.Expression> paAndExpr(Seq s) {
        Res<List<Ast.Expression>> r = c.sep1(pNotExpression, pkw_and).pa(s);
        if (r == null) {
            return null;
        } else {
            return new Res<Ast.Expression>(new Ast.AndExpr(r.v), r.next);
        }
    }

    final Pa<Ast.Expression> pAndExpr = new Pa<Expression>() {
        @Override
        public Res<Expression> par(Seq s) {
            return paAndExpr(s);
        }
    };

    final Pa<Expression> pNotExpression = new Pa<Expression>() {

        @Override
        public Res<Expression> par(Seq s) {
            Res<String> r = c.opt(pkw_not).pa(s);
            Res<Expression> r2 = paCompExpr(r.next);
            if (r.v == null) {
                return r2;
            } else {
                return new Res<Ast.Expression>(new Ast.NotExpr(r2.v), r2.next);
            }
        }

    };

    final Res<Expression> paCompExpr(Seq s) {
        Res<Ast.Expression> r = paIsNullExpr(s);
        if (r == null) {
            return null;
        }
        Res<Ast.CmpOperator> r2 = pCmpOp.pa(r.next);
        if (r2 == null) {
            return r;
        } else {
            Res<Ast.Expression> r3 = paIsNullExpr(r2.next);
            return new Res<Expression>(new Ast.CompareExpr(r2.v, r.v, r3.v), r3.next);
        }
    }

    Res<Expression> paIsNullExpr(Seq s) {
        Res<Expression> r = paLikeExpr(s);
        if (r == null) {
            return null;
        }
        Res<String> r2 = pkw_is.pa(r.next);
        if (r2 == null) {
            return r;
        }
        Res<String> r3 = c.opt(pkw_not).pa(r2.next);
        Res<Expression> r4 = paLikeExpr(r3.next);
        must(r4, r3.next, "expression");
        return new Res<Expression>(new Ast.IsNullExpr(r4.v, r2.v != null), r4.next);

    }

    Res<Expression> paLikeExpr(Seq s) {
        Res<Expression> r = paBetweenExpr(s);
        if (r == null) {
            return null;
        }
        Res<Boolean> rnot = c.bopt(pkw_not).pa(r.next);
        Res<String> r2 = pkw_like.pa(rnot.next);
        if (r2 == null) {
            return r;
        }
        Res<Expression> r3 = paBetweenExpr(r2.next);
        must(r3, r2.next, "expression");
        Res<String> r4 = c.forkw("escape").pa(r3.next);
        if (r4 == null) {
            return new Res<Expression>(new Ast.LikeExpression(r.v, r3.v, null, rnot.v), r3.next);
        } else {
            Res<Expression> r5 = paBetweenExpr(r4.next);
            must(r5, r4.next, "expression");
            return new Res<Expression>(new Ast.LikeExpression(r.v, r3.v, r5.v, rnot.v), r5.next);
        }
    }

    Res<Expression> paBetweenExpr(Seq s) {
        Res<Expression> r = paInExpression(s);
        if (r == null) {
            return null;
        }
        Res<String> r2 = c.opt(pkw_not).pa(r.next);
        Res<String> r3 = pkw_between.pa(r2.next);
        if (r3 == null) {
            return r;
        }
        Res<Expression> re1 = paInExpression(r3.next);
        Res<String> rand = pkw_and.pa(re1.next);
        Res<Expression> re2 = paInExpression(rand.next);
        if (r2.v == null) {
            return new Res<Expression>(new Ast.BetweenExpression(r.v, re1.v, re2.v), re2.next);
        } else {
            // fixme:  x not between a and b versus not (x between a and b)
            return new Res<Expression>(new Ast.NotExpr(new Ast.BetweenExpression(r.v, re1.v, re2.v)), re2.next);
        }
    }

    Res<Expression> paInExpression(Seq s) {
        // multiset !
        Res<Expression> r = paMultisetExpression(s);
        if (r == null) {
            return null;
        }
        Res<String> r2 = c.opt(c.forkw("not")).pa(r.next);
        Res<String> r3 = c.forkw("in").pa(r2.next);
        if (r3 == null) {
            return r;
        }
        Res<List<Expression>> r4 = c.withParensCommit(c.sep1(pExpr, c.pComma), r3.next);
        Ast.Expression e = new Ast.InExpression(r.v, r4.v);
        if (r2.v == null) {
            return new Res<>(e, r4.next);
        } else {
            return new Res<Expression>(new Ast.NotExpr(e), r4.next);
        }

    }

    Res<Expression> paMultisetExpression(Seq s) {
        // fixme
        Res<Expression> r = paAddExpression(s);
        if (r == null) {
            return null;
        }
        Expression e = r.v;
        Seq ss = r.next;
        while (true) {
            Res rmu = pkw_multiset_union_all.pa(ss);
            if (rmu == null) {
                return new Res<Expression>(e, ss);
            }
            Res<Expression> r2 = paAddExpression(rmu.next);
            must(r2, rmu.next, "expecting an expression");
            e = new Ast.MultisetExpr("multi", e, r2.v);
            ss = r2.next;
        }
    }

    Res<Expression> paAddExpression(Seq s) {
        Res<Expression> r = paMulExpression(s);
        if (r == null) {
            return null;
        }
        Expression e = r.v;
        Seq ss = r.next;
        while (true) {
            final Ast.Binop binop;
            switch (ss.head().ttype) {
                case Plus:
                    binop = Ast.Binop.ADD;
                    break;
                case Minus:
                    binop = Ast.Binop.MINUS;
                    break;
                case StringAdd:
                    binop = Ast.Binop.CONCAT;
                    break;
                default:
                    return new Res<>(e, ss);
            }
            Res<Expression> rx = paMulExpression(ss.tail());
            must(rx, ss.tail(), "expecting an expression");
            e = new Ast.BinopExpression(binop, e, rx.v);
            ss = rx.next;
        }
    }

    Res<Expression> paMulExpression(Seq s) {
        Res<Expression> r = paUnarySignExpr(s);
        if (r == null) {
            return null;
        }
        Expression e = r.v;
        Seq ss = r.next;
        while (true) {
            final Ast.Binop binop;
            switch (ss.head().ttype) {
                case Mul:
                    binop = Ast.Binop.ADD;
                    break;
                case Div:
                    binop = Ast.Binop.MINUS;
                    break;
                case Ident:
                    if (ss.head().str.equalsIgnoreCase("mod")) {
                        binop = Ast.Binop.MOD;
                        break;
                    } else {
                        return new Res<>(e, ss);
                    }
                default:
                    return new Res<>(e, ss);
            }
            Res<Expression> rx = paUnarySignExpr(ss.tail());
            must(rx, ss.tail(), "expecting an expression");
            e = new Ast.BinopExpression(binop, e, rx.v);
            ss = rx.next;
        }
    }

    Res<Expression> paUnarySignExpr(Seq s) {
        switch (s.head().ttype) {
            case Plus:
                Res<Expression> r1 = pExponentExpr(s.tail());
                must(r1, s.tail(), "exptecting expression");
                return new Res<Expression>(new Ast.UnaryPlusExpression(r1.v), r1.next);
            case Minus:
                Res<Expression> r2 = pExponentExpr(s.tail());
                must(r2, s.tail(), "exptecting expression");
                return new Res<Expression>(new Ast.UnaryPlusExpression(r2.v), r2.next);
            default:
                return pExponentExpr(s);
        }

    }

    Res<Expression> pExponentExpr(Seq s) {
        Res<Expression> r = paAtomExpr(s);
        if (r == null) {
            return null;
        }
        Res<String> rp = c.pPower.pa(r.next);
        if (rp == null) {
            return r;
        }
        Res<Expression> re = paAtomExpr(rp.next);
        must(re, rp.next, "Expression expected");
        return new Res<Expression>(new Ast.BinopExpression(Ast.Binop.POWER, r.v, re.v), re.next);
    }
    /* 
     and pAtom s = orn [
     pNumber,
     pString,
     (* true and false are just identifiers in pl/sql ... *)
     pFalse,
     pTrue,
     pParenExpr,
     pSQLAttribute,
     pNew, (* fixme : commit after new *)
     pVariableOrFunctionCall,
     pNull,
     pCaseBoolExpr,
     pCaseMatchExpr
     ] s
     */

    public Res<Expression> paAtomExpr(Seq s) {
        TokenType tt = s.head().ttype;
        if (tt == TokenType.Int || tt == TokenType.Float) {
            return new Res<Expression>(new Ast.CNumber(new BigDecimal(s.head().str)), s.tail());
{'fraction_non_alphanumeric': 0.14411747291136243, 'fraction_numerical': 0.005832198472378471, 'mean_word_length': 1.7092556945781199, 'pattern_counts': {'":': 0, '<': 365, '<?xml version=': 0, '>': 365, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5918040', 'n_tokens_mistral': 11421, 'n_tokens_neox': 10974, 'n_words': 2309}