# Grammar for minijava+ (adopted from MYOCC version 2014) # This package includes this grammar, absyntax.rs, mjlexer.rs and main.rs # There are also several "minijava" programs that can be parsed including # QuickSort.mj and BinaryTree.mj # The grammar defines a scaled-down version of Java similar to the "Tiger" # language in Andrew Appel's compiler textbooks. This language was originally # used in a compilers class taught at Hofstra University in 2014, during which # students wrote their own LR(1) parser generator in Java. It's easier to # implement full LR(1) as opposed to LALR(1), which is all that's needed for # this grammar. # This grammar does not really introduce many more features compared to # what's already covered in the advanced calculator grammar, but it serves as # a larger, more realistic example of how rustlr can be used to build ASTs and # parsers for languages. !use rustlr::{LBox,makelbox}; !use crate::absyntax::*; !use crate::absyntax::Construct::*; !use crate::absyntax::Expr::*; !use crate::absyntax::Stat::*; lifetime 'lt absyntype Construct<'lt> terminal ID INTEGER STRING BOOL terminal class public static void main String extends return length terminal ( ) [ ] ; DOT ! , new this terminal LBR RBR OROR terminal int boolean if else while == = + - * / < && MOD nonterminal Program nonterminal MainCl nonterminal ClassDec nonterminal ClassDecl nonterminal Extension nonterminal VarDec nonterminal MethodDec nonterminal Decl nonterminal FormalLst nonterminal FormalRst nonterminal Type nonterminal Stat nonterminal Stats nonterminal Exp nonterminal ExpLst nonterminal ExpRst topsym Program resync ; left + 500 left - 510 left * 700 left / 710 left && 400 left OROR 350 left ! 450 left == 310 left = 800 left < 300 left MOD 705 left DOT 810 # to deal with the dangling-else problem, else is given higher precedence # than if. Reduction by (Stat --> if (Exp) Stat) will be delayed if the # the lookahead symbol is 'else'. left if 30 left else 40 Program --> MainCl:@Maincl(mc)@ ClassDecl:@Classes(cs)@ { Program(parser.lb(mc),cs) } MainCl ==> class ID:@Id(cn)@ LBR public static void main ( String [ ] ID:@Id(an)@ ) LBR Stats:@Stms(thebody)@ RBR RBR { Maincl(Mainclass{classname:cn, argvname:an, body: Blockst(thebody), }) } <== ClassDecl --> { Classes(Vec::new()) } ClassDecl ==> ClassDecl:@Classes(mut cs)@ ClassDec:@Class(cl)@ { cs.push(parser.lbx(1,cl)); Classes(cs) } <== ClassDec ==> class ID:@Id(name)@ Extension:@Id(sup)@ LBR Decl:@Decs(mut ds)@ RBR { let mut vdecs=Vec::new(); let mut mdecs=Vec::new(); separatedecs(ds,&mut vdecs,&mut mdecs); /*split var and method declarations*/ Class(ClassDec {superclass:sup, classname:name, vars:vdecs, methods:mdecs}) } <== Extension --> extends ID:sup { sup.value } Extension --> { Id("Object") } VarDec --> Type:@Id(t)@ ID:@Id(v)@ ; { Vdec(VarDec{dname:v,dtype:t,initval:Nothing,}) } VarDec ==> Type:@Id(t)@ ID:@Id(v)@ = Exp:@Exp(e)@ ; { Vdec(VarDec{dname:v,dtype:t,initval:e}) } <== MethodDec ==> public Type:@Id(ty)@ ID:@Id(name)@ ( FormalLst:@Vdecs(args)@ ) LBR Stats:@Stms(mbody)@ RBR { Method(MethodDec{ formals:args, body: mbody, classname:ty, methodname:name, }) } <== Decl --> { Decs(Vec::new()) } Decl --> Decl:@Decs(mut ds)@ VarDec:v { ds.push(parser.lbx(1,v.value)); Decs(ds) } Decl --> Decl:@Decs(mut ds)@ MethodDec:m { ds.push(makelbox!(m,m.value)); Decs(ds) } FormalLst --> { Vdecs(Vec::new()) } # warning: list constructed backwards: FormalLst ==> Type:@Id(ty)@ ID:@Id(a)@ FormalRst:@Vdecs(mut frs)@ { frs.push(parser.lb(VarDec{dname:a,dtype:ty,initval:Nothing})); Vdecs(frs) } <== FormalRst --> { Vdecs(Vec::new()) } FormalRst ==> , Type:@Id(ty)@ ID:@Id(a)@ FormalRst:@Vdecs(mut frs)@ { frs.push(parser.lb(VarDec{dname:a,dtype:ty,initval:Nothing})); Vdecs(frs) } <== Type --> int [ ] { return Id("int[]"); } Type --> boolean { return Id("boolean"); } Type --> String { return Id("String"); } Type --> int { return Id("int"); } Type --> void { return Id("void"); } Type --> ID:c { c.value } Stats --> { Stms(Vec::new()) } Stats --> Stats:@Stms(mut sv)@ Stat:@Stm(s)@ { sv.push(makelbox!(_item1_,s)); Stms(sv) } Stat --> LBR Stats:@Stms(sv)@ RBR { Stm(Blockst(sv)) } Stat ==> if ( Exp:@Exp(c)@ ) Stat:@Stm(a)@ else Stat:@Stm(b)@ { Stm(Ifstat(makelbox!(_item2_,c), makelbox!(_item4_,a), makelbox!(_item6_,b))) } <== Stat --> if ( Exp:@Exp(c)@ ) Stat:@Stm(a)@ { Stm(Ifstat(makelbox!(_item2_,c),makelbox!(_item4_,a),parser.lbx(5,Nopst))) } Stat --> while ( Exp:@Exp(c)@ ) Stat:@Stm(s)@ { Stm(Whilest(parser.lbx(2,c),parser.lbx(4,s))) } Stat --> ID:@Id(v)@ = Exp:@Exp(e)@ ; { Stm(Assignst(v,parser.lbx(2,e))) } ### either change both leading Exp to ID or keep both as Exp Stat ==> Exp:@Exp(v)@ [ Exp:@Exp(i)@ ] = Exp:@Exp(e)@ ; { Stm(ArAssignst(parser.lbx(0,v),parser.lbx(2,i),parser.lbx(5,e))) } <== Stat ==> Exp:@Exp(obj)@ DOT ID:@Id(m)@ ( ExpLst:@Exps(args)@ ) ; { Stm(Callstat(parser.lb(obj),m,args)) } <== Stat --> return Exp:@Exp(e)@ ; { Stm(Returnst(parser.lbx(1,e))) } Stat --> VarDec:@Vdec(v)@ {Stm(Vardecst(v.dname,v.dtype,parser.lb(v.initval)))} Exp --> Exp:@Exp(a)@ * Exp:@Exp(b)@ {Exp(Binop("*",parser.lbx(0,a),parser.lbx(2,b)))} Exp --> Exp:@Exp(a)@ + Exp:@Exp(b)@ {Exp(Binop("+",parser.lbx(0,a),parser.lbx(2,b)))} Exp --> Exp:@Exp(a)@ / Exp:@Exp(b)@ {Exp(Binop("/",parser.lbx(0,a),parser.lbx(2,b)))} Exp --> Exp:@Exp(a)@ - Exp:@Exp(b)@ {Exp(Binop("-",parser.lbx(0,a),parser.lbx(2,b)))} Exp --> Exp:@Exp(a)@ && Exp:@Exp(b)@ {Exp(Binop("&&",parser.lbx(0,a),parser.lbx(2,b)))} Exp --> Exp:@Exp(a)@ OROR Exp:@Exp(b)@ {Exp(Binop("OROR",parser.lbx(0,a),parser.lbx(2,b)))} Exp --> ! Exp:@Exp(a)@ { Exp(Notexp(parser.lbx(1,a))) } Exp --> Exp:@Exp(a)@ < Exp:@Exp(b)@ {Exp(Binop("<",parser.lbx(0,a),parser.lbx(2,b)))} Exp --> Exp:@Exp(a)@ MOD Exp:@Exp(b)@ {Exp(Binop("%",parser.lbx(0,a),parser.lbx(2,b)))} Exp --> Exp:@Exp(a)@ == Exp:@Exp(b)@ {Exp(Binop("==",parser.lbx(0,a),parser.lbx(2,b)))} Exp --> Exp:@Exp(a)@ [ Exp:@Exp(i)@ ] {Exp(Binop("[]",parser.lbx(0,a),parser.lbx(2,i)))} Exp --> Exp:@Exp(obj)@ DOT ID:@Id(field)@ { Exp(Field(field,parser.lbx(0,obj))) } Exp --> Exp:@Exp(obj)@ DOT ID:@Id(f)@ ( ExpLst:@Exps(args)@ ) { Exp(Callexp(parser.lb(obj),f,args)) } Exp --> INTEGER:@i@ { i } Exp --> STRING:s { s.value } Exp --> BOOL:@b@ { b } Exp --> ID:@Id(x)@ { Exp(Var(x)) } Exp --> this { Exp(Thisptr) } Exp --> new int [ Exp:@Exp(s)@ ] { Exp(Newarray(parser.lbx(3,s))) } Exp --> new ID:@Id(x)@ ( ) { Exp(Newobj(x)) } Exp --> ( Exp:e ) { e.value } # warning: backwards list: ExpLst --> { Exps(Vec::new()) } ExpLst --> Exp:@Exp(e)@ ExpRst:@Exps(mut er)@ { er.push(parser.lbx(0,e)); Exps(er) } ExpRst --> { Exps(Vec::new()) } ExpRst --> , Exp:@Exp(e)@ ExpRst:@Exps(mut er)@ { er.push(parser.lbx(1,e)); Exps(er) } # Lexical scanner setup using StrTokenizer lexname DOT . lexname MOD % lexname LBR { lexname RBR } lexname OROR || lexvalue BOOL Alphanum("true") Exp(Bool(true)) lexvalue BOOL Alphanum("false") Exp(Bool(false)) lexvalue ID Alphanum(x) Id(x) lexvalue INTEGER Num(n) Exp(Int(n as i32)) lexvalue STRING Strlit(s) Exp(Strlit(s)) EOF