%{ (* Parser for a small imperative language (uC). sestoft@dina.kvl.dk * 2001-03-18, 2002-03-15 No parser conflicts thanks to Niels Jørgen Kokholm. *) open Absyn; fun compose1 f (g, s) = (g o f, s); val nl = Cst(CstI 10) %} %token CSTINT CSTBOOL %token CSTSTRING NAME %token CHAR ELSE FOR IF INT NULL PRINT PRINTLN RETURN VOID WHILE %token PLUS MINUS TIMES EQ DIV MOD %token EQ NE GT LT GE LE %token NOT SEQOR SEQAND %token LPAR RPAR LBRACE RBRACE LBRACK RBRACK SEMI COMMA ASSIGN AMP %token EOF %right ASSIGN /* lowest precedence */ %nonassoc PRINT %left SEQOR %left SEQAND %left EQ NE %nonassoc GT LT GE LE %left PLUS MINUS %left TIMES DIV MOD %nonassoc NOT AMP %nonassoc LBRACK /* highest precedence */ %start Main %type Main %type Topdecs %type Topdec Fundec %type Vardec %type <(Absyn.typ -> Absyn.typ) * string> Vardesc %type Paramdecs Paramdecs1 %type Stmt StmtM StmtU Block %type StmtOrDecSeq %type Expr ExprNotAccess AtExprNotAccess %type Exprs Exprs1 %type Access %type Const %type Type %% Main: Topdecs EOF { Prog $1 } ; Topdecs: /* empty */ { [] } | Topdec Topdecs { $1 :: $2 } ; Topdec: Vardec SEMI { Vardec $1 } | Fundec { $1 } ; Vardec: Type Vardesc { (#1 $2 $1, #2 $2) } ; Vardesc: NAME { (fn t => t, $1) } | TIMES Vardesc { compose1 TypP $2 } | LPAR Vardesc RPAR { $2 } | Vardesc LBRACK RBRACK { compose1 (fn t => TypA(t, NONE)) $1 } | Vardesc LBRACK Expr RBRACK { compose1 (fn t => TypA(t, SOME $3)) $1 } ; Fundec: VOID NAME LPAR Paramdecs RPAR Block { Fundec(NONE, $2, $4, $6) } | Type NAME LPAR Paramdecs RPAR Block { Fundec(SOME($1), $2, $4, $6) } ; Paramdecs: /* empty */ { [] } | Paramdecs1 { $1 } ; Paramdecs1: Vardec { [$1] } | Vardec COMMA Paramdecs1 { $1 :: $3 } ; Block: LBRACE StmtOrDecSeq RBRACE { Block $2 } ; StmtOrDecSeq: /* empty */ { [] } | Stmt StmtOrDecSeq { Stmt $1 :: $2 } | Vardec SEMI StmtOrDecSeq { Dec $1 :: $3 } ; Stmt: StmtM { $1 } | StmtU { $1 } ; StmtM: /* No unbalanced if-else */ Expr SEMI { Expr($1) } | RETURN SEMI { Return NONE } | RETURN Expr SEMI { Return(SOME($2)) } | Block { $1 } | IF LPAR Expr RPAR StmtM ELSE StmtM { If($3, $5, $7) } | WHILE LPAR Expr RPAR StmtM { While($3, $5) } ; StmtU: IF LPAR Expr RPAR StmtM ELSE StmtU { If($3, $5, $7) } | IF LPAR Expr RPAR Stmt { If($3, $5, Block []) } | WHILE LPAR Expr RPAR StmtU { While($3, $5) } ; Expr: Access { Access $1 } | ExprNotAccess { $1 } ; ExprNotAccess: AtExprNotAccess { $1 } | Access ASSIGN Expr { Assign($1, $3) } | NAME LPAR Exprs RPAR { Call($1, $3) } | NOT Expr { Prim1("!", $2) } | PRINT Expr { Prim1("printi", $2) } | PRINTLN { Prim1("printc", nl) } | Expr PLUS Expr { Prim2("+", $1, $3) } | Expr MINUS Expr { Prim2("-", $1, $3) } | Expr TIMES Expr { Prim2("*", $1, $3) } | Expr DIV Expr { Prim2("/", $1, $3) } | Expr MOD Expr { Prim2("%", $1, $3) } | Expr EQ Expr { Prim2("==", $1, $3) } | Expr NE Expr { Prim2("!=", $1, $3) } | Expr GT Expr { Prim2(">", $1, $3) } | Expr LT Expr { Prim2("<", $1, $3) } | Expr GE Expr { Prim2(">=", $1, $3) } | Expr LE Expr { Prim2("<=", $1, $3) } | Expr SEQAND Expr { Andalso($1, $3) } | Expr SEQOR Expr { Orelse($1, $3) } ; AtExprNotAccess: Const { Cst($1) } | LPAR ExprNotAccess RPAR { $2 } | AMP Access { Addr $2 } ; Access: NAME { AccVar $1 } | LPAR Access RPAR { $2 } | TIMES Access { AccDeref (Access $2)} | TIMES AtExprNotAccess { AccDeref $2 } | Access LBRACK Expr RBRACK { AccIndex($1, $3) } ; Exprs: /* empty */ { [] } | Exprs1 { $1 } ; Exprs1: Expr { [$1] } | Expr COMMA Exprs1 { $1 :: $3 } ; Const: CSTINT { CstI($1) } | CSTBOOL { CstI($1) } | MINUS CSTINT { CstI(~ $2) } | NULL { CstN } ; Type: INT { TypI } | CHAR { TypC } ;