├── .gitignore ├── LICENSE ├── README.md ├── build.xml ├── docs ├── README ├── hier.sty ├── jacc.bib ├── jacc.pdf ├── jacc.tex ├── jaccNutshell.pdf └── nutshell.ppt ├── examples ├── Calc.errs ├── Calc.jacc ├── MoreCalc.errs ├── Unary.jacc ├── dang.ex ├── dang.jacc ├── example1 ├── example2 └── simpleCalc.jacc ├── images └── jacc.gif ├── scripts ├── jacc └── jacc.bat └── src ├── compiler ├── Diagnostic.java ├── Failure.java ├── Handler.java ├── JavaSource.java ├── Lexer.java ├── Phase.java ├── Position.java ├── SimpleHandler.java ├── Source.java ├── SourceLexer.java ├── SourcePosition.java ├── StdinSource.java └── Warning.java └── jacc ├── CommandLine.java ├── Conflicts.java ├── DebugLexer.java ├── DotOutput.java ├── Fixity.java ├── HTMLOutput.java ├── JaccAbstractParser.java ├── JaccJob.java ├── JaccLexer.java ├── JaccParser.java ├── JaccProd.java ├── JaccResolver.java ├── JaccSymbol.java ├── JaccTables.java ├── JaccTokens.java ├── Output.java ├── ParserOutput.java ├── Settings.java ├── TextOutput.java ├── TokensOutput.java ├── grammar ├── Analysis.java ├── Finitary.java ├── First.java ├── Follow.java ├── Grammar.java ├── LALRMachine.java ├── LR0Items.java ├── LR0Machine.java ├── Left.java ├── LookaheadMachine.java ├── Machine.java ├── Nullable.java ├── Parser.java ├── Resolver.java ├── SLRMachine.java └── Tables.java └── util ├── BitSet.java ├── DepthFirst.java ├── ElemInterator.java ├── IntSet.java ├── Interator.java ├── SCC.java └── SeqInterator.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.aux 3 | *.log 4 | *.bbl 5 | *.blg 6 | dist/ 7 | bin/ 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jacc 2 | ![jacc logo](images/jacc.gif) 3 | 4 | Copyright Mark P Jones, Portland State University, 2004-2016. 5 | 6 | This repository provides a copy of the source code for jacc, just another 7 | compiler compiler for Java. The materials here were imported from 8 | the [jacc homepage](http://web.cecs.pdx.edu/~mpj/jacc) and are 9 | made available here as free software under the GPL 3.0 license. 10 | 11 | # Overview 12 | jacc is a parser generator for [java](http://java.sun.com). But there 13 | are already several other parser generators for Java, including CUP, 14 | Antlr, JavaCC, SableCC, Coco/R, BYACC/Java, and the Jikes Parser Generator. 15 | So why would somebody write yet another one, and why might you want to use it? 16 | 17 | In short, what makes jacc different from other 18 | tools is its combination of the following features: 19 | 20 | - Close syntactic compatibility with 21 | Johnson's classic yacc parser generator for C 22 | (in so far as is possible given that the two tools target 23 | different languages); 24 | 25 | - Semantic compatibility with yacc: 26 | jacc generates bottom-up/shift-reduce parsers for LALR(1) 27 | grammars with disambiguating rules; 28 | 29 | - Implemented in pure Java, so it is 30 | portable and runs on many Java development platforms; 31 | 32 | - Modest additions to help users understand and debug 33 | generated parsers, including: a feature for tracing 34 | parser behavior on sample inputs, HTML output, and 35 | tests for LR(0) and SLR(1) conflicts; 36 | 37 | - Primitive support for distributing grammar descriptions 38 | across multiple files to support modular construction 39 | or extension of parsers; 40 | 41 | - A mechanism for generating syntax error messages 42 | from examples based on ideas described 43 | by [Clinton Jeffery](http://unicon.sourceforge.net/merr/); 44 | 45 | - Generated parsers that use a technique 46 | described by Bhamidipaty and Proebsting for creating 47 | very fast yacc-compatible parsers by generating code 48 | instead of encoding the specifics of a particular 49 | parser in a set of tables as the classic yacc 50 | implementations normally do. 51 | 52 | So, if you are looking for a yacc-compatible 53 | parser generator for Java, then perhaps jacc may meet your needs! 54 | 55 | # Installation 56 | Installation of this software requires a suitable Java development 57 | kit as well as a copy of ant. I have used these instructions with 58 | a variety of different Java versions on a variety of different 59 | platforms. 60 | 61 | To build, use the command `ant` (or `ant jacc`, if you prefer). 62 | This will create a file `jacc.jar` in the `dist/` folder that you 63 | should move to a suitable directory on your machine. After this: 64 | 65 | - On Windows: 66 | Copy the batch file `scripts/jacc.bat` to a directory on your path. 67 | You should edit this file so that the string `%JACC_PATH%` is 68 | replaced with the absolute path of the directory containing 69 | `jacc.jar` (or else set the environment variable `JACC_PATH` to 70 | point to that directory). 71 | 72 | - On Unix: 73 | Copy the file `scripts/jacc` to a directory on your path. 74 | You should edit this file so that the string `$JACC_PATH` is 75 | replaced with the absolute path of the directory containing 76 | `jacc.jar` (or else set the environment variable `JACC_PATH` 77 | to point to that directory). 78 | 79 | After the appropriate files have been installed, you can use the command 80 | `ant clean` to delete temporary files that were constructed during the 81 | installation process. 82 | 83 | # History 84 | jacc was written by Mark P Jones at the end of 1999 for use in his 85 | class on compiler construction at the beginning of 2000. It has been 86 | used in several different classes and projects since then, but has not 87 | yet been widely distributed. Comments and suggestions that might help 88 | to improve either the tool or the documentation are always welcome! 89 | 90 | # License 91 | Early attempts to obtain approval for open source distribution of jacc 92 | were delayed for many years. This version, corresponding very closely 93 | to the original distribution from April 2004 but with the addition of 94 | extra code for generating dot descriptions of generated machines, is 95 | distributed under a GPL license; please see the file LICENSE for more 96 | details. 97 | 98 | # Known issues 99 | This version of jacc has been in use for many years and is believed 100 | to work well in most cases. There is, however, a known issue in the 101 | treatment of `%nonassoc` directives that will be fixed in a future 102 | update. 103 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/README: -------------------------------------------------------------------------------- 1 | To build the documentation, run: 2 | 3 | pdflatex jacc 4 | bibtex jacc 5 | pdflatex jacc 6 | pdflatex jacc 7 | 8 | -------------------------------------------------------------------------------- /docs/hier.sty: -------------------------------------------------------------------------------- 1 | \newdimen\hierlinethickness % thickness of lines used in the drawing 2 | \hierlinethickness=0.2pt 3 | 4 | \newdimen\hierboxsep % separation between box border and contents 5 | \hierboxsep=1pt 6 | 7 | \newdimen\hiervdrop % vertical drop from top of box to horiz entry 8 | \hiervdrop=2mm 9 | 10 | \newdimen\hiervgap % vertical gap between boxes 11 | \hiervgap=2mm 12 | 13 | \newdimen\hierhgap % horizontal gap between boxes and horiz lines 14 | \hierhgap=1mm 15 | 16 | \newdimen\hierboxwidth % width of framed boxes in hierarchy diagrams 17 | \hierboxwidth=26mm 18 | 19 | \newdimen\scrdim 20 | \newdimen\joinh 21 | 22 | \newbox\scrbox 23 | \newbox\vcol 24 | \newbox\hierleft 25 | \newbox\hierright 26 | 27 | % \toDrop{arg}: forms a box from the specified argument, lowering it as 28 | % necessary so that the final result has height \hiervdrop. 29 | \def\toDrop#1{\setbox\scrbox=#1\scrdim=\ht\scrbox 30 | \advance\scrdim by-\hiervdrop 31 | \lower\scrdim\box\scrbox} 32 | 33 | \def\hierLineIntoBox{\vrule depth0pt width2\hierhgap height\hierlinethickness} 34 | \def\hierbox#1{\vbox{\hrule width\hierboxwidth height\hierlinethickness 35 | \hbox to \hierboxwidth 36 | {\vrule width\hierlinethickness 37 | \strut #1\vrule width\hierlinethickness 38 | }\hrule width\hierboxwidth height\hierlinethickness}} 39 | 40 | \def\hier#1{% 41 | \setbox\hierleft=\hbox{\hierLineIntoBox\toDrop{\hierbox{\,#1\hfill}}}% 42 | \@ifnextchar+{\colInit}{% 43 | \@ifnextchar-{\vcolInit}{% 44 | \setbox\hierright=\hbox{}\hierDisplay}}} 45 | 46 | \def\basehier#1{% 47 | \setbox\hierleft=\hbox{\toDrop{\hierbox{\,#1\hfill}}}% 48 | \@ifnextchar+{\colInit}{% 49 | \@ifnextchar-{\vcolInit}{% 50 | \setbox\hierright=\hbox{}\hierDisplay}}} 51 | 52 | %--- 53 | 54 | \def\colInit+#1{\joinh=0pt\setbox\vcol=\vbox{#1}\colLoop} 55 | \def\colLoop{\@ifnextchar+{\colAdd}{\colDone}} 56 | \def\colAdd+#1{\setbox\vcol=\vbox{\unvbox\vcol\vskip\hiervgap}\joinh=\ht\vcol 57 | \setbox\vcol=\vbox{\unvbox\vcol #1}\colLoop} 58 | \def\colDone{% 59 | \setbox\hierright=\hbox{\vrule depth0pt width\hierhgap height\hierlinethickness 60 | \vrule height\hierlinethickness 61 | width\hierlinethickness 62 | depth\joinh 63 | \toDrop{\box\vcol}}\hierDisplay} 64 | \def\hierDisplay{\hbox{\unhbox\hierleft\unhbox\hierright}} 65 | 66 | %--- 67 | 68 | \def\vcolInit-#1{\joinh=0pt\setbox\vcol=\vbox{#1}\vcolLoop} 69 | \def\vcolLoop{\@ifnextchar+{\vcolAdd}{\vcolDone}} 70 | \def\vcolAdd+#1{\setbox\vcol=\vbox{\unvbox\vcol\vskip\hiervgap}\joinh=\ht\vcol 71 | \setbox\vcol=\vbox{\unvbox\vcol #1}\vcolLoop} 72 | \def\vcolDone{% 73 | \scrdim=\hiervdrop\advance\scrdim by\hiervgap 74 | \setbox\hierright=\hbox{\vrule height\scrdim 75 | width\hierlinethickness 76 | depth\joinh 77 | \toDrop{\box\vcol}}\vhierDisplay} 78 | \def\vhierDisplay{\hbox{\vbox{\box\hierleft\hrule width0mm depth0pt height0pt\hbox{\hskip3\hierhgap\unhbox\hierright}}}} 79 | 80 | %--- 81 | -------------------------------------------------------------------------------- /docs/jacc.bib: -------------------------------------------------------------------------------- 1 | @misc{bison, 2 | author = "Free Software Foundation", 3 | title = "Bison", 4 | note = "({\tt http://www.gnu.org/manual/bison-1.35/bison.html})" 5 | } 6 | 7 | @article{mule, 8 | title = "Very Fast {YACC}-Compatible Parsers (For Very Little Effort)", 9 | author = "Achyutram Bhamidipaty and Todd A. Proebsting", 10 | journal = "Software---Practice \& Experience", 11 | volume = "28", 12 | number = "2", 13 | month = "February", 14 | year = "1998", 15 | pages = "181--190" 16 | } 17 | 18 | @book{JLS, 19 | title = "The {Java} Language Specification, Second Edition", 20 | author = "James Gosling and Bill Joy and Guy Steele and Gilad Bracha", 21 | publisher = "Addison-Wesley", 22 | month = "June", 23 | year = "2000" 24 | } 25 | 26 | @misc{CUP, 27 | author = "Scott E. Hudson", 28 | title = "The {CUP} Parser Generator for {Java}", 29 | note = "({\tt http://www.cs.princeton.edu/\~{ }appel/modern/java/CUP/})" 30 | } 31 | 32 | @misc{Antlr, 33 | author = "Terrence Parr", 34 | title = "Antlr, Another Tool for Language Recognition", 35 | note = "({\tt http://www.antlr.org/})" 36 | } 37 | 38 | @misc{JavaCC, 39 | author = "Sun Microsystems", 40 | title = "{Java} Compiler Compiler ({JavaCC})---the {Java} Parser Generator", 41 | note = "({\tt http://www.webgain.com/products/java\_cc/})" 42 | } 43 | 44 | @misc{SableCC, 45 | author = "{The {Sable} Research Group}", 46 | title = "{SableCC}", 47 | note = "({\tt http://www.sablecc.org/})" 48 | } 49 | 50 | @misc{CocoR, 51 | author = "H. {M\"ossenb\"ock}", 52 | title = "{Coco/R} for {Java}", 53 | note = "({\tt http://www.ssw.uni-linz.ac.at/Research/Projects/Coco/Java/})" 54 | } 55 | 56 | @misc{ByaccJava, 57 | author = "Bob Jamison", 58 | title = "{BYACC/Java}", 59 | note = "({\tt http://troi.lincom-asg.com/\~{ }rjamison/byacc/})" 60 | } 61 | 62 | @misc{JikesPG, 63 | author = "David Shields and Philippe G. Charles", 64 | title = "The {Jikes} Parser Generator", 65 | note = "({\tt http://www-124.ibm.com/developerworks/projects/jikes/})" 66 | } 67 | 68 | @book{LevineMasonBrown:lex+yacc, 69 | title = "{lex \& yacc}, 2nd Edition", 70 | author = "John Levine and Tony Mason and Doug Brown", 71 | publisher = "O'Reilly", 72 | year = "1992" 73 | } 74 | 75 | @techreport{Johnson:yacc, 76 | author = "Johnson, S.C.", 77 | title = "Yacc---Yet Another Compiler Compiler", 78 | number = "Computer Science Technical Report Number 32", 79 | institution = "Bell Laboratories", 80 | month = "July", 81 | year = "1975" 82 | } 83 | 84 | @article{Jeffery:merr, 85 | author = {Clinton L. Jeffery}, 86 | title = "Generating {LR} syntax error messages from examples", 87 | journal = {ACM Trans. Program. Lang. Syst.}, 88 | volume = {25}, 89 | number = {5}, 90 | year = {2003}, 91 | issn = {0164-0925}, 92 | pages = {631--640}, 93 | doi = {http://doi.acm.org/10.1145/937563.937566}, 94 | publisher = {ACM Press}, 95 | } 96 | 97 | 98 | -------------------------------------------------------------------------------- /docs/jacc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zipwith/jacc/c553c059081d4fad51f04ac1a373799daf0c80bb/docs/jacc.pdf -------------------------------------------------------------------------------- /docs/jaccNutshell.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zipwith/jacc/c553c059081d4fad51f04ac1a373799daf0c80bb/docs/jaccNutshell.pdf -------------------------------------------------------------------------------- /docs/nutshell.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zipwith/jacc/c553c059081d4fad51f04ac1a373799daf0c80bb/docs/nutshell.ppt -------------------------------------------------------------------------------- /examples/Calc.errs: -------------------------------------------------------------------------------- 1 | // This file contains some simple examples of 2 | // errors diagnostics for Calc.jacc: 3 | 4 | "left operand is missing" 5 | : '+' expr 6 | | '-' expr 7 | | '*' expr 8 | | '/' expr 9 | ; 10 | 11 | "unexpected closing parenthesis" 12 | : expr ')' ; 13 | 14 | "unexpected opening parenthesis" 15 | : expr '(' ; 16 | 17 | "right operand is missing" 18 | : expr '+' 19 | | expr '+' ')' 20 | | expr '+' '+' 21 | | expr '-' 22 | | expr '-' ')' 23 | | expr '*' 24 | | expr '*' ')' 25 | | expr '/' 26 | | expr '/' ')' 27 | ; 28 | 29 | "unnecessary semicolon after last expression (or missing expression)" 30 | : prog ';' ; 31 | 32 | "empty parentheses" 33 | : '(' ')' ; 34 | 35 | "missing expression" 36 | : ';' ; 37 | 38 | -------------------------------------------------------------------------------- /examples/Calc.jacc: -------------------------------------------------------------------------------- 1 | // To compile and run this program using jacc and Sun's JDK: 2 | // 3 | // In a directory containing only the file Calc.jacc: 4 | // 5 | // jacc Calc.jacc 6 | // javac *.java 7 | // java CalcParser 8 | // ... enter arithmetic expressions ... hit EOF to terminate 9 | // 10 | 11 | 12 | %{ 13 | abstract class Expr { 14 | abstract int eval(); 15 | } 16 | 17 | class IntExpr extends Expr { 18 | private int value; 19 | IntExpr(int value) { this.value = value; } 20 | int eval() { return value; } 21 | } 22 | 23 | abstract class BinExpr extends Expr { 24 | protected Expr left, right; 25 | BinExpr(Expr left, Expr right) { 26 | this.left = left; this.right = right; 27 | } 28 | } 29 | 30 | class AddExpr extends BinExpr { 31 | AddExpr(Expr left, Expr right) { super(left, right); } 32 | int eval() { return left.eval() + right.eval(); } 33 | } 34 | 35 | class SubExpr extends BinExpr { 36 | SubExpr(Expr left, Expr right) { super(left, right); } 37 | int eval() { return left.eval() - right.eval(); } 38 | } 39 | 40 | class MulExpr extends BinExpr { 41 | MulExpr(Expr left, Expr right) { super(left, right); } 42 | int eval() { return left.eval() * right.eval(); } 43 | } 44 | 45 | class DivExpr extends BinExpr { 46 | DivExpr(Expr left, Expr right) { super(left, right); } 47 | int eval() { return left.eval() / right.eval(); } 48 | } 49 | 50 | class CalcLexer implements CalcTokens { 51 | private int c = ' '; 52 | 53 | /** Read a single input character from standard input. 54 | */ 55 | private void nextChar() { 56 | if (c>=0) { 57 | try { 58 | c = System.in.read(); 59 | } catch (Exception e) { 60 | c = (-1); 61 | } 62 | } 63 | } 64 | 65 | private int token; 66 | private IntExpr yylval; 67 | 68 | /** Read the next token and return the 69 | * corresponding integer code. 70 | */ 71 | int nextToken() { 72 | for (;;) { 73 | // Skip whitespace 74 | while (c==' ' || c=='\n' || c=='\t' || c=='\r') { 75 | nextChar(); 76 | } 77 | if (c<0) { 78 | return (token=ENDINPUT); 79 | } 80 | switch (c) { 81 | case '+' : nextChar(); 82 | return token='+'; 83 | case '-' : nextChar(); 84 | return token='-'; 85 | case '*' : nextChar(); 86 | return token='*'; 87 | case '/' : nextChar(); 88 | return token='/'; 89 | case '(' : nextChar(); 90 | return token='('; 91 | case ')' : nextChar(); 92 | return token=')'; 93 | case ';' : nextChar(); 94 | return token=';'; 95 | default : if (Character.isDigit((char)c)) { 96 | int n = 0; 97 | do { 98 | n = 10*n + (c - '0'); 99 | nextChar(); 100 | } while (Character.isDigit((char)c)); 101 | yylval = new IntExpr(n); 102 | return token=INTEGER; 103 | } else { 104 | Main.error("Illegal character "+c); 105 | nextChar(); 106 | } 107 | } 108 | } 109 | } 110 | 111 | /** Return the token code for the current lexeme. 112 | */ 113 | int getToken() { 114 | return token; 115 | } 116 | 117 | /** Return the semantic value for the current lexeme. 118 | */ 119 | IntExpr getSemantic() { 120 | return yylval; 121 | } 122 | } 123 | 124 | class Main { 125 | public static void main(String[] args) { 126 | CalcLexer lexer = new CalcLexer(); 127 | lexer.nextToken(); 128 | CalcParser parser = new CalcParser(lexer); 129 | parser.parse(); 130 | } 131 | 132 | static void error(String msg) { 133 | System.out.println("ERROR: " + msg); 134 | System.exit(1); 135 | } 136 | } 137 | %} 138 | 139 | %semantic Expr 140 | %token '+' '-' '*' '/' '(' ')' ';' INTEGER 141 | %left '+' '-' 142 | %left '*' '/' 143 | 144 | %% 145 | 146 | prog : prog ';' expr { System.out.println($3.eval()); } 147 | | expr { System.out.println($1.eval()); } 148 | ; 149 | expr : expr '+' expr { $$ = new AddExpr($1, $3); } 150 | | expr '-' expr { $$ = new SubExpr($1, $3); } 151 | | expr '*' expr { $$ = new MulExpr($1, $3); } 152 | | expr '/' expr { $$ = new DivExpr($1, $3); } 153 | | '(' expr ')' { $$ = $2; } 154 | | INTEGER { $$ = $1; } 155 | ; 156 | 157 | %% 158 | private CalcLexer lexer; 159 | 160 | CalcParser(CalcLexer lexer) { this.lexer = lexer; } 161 | 162 | private void yyerror(String msg) { 163 | Main.error(yyerrno<0 ? msg : yyerrmsgs[yyerrno]); 164 | } 165 | 166 | -------------------------------------------------------------------------------- /examples/MoreCalc.errs: -------------------------------------------------------------------------------- 1 | "Unexpected opening parenthesis" 2 | : ')' 3 | ; 4 | 5 | "Missing operand" 6 | : expr INTEGER 7 | ; 8 | -------------------------------------------------------------------------------- /examples/Unary.jacc: -------------------------------------------------------------------------------- 1 | // This file contains an extension to the calculator program in Calc.jacc 2 | // that adds support for unary minus. It is intended as a simple example 3 | // to illustrate jacc's ability to take input from multiple files. 4 | // 5 | // To compile and run this program using jacc and Sun's JDK: 6 | // 7 | // In a directory containing only the files Calc.jacc and Unary.jacc: 8 | // 9 | // jacc Calc.jacc Unary.jacc 10 | // javac *.java 11 | // java Calc 12 | // ... enter arithmetic expressions ... hit EOF to terminate 13 | // 14 | 15 | 16 | %{ 17 | class UminusExpr extends Expr { 18 | private Expr expr; 19 | UminusExpr(Expr expr) { this.expr = expr; } 20 | int eval() { return -expr.eval(); } 21 | } 22 | %} 23 | 24 | %left UMINUS 25 | 26 | %% 27 | 28 | expr : '-' expr %prec UMINUS { $$ = new UminusExpr($2); } 29 | ; 30 | -------------------------------------------------------------------------------- /examples/dang.ex: -------------------------------------------------------------------------------- 1 | 2 | IF expr THEN 3 | IF expr THEN 4 | stmt 5 | ELSE 6 | stmt 7 | 8 | -------------------------------------------------------------------------------- /examples/dang.jacc: -------------------------------------------------------------------------------- 1 | %token IF THEN ELSE expr other 2 | %% 3 | stmt : IF expr THEN stmt ELSE stmt 4 | | IF expr THEN stmt 5 | | other 6 | ; 7 | 8 | -------------------------------------------------------------------------------- /examples/example1: -------------------------------------------------------------------------------- 1 | 2 | INTEGER '+' INTEGER '*' INTEGER ';' 3 | INTEGER '*' INTEGER '+' INTEGER 4 | 5 | -------------------------------------------------------------------------------- /examples/example2: -------------------------------------------------------------------------------- 1 | 2 | expr '+' expr '*' expr ';' 3 | expr '*' expr '+' expr 4 | 5 | -------------------------------------------------------------------------------- /examples/simpleCalc.jacc: -------------------------------------------------------------------------------- 1 | // To compile and run this program using jacc and Sun's JDK: 2 | // 3 | // jacc simpleCalc.jacc 4 | // javac Calc.java CalcTokens.java 5 | // java Calc 6 | // ... enter arithmetic expressions ... hit EOF to terminate 7 | // 8 | 9 | %class Calc 10 | %interface CalcTokens 11 | %semantic int : yylval 12 | %get token 13 | %next yylex() 14 | 15 | %token '+' '-' '*' '/' '(' ')' ';' INTEGER 16 | %left '+' '-' 17 | %left '*' '/' 18 | 19 | %% 20 | 21 | prog : prog ';' expr { System.out.println($3); } 22 | | expr { System.out.println($1); } 23 | ; 24 | expr : expr '+' expr { $$ = $1 + $3; } 25 | | expr '-' expr { $$ = $1 - $3; } 26 | | expr '*' expr { $$ = $1 * $3; } 27 | | expr '/' expr { $$ = $1 / $3; } 28 | | '(' expr ')' { $$ = $2; } 29 | | INTEGER { $$ = $1; } 30 | ; 31 | 32 | %% 33 | 34 | private void yyerror(String msg) { 35 | System.out.println("ERROR: " + msg); 36 | System.exit(1); 37 | } 38 | 39 | private int c; 40 | 41 | /** Read a single input character from standard input. 42 | */ 43 | private void nextChar() { 44 | if (c>=0) { 45 | try { 46 | c = System.in.read(); 47 | } catch (Exception e) { 48 | c = (-1); 49 | } 50 | } 51 | } 52 | 53 | int token; 54 | int yylval; 55 | 56 | /** Read the next token and return the 57 | * corresponding integer code. 58 | */ 59 | int yylex() { 60 | for (;;) { 61 | // Skip whitespace 62 | while (c==' ' || c=='\n' || c=='\t' || c=='\r') { 63 | nextChar(); 64 | } 65 | if (c<0) { 66 | return (token=ENDINPUT); 67 | } 68 | switch (c) { 69 | case '+' : nextChar(); 70 | return token='+'; 71 | case '-' : nextChar(); 72 | return token='-'; 73 | case '*' : nextChar(); 74 | return token='*'; 75 | case '/' : nextChar(); 76 | return token='/'; 77 | case '(' : nextChar(); 78 | return token='('; 79 | case ')' : nextChar(); 80 | return token=')'; 81 | case ';' : nextChar(); 82 | return token=';'; 83 | default : if (Character.isDigit((char)c)) { 84 | int n = 0; 85 | do { 86 | n = 10*n + (c - '0'); 87 | nextChar(); 88 | } while (Character.isDigit((char)c)); 89 | yylval = n; 90 | return token=INTEGER; 91 | } else { 92 | yyerror("Illegal character "+c); 93 | nextChar(); 94 | } 95 | } 96 | } 97 | } 98 | 99 | public static void main(String[] args) { 100 | Calc calc = new Calc(); 101 | calc.nextChar(); // prime the character input stream 102 | calc.yylex(); // prime the token input stream 103 | calc.parse(); // parse the input 104 | } 105 | -------------------------------------------------------------------------------- /images/jacc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zipwith/jacc/c553c059081d4fad51f04ac1a373799daf0c80bb/images/jacc.gif -------------------------------------------------------------------------------- /scripts/jacc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | java -jar $JACC_PATH/jacc.jar $* 3 | -------------------------------------------------------------------------------- /scripts/jacc.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | java -jar %JACC_PATH%\jacc.jar %* 3 | -------------------------------------------------------------------------------- /src/compiler/Diagnostic.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package compiler; 7 | 8 | /** A base class for objects that represent compiler diagnostics. 9 | * Errors should be implemented as subclasses of Failure, while 10 | * warnings are normally implemented as as subclasses of Warning. 11 | */ 12 | public abstract class Diagnostic extends Exception { 13 | /** Used to hold a simple description of the problem that 14 | * occurred. This field is used only by the default 15 | * implementation of getDescription() that is provided in 16 | * this class. More complex diagnostics are likely to 17 | * override this method, and hence will not use this field. 18 | * 19 | * The format and interpretation of the description field 20 | * have not yet been determined. It would, however, make 21 | * sense to allow the use of some kind of XML/HTML tags to 22 | * allow embedding of structure/formatting hints. 23 | */ 24 | private String text; 25 | public String getText() { 26 | return text; 27 | } 28 | 29 | /** A pointer to the place where the error was detected. 30 | * A null value can be used for diagnostics that are not 31 | * associated with any particular point in the source. 32 | */ 33 | private Position position; 34 | public Position getPos() { 35 | return position; 36 | } 37 | 38 | /** Return a cross reference string for this diagnostic. The 39 | * format and interpretation of this string has not yet 40 | * determined, but might, for example, be used to construct 41 | * a URL or file name that contains additional information 42 | * about problems of this kind. 43 | */ 44 | protected String crossRef; 45 | public String getCrossRef() { 46 | return null; 47 | } 48 | 49 | /** Construct a simple diagnostic with a fixed description. 50 | */ 51 | public Diagnostic(String text) { 52 | this.text = text; 53 | } 54 | 55 | /** Construct a diagnostic object for a particular source position. 56 | */ 57 | public Diagnostic(Position position) { 58 | this.position = position; 59 | } 60 | 61 | /** Construct a simple diagnostic with a fixed description 62 | * and a source position. 63 | */ 64 | public Diagnostic(Position position, String text) { 65 | this.position = position; 66 | this.text = text; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/compiler/Failure.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package compiler; 7 | 8 | /** Represents an error diagnostic. To avoid a clash with java.lang.Error, 9 | * we resisted the temptation to call this class Error. 10 | */ 11 | public class Failure extends Diagnostic { 12 | /** Construct a simple failure report with a fixed description. 13 | */ 14 | public Failure(String text) { 15 | super(text); 16 | } 17 | 18 | /** Construct a failure report for a particular source position. 19 | */ 20 | public Failure(Position position) { 21 | super(position); 22 | } 23 | 24 | /** Construct a simple failure report with a fixed description 25 | * and a source position. 26 | */ 27 | public Failure(Position position, String text) { 28 | super(position, text); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/compiler/Handler.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package compiler; 7 | 8 | /** Represents a handler for diagnostics. In particular applications, 9 | * we can use subclasses to specify how diagnostics should be handled. 10 | */ 11 | public abstract class Handler { 12 | /** Count how many diagnostics have been reported. 13 | */ 14 | private int numDiagnostics = 0; 15 | public int getNumDiagnostics() { 16 | return numDiagnostics; 17 | } 18 | 19 | /** Count how many failures have been reported. 20 | */ 21 | private int numFailures = 0; 22 | public int getNumFailures() { 23 | return numFailures; 24 | } 25 | 26 | /** Report a problem to this diagnostic handler. 27 | */ 28 | public void report(Diagnostic d) { 29 | numDiagnostics++; 30 | if (d instanceof Failure) { 31 | numFailures++; 32 | } 33 | respondTo(d); 34 | } 35 | 36 | /** Respond to a diagnostic report. Subclasses should 37 | * override this method to deal with diagnostic reports in an 38 | * appropriate way. Diagnostics will normally be passed to this 39 | * method indirectly via a call to report() in the client code. 40 | */ 41 | protected abstract void respondTo(Diagnostic d); 42 | 43 | /** Reset the diagnostic handler. This should set the diagnostic 44 | * handler back to the state of a freshly created handler. As a 45 | * default, we just reset the counters. 46 | */ 47 | public void reset() { 48 | numDiagnostics = 0; 49 | numFailures = 0; 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/compiler/JavaSource.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package compiler; 7 | 8 | import java.io.Reader; 9 | import java.io.IOException; 10 | 11 | /** An implementation of the Source interface that follows the low 12 | * level lexical conventions of Java for Unicode escapes, etc.. 13 | * Uses a Reader to obtain its input. Also expands tabs. 14 | */ 15 | public class JavaSource extends Source { 16 | private Reader input; 17 | private int tabwidth; 18 | private String description; 19 | 20 | private final static int DEFAULT_TABWIDTH = 8; 21 | 22 | public JavaSource(Handler handler, 23 | String description, 24 | Reader input, 25 | int tabwidth) { 26 | super(handler); 27 | this.description = description; 28 | this.input = input; 29 | this.tabwidth = tabwidth; 30 | } 31 | 32 | public JavaSource(Handler handler, 33 | String description, 34 | Reader input) { 35 | this(handler, description, input, DEFAULT_TABWIDTH); 36 | } 37 | 38 | public JavaSource(String description, Reader input) { 39 | this(null, description, input); 40 | } 41 | 42 | /** Return a description of this source. 43 | */ 44 | public String describe() { 45 | return description; 46 | } 47 | 48 | // --------------------------------------------------------------------- 49 | // Character level input: eliminate trailing ^Z at EOF 50 | 51 | private int c0; // First input character 52 | private int c1 = 0; // Lookahead 53 | private int lineNumber = 0; // Line number 54 | 55 | private void skip() throws IOException { 56 | c0 = c1; 57 | if (c0!=(-1)) { 58 | c1 = input.read(); 59 | if (c0==26 && c1==(-1)) { 60 | c0 = c1; 61 | } 62 | } 63 | } 64 | 65 | // --------------------------------------------------------------------- 66 | // Line level input: deal with unicode escapes and separate lines 67 | 68 | private StringBuffer buf; // Buffer for input lines 69 | 70 | /** Read the next line from the input stream. 71 | * 72 | * @return The next line, or null at the end of the input stream. 73 | */ 74 | public String readLine() { 75 | if (input==null) { // Return null when done 76 | return null; 77 | } 78 | 79 | if (buf==null) { // Allocate or clear buffer 80 | buf = new StringBuffer(); 81 | } else { 82 | buf.setLength(0); 83 | } 84 | 85 | try { 86 | if (lineNumber++ == 0) { // Set lookahead character 87 | skip(); // for first input line. 88 | skip(); 89 | } 90 | if (c0==(-1)) { // File ends at the beginning 91 | // TODO: is it really safe to close here (or to omit the 92 | // close altogether)? The issue is that we don't want the 93 | // lines of text for this source to be thrown away prematurely. 94 | //close(); 95 | return null; // of a line? 96 | } 97 | 98 | while (c0!=(-1) && c0!='\n' && c0!='\r') { 99 | if (c0=='\\') { 100 | skip(); 101 | if (c0=='u') { // Unicode escapes 102 | do { 103 | skip(); 104 | } while (c0=='u'); 105 | int n = 0; 106 | int i = 0; 107 | int d = 0; 108 | while (i<4 && c0!=(-1) && 109 | (d=Character.digit((char)c0,16))>=0) { 110 | n = (n<<4) + d; 111 | i++; 112 | skip(); 113 | } 114 | if (i!=4) { 115 | report(new Warning( 116 | "Error in Unicode escape sequence")); 117 | } else { 118 | buf.append((char)n); 119 | } 120 | } else { 121 | buf.append('\\'); 122 | if (c0==(-1)) { 123 | break; 124 | } else { 125 | buf.append((char)c0); 126 | } 127 | skip(); 128 | } 129 | } else if (c0=='\t' && tabwidth>0) { // Expand tabs 130 | int n = tabwidth - (buf.length() % tabwidth); 131 | for (; n>0; n--) { 132 | buf.append(' '); 133 | } 134 | skip(); 135 | } else { 136 | buf.append((char)c0); 137 | skip(); 138 | } 139 | } 140 | if (c0=='\r') { // Skip CR, LF, CRLF 141 | skip(); 142 | } 143 | if (c0 =='\n') { 144 | skip(); 145 | } 146 | } catch (IOException e) { 147 | close(); 148 | } 149 | 150 | return buf.toString(); 151 | } 152 | 153 | /** Return the current line number. 154 | */ 155 | public int getLineNo() { 156 | return lineNumber; 157 | } 158 | 159 | /** Close the input stream and any associated resources. The default 160 | * for this method is to do nothing. 161 | */ 162 | public void close() { 163 | if (input!=null) { 164 | try { 165 | input.close(); 166 | } 167 | catch (IOException e) { 168 | // Should I complain? 169 | } 170 | input = null; 171 | buf = null; 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/compiler/Lexer.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package compiler; 7 | 8 | /** A framework for building lexical analyzers. 9 | * 10 | * Conventions: when a token has been recognized, save the integer 11 | * code for it in the variable `token' and any corresponding text, 12 | * if required, in the variable `textToken'. 13 | */ 14 | public abstract class Lexer extends Phase { 15 | protected int token; 16 | protected String lexemeText; 17 | 18 | /** Construct a lexical analysis phase with a specified diagnostic 19 | * handler. 20 | */ 21 | public Lexer(Handler handler) { 22 | super(handler); 23 | } 24 | 25 | /** Construct a lexical analysis phase, and specify which token 26 | * should be returned first. This feature is useful in programs 27 | * where an initial token is used to allow the same parser to be 28 | * used, in effect, with multiple start symbols. By loading the 29 | * firstToken with particular values, we can force the parser to 30 | * use different productions for the start symbol. 31 | * 32 | * For example, in a program that needs to parse exprs, stmts, 33 | * or files, all using elements from the same grammar, then we 34 | * might set up a start symbol with the following productions: 35 | *
 36 |      *    start : PARSE_EXPR expr
 37 |      *          | PARSE_STMT stmt
 38 |      *          | PARSE_FILE file
 39 |      *          ;
 40 |      *  
41 | * To parse an expression, statement, or file, we just make sure 42 | * that our lexer is constructed using the appropriate firstToken: 43 | *
 44 |      *    new Lexer(handler, PARSE_EXPR);
 45 |      *    new Lexer(handler, PARSE_STMT);
 46 |      *    new Lexer(handler, PARSE_FILE);
 47 |      *  
48 | * We do not specify either a text or a position for this first 49 | * token, so a program using a Lexer should not attempt to read 50 | * or use those attributes of the first token. 51 | * 52 | * In an application where this feature is not required, the program 53 | * should just use: 54 | *
 55 |      *    new Lexer(handler);
 56 |      *  
57 | * and then invoke nextToken() to read the first input 58 | * token. 59 | */ 60 | public Lexer(Handler handler, int firstToken) { 61 | super(handler); 62 | token = firstToken; 63 | } 64 | 65 | /** Read the next token and return the corresponding integer code. 66 | * At the same time, the function should set lexemeText to the text 67 | * of the lexeme (if relevant), and should be prepared to respond 68 | * to a getPos request with the position at which the token began. 69 | * This function should not normally be called again after an end 70 | * of input token has been returned. 71 | */ 72 | public abstract int nextToken(); 73 | 74 | /** Returns the code for the current token. 75 | */ 76 | public int getToken() { 77 | return token; 78 | } 79 | 80 | /** Returns the text (if any) for the current lexeme. 81 | */ 82 | public String getLexeme() { 83 | return lexemeText; 84 | } 85 | 86 | /** Return a position describing where the current token was 87 | * found. This method is abstract because different types 88 | * of lexer will need to return different types of source 89 | * position depending on how they get their input. 90 | */ 91 | public abstract Position getPos(); 92 | 93 | /** Test for a particular next token code. 94 | */ 95 | public boolean match(int token) { 96 | if (token==this.token) { 97 | nextToken(); 98 | return true; 99 | } 100 | return false; 101 | } 102 | 103 | /** Close this lexer. Calling this method signals that this 104 | * lexer will not be used again, and allows the implementation 105 | * to free up any resources that it might be holding such as 106 | * files, input streams, etc. 107 | */ 108 | public abstract void close(); 109 | } 110 | -------------------------------------------------------------------------------- /src/compiler/Phase.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package compiler; 7 | 8 | /** Base class for compiler phases. Its only real purpose is to provide 9 | * convenient access to a diagnostic handler. 10 | */ 11 | public abstract class Phase { 12 | private Handler handler; 13 | 14 | /** Construct a new phase with a specified diagnostic handler. 15 | */ 16 | protected Phase(Handler handler) { 17 | this.handler = handler; 18 | } 19 | 20 | /** Return the handler for this phase. 21 | */ 22 | public Handler getHandler() { 23 | return handler; 24 | } 25 | 26 | /** Report a diagnostic detected in this phase. 27 | */ 28 | public void report(Diagnostic d) { 29 | if (handler!=null) { 30 | handler.report(d); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/compiler/Position.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package compiler; 7 | 8 | /** An abstraction of a position within a source document. 9 | * Source code may come from many different sources: a text file, 10 | * a compressed file, a web page, a string, a database, a data structure 11 | * in memory, a console input stream, a network resource, etc. In each 12 | * case we need to be able to give a name to source code positions so 13 | * that we can tell users where certain values are defined, or where 14 | * errors have been detected. 15 | * 16 | * [We might also like to have a mechanism for starting an editor at a 17 | * particular position so that users can view and potentially change the 18 | * corresponding sections of source code.] 19 | */ 20 | public abstract class Position { 21 | /** Obtain a printable description of the source position. 22 | */ 23 | public abstract String describe(); 24 | 25 | /** Return a column number for this position. By convention, 26 | * column numbers start at zero. If no sensible column number 27 | * value is available, then we return zero. Calling methods 28 | * will not be able to distinguish between uses of zero as a 29 | * genuine column number, and uses of zero as a "no column number 30 | * available" indicator. 31 | */ 32 | public int getColumn() { 33 | return 0; 34 | } 35 | 36 | /** Return a row number for this position. By convention, 37 | * row numbers start at zero. If no sensible row number 38 | * value is available, then we return zero. Calling methods 39 | * will not be able to distinguish between uses of zero as a 40 | * genuine row number, and uses of zero as a "no row number 41 | * available" indicator. 42 | */ 43 | public int getRow() { 44 | return 0; 45 | } 46 | 47 | /** Copy a source position. 48 | */ 49 | public abstract Position copy(); 50 | } 51 | -------------------------------------------------------------------------------- /src/compiler/SimpleHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package compiler; 7 | 8 | /** A simple implementation of the Handler interface that prints the 9 | * position and description of each diagnostic on System.err, and 10 | * then returns to the caller. 11 | */ 12 | public class SimpleHandler extends Handler { 13 | /** Respond to a diagnostic by displaying it on the error output 14 | * stream. 15 | */ 16 | protected void respondTo(Diagnostic d) { 17 | Position pos = d.getPos(); 18 | if (d instanceof Warning) { 19 | System.err.print("WARNING: "); 20 | } else { 21 | System.err.print("ERROR: "); 22 | } 23 | if (pos!=null) { 24 | System.err.println(pos.describe()); 25 | } 26 | String txt = d.getText(); 27 | if (txt!=null) { 28 | System.err.println(txt); 29 | } 30 | System.err.println(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/compiler/Source.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package compiler; 7 | 8 | /** A base class for line-oriented source input. 9 | */ 10 | public abstract class Source extends Phase { 11 | /** Construct a new source input phase with a specified handler. 12 | */ 13 | public Source(Handler handler) { 14 | super(handler); 15 | } 16 | 17 | /** Return a printable description of the source. 18 | */ 19 | public abstract String describe(); 20 | 21 | /** Read the next line from the input stream. 22 | * 23 | * @return The next line, or null at the end of the input stream. 24 | */ 25 | public abstract String readLine(); 26 | 27 | /** Return the current line number. By convention, we use 0 to 28 | * indicate the absence of a line number; counting for regular 29 | * lines begins at 1. 30 | */ 31 | public abstract int getLineNo(); 32 | 33 | /** Return the text of a specific line, if it is available. If the 34 | * requested source line is not available, then a null is returned. 35 | * This mechanism is useful with sources that cache either the text 36 | * of each line that has been read, or perhaps just the offsets of 37 | * each line within a given file. A console input source may use a 38 | * queue like structure to provide history for the most recently 39 | * entered line. A source that uses an array of Strings as its input, 40 | * or perhaps accesses the text from a text editor's buffer in an IDE, 41 | * might be able to offer up lines through this method that have not 42 | * yet been read using readLine(). On the other hand, some sources 43 | * might choose to return null for every call to getLine(), which is 44 | * the default behavior specified below, or to flush any caches of 45 | * line data after the source has been closed. 46 | */ 47 | public String getLine(int lineNo) { 48 | return null; 49 | } 50 | 51 | /** Close the input stream and any associated resources. The default 52 | * for this method is to do nothing. 53 | */ 54 | public void close() { 55 | // Do nothing! 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/compiler/SourceLexer.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package compiler; 7 | 8 | /** A base class for building lexical analyzers that use a Source 9 | * object as input. 10 | */ 11 | public abstract class SourceLexer extends Lexer { 12 | /** The Source object for this lexical analyzer. 13 | */ 14 | protected Source source; 15 | 16 | /** Holds the text of the current line. 17 | */ 18 | protected String line; 19 | 20 | /** Holds the position in the current line; a zero index 21 | * indicates the first character in the line, while an 22 | * index of line.length() indicates a "virtual EOL" at 23 | * the end of the line. 24 | */ 25 | protected int col = (-1); 26 | 27 | private SourcePosition pos; 28 | 29 | protected final static int EOF = -1; 30 | protected final static int EOL = '\n'; 31 | protected int c; 32 | 33 | public SourceLexer(Handler handler, Source source) { 34 | super(handler); 35 | this.source = source; 36 | this.pos = new SourcePosition(source); 37 | this.line = source.readLine(); 38 | nextChar(); 39 | } 40 | 41 | public Position getPos() { 42 | return pos.copy(); 43 | } 44 | 45 | protected void markPosition() { 46 | pos.updateCoords(source.getLineNo(), col); 47 | } 48 | 49 | protected void nextLine() { 50 | line = source.readLine(); 51 | col = (-1); 52 | nextChar(); 53 | } 54 | 55 | protected int nextChar() { 56 | if (line==null) { 57 | c = EOF; 58 | col = 0; // EOF is always at column 0 59 | } else if (++col>=line.length()) { 60 | c = EOL; 61 | } else { 62 | c = line.charAt(col); 63 | } 64 | return c; 65 | } 66 | 67 | public void close() { 68 | if (source != null) { 69 | source.close(); 70 | source = null; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/compiler/SourcePosition.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package compiler; 7 | 8 | /** A position within a source Source object. 9 | * 10 | * [We might also like to have a mechanism for starting an editor at a 11 | * particular position so that users can view and potentially change the 12 | * corresponding sections of source code.] 13 | */ 14 | public class SourcePosition extends Position { 15 | private Source source; 16 | private int row; 17 | private int column; 18 | 19 | public SourcePosition(Source source, int row, int column) { 20 | this.source = source; 21 | this.row = row; 22 | this.column = column; 23 | } 24 | 25 | public SourcePosition(Source source) { 26 | this(source, 0, 0); 27 | } 28 | 29 | /** Return the source for this position. 30 | */ 31 | public Source getSource() { 32 | return source; 33 | } 34 | 35 | /** Return the row number for this position. 36 | */ 37 | public int getRow() { 38 | return row; 39 | } 40 | 41 | /** Return the column number for this position. 42 | */ 43 | public int getColumn() { 44 | return column; 45 | } 46 | 47 | /** Update the coordinates of this position. 48 | */ 49 | public void updateCoords(int row, int column) { 50 | this.row = row; 51 | this.column = column; 52 | } 53 | 54 | /** Obtain a printable description of the source position. 55 | */ 56 | public String describe() { 57 | StringBuffer buf = new StringBuffer(); 58 | if (source!=null) { 59 | buf.append('"'); 60 | buf.append(source.describe()); 61 | buf.append('"'); 62 | if (row>0) { 63 | buf.append(", "); 64 | } 65 | } 66 | if (row>0) { 67 | buf.append("line "); 68 | buf.append(row); 69 | } 70 | String line = source.getLine(row); 71 | if (line!=null) { 72 | buf.append('\n'); 73 | buf.append(line); 74 | buf.append('\n'); 75 | for (int i=0; i=args.length) { 72 | usage("Missing filename for -e option"); 73 | } 74 | errFiles = new NameList(args[++i], errFiles); 75 | break; 76 | case 'r': 77 | if (i+1>=args.length) { 78 | usage("Missing filename for -r option"); 79 | } 80 | runFiles = new NameList(args[++i], runFiles); 81 | break; 82 | case 'n': 83 | wantStates = true; 84 | break; 85 | default: 86 | usage("Unrecognized command line option "+ 87 | arg.charAt(j)); 88 | } 89 | } 90 | } else if (!arg.endsWith(suffix)) { 91 | usage("Input file must have \"" + suffix + "\" suffix"); 92 | } else { 93 | inputs = new NameList(arg, inputs); 94 | } 95 | } 96 | 97 | if (inputs==null) { 98 | usage("No input file(s) specified"); 99 | } 100 | 101 | Handler handler = new SimpleHandler(); 102 | String firstName = inputs.getFirst(); 103 | int n = 1 + Math.max(firstName.lastIndexOf('\\'), 104 | firstName.lastIndexOf('/')); 105 | String prefix = firstName.substring(0,n); 106 | String name = firstName 107 | .substring(n, firstName.length()-suffix.length()); 108 | final JaccJob job = new JaccJob(handler, out, settings); 109 | NameList.visit(inputs, new NameList.Visitor() { 110 | void visit(String name) { job.parseGrammarFile(name); } 111 | }); 112 | job.buildTables(); 113 | settings.fillBlanks(name); 114 | NameList.visit(errFiles, new NameList.Visitor() { 115 | void visit(String name) { job.readErrorExamples(name); } 116 | }); 117 | 118 | if (handler.getNumFailures()>0) { 119 | return; 120 | } 121 | if (wantParser) { 122 | new ParserOutput(handler, job) 123 | .write(prefix + settings.getClassName() + ".java"); 124 | } 125 | if (wantTokens) { 126 | new TokensOutput(handler, job) 127 | .write(prefix + settings.getInterfaceName() + ".java"); 128 | } 129 | if (wantText) { 130 | new TextOutput(handler, job, wantFirst) 131 | .write(prefix + name + ".output"); 132 | } 133 | if (wantHTML) { 134 | new HTMLOutput(handler, job, wantFirst) 135 | .write(prefix + name + "Machine.html"); 136 | } 137 | if (wantDot) { 138 | new DotOutput(handler, job) 139 | .write(prefix + name + ".dot"); 140 | } 141 | final boolean showState = wantStates; 142 | NameList.visit(runFiles, new NameList.Visitor() { 143 | void visit(String name) { job.readRunExample(name, showState); } 144 | }); 145 | } 146 | 147 | /** Display message describing the format of command line arguments. 148 | */ 149 | private static void usage(String msg) { 150 | System.err.println(msg); 151 | System.err.println("usage: jacc [options] file.jacc ..."); 152 | 153 | System.err.println("options (individually, or in combination):"); 154 | System.err.println(" -p do not generate parser"); 155 | System.err.println(" -t do not generate token specification"); 156 | System.err.println(" -v output text description of machine"); 157 | System.err.println(" -h output HTML description of machine"); 158 | System.err.println(" -d output dot description of machine"); 159 | System.err.println(" -f show first/follow sets (with -h or -v)"); 160 | System.err.println(" -a treat as LALR(1) grammar (default)"); 161 | System.err.println(" -s treat as SLR(1) grammar"); 162 | System.err.println(" -0 treat as LR(0) grammar"); 163 | System.err.println(" -r file run parser on input in file"); 164 | System.err.println(" -n show state numbers in parser output"); 165 | System.err.println(" -e file read error cases from file"); 166 | System.exit(1); 167 | } 168 | 169 | /** A simple linked list data structure for holding lists of 170 | * file names. The associated visitor will visit the names 171 | * in the order they were added from first to last. 172 | */ 173 | private static class NameList { 174 | public String name; 175 | public NameList names; 176 | public NameList(String name, NameList names) { 177 | this.name = name; 178 | this.names = names; 179 | } 180 | 181 | public abstract static class Visitor { 182 | abstract void visit(String name); 183 | } 184 | 185 | public static void visit(NameList ns, Visitor visitor) { 186 | if (ns!=null) { 187 | visit(ns.names, visitor); 188 | visitor.visit(ns.name); 189 | } 190 | } 191 | 192 | public String getFirst() { 193 | NameList ns = this; 194 | while (ns.names!=null) { 195 | ns = ns.names; 196 | } 197 | return ns.name; 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/jacc/Conflicts.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package jacc; 7 | 8 | import jacc.grammar.Grammar; 9 | import jacc.grammar.Machine; 10 | 11 | /** A linked list structure for storing conflicts. 12 | */ 13 | public class Conflicts { 14 | /** Represents a shift/reduce error. 15 | */ 16 | private final static int SR = 0; 17 | 18 | /** Represents a reduce/reduce error. 19 | */ 20 | private final static int RR = 1; 21 | 22 | /** The type of this conflict, either SR or RR. 23 | */ 24 | private int type; 25 | 26 | /** First argument for conflict. If this is a SR conflict, 27 | * then this value holds the state number of the shift, 28 | * If this is an RR conflict, then this value holds the 29 | * offset of the first reduction. 30 | */ 31 | private int arg1; 32 | 33 | /** Second argument for conflict, holds the offset of another 34 | * reduction. 35 | */ 36 | private int arg2; 37 | 38 | /** Token symbol on which this conflict occurs. 39 | */ 40 | private Grammar.Symbol sym; 41 | 42 | /** Link to rest of the conflicts in this list. 43 | */ 44 | private Conflicts next; 45 | 46 | /** Construct a new conflict item. 47 | */ 48 | private Conflicts(int type, int arg1, int arg2, 49 | Grammar.Symbol sym, Conflicts next) { 50 | this.type = type; 51 | this.arg1 = arg1; 52 | this.arg2 = arg2; 53 | this.sym = sym; 54 | this.next = next; 55 | } 56 | 57 | /** Construct a shift/reduce conflict. 58 | */ 59 | public static Conflicts sr(int arg1, int arg2, Grammar.Symbol sym, Conflicts cs) { 60 | return append(cs, new Conflicts(SR, arg1, arg2, sym, null)); 61 | } 62 | 63 | /** Construct a reduce/reduce conflict. 64 | */ 65 | public static Conflicts rr(int arg1, int arg2, Grammar.Symbol sym, Conflicts cs) { 66 | return append(cs, new Conflicts(RR, arg1, arg2, sym, null)); 67 | } 68 | 69 | /** Destructive append on two lists of conflicts. 70 | */ 71 | private static Conflicts append(Conflicts cs, Conflicts cs1) { 72 | if (cs==null) { 73 | return cs1; 74 | } else { 75 | Conflicts prev = cs; 76 | while (prev.next!=null) { 77 | prev = prev.next; 78 | } 79 | prev.next = cs1; 80 | return cs; 81 | } 82 | } 83 | 84 | /** Produce a string that describes all the conflicts in a list. 85 | */ 86 | public static String describe(Machine machine, int st, Conflicts cs) { 87 | if (cs==null) { 88 | return ""; 89 | } else { 90 | StringBuffer buf = new StringBuffer(); 91 | String nl = System.getProperty("line.separator","\n"); 92 | for (; cs!=null; cs=cs.next) { 93 | buf.append(st); 94 | buf.append(": "); 95 | if (cs.type==SR) { 96 | buf.append("shift/reduce conflict ("); 97 | if (cs.arg1<0) { 98 | buf.append("$end"); 99 | } else { 100 | buf.append("shift "); 101 | buf.append(cs.arg1); 102 | } 103 | buf.append(" and red'n "); 104 | buf.append(machine.reduceItem(st, cs.arg2).getSeqNo()); 105 | } else { 106 | buf.append("reduce/reduce conflict (red'ns "); 107 | buf.append(machine.reduceItem(st, cs.arg1).getSeqNo()); 108 | buf.append(" and "); 109 | buf.append(machine.reduceItem(st, cs.arg2).getSeqNo()); 110 | } 111 | buf.append(") on "); 112 | buf.append(cs.sym.getName()); 113 | buf.append(nl); 114 | } 115 | return buf.toString(); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/jacc/DebugLexer.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package jacc; 7 | 8 | import compiler.Handler; 9 | import compiler.Source; 10 | 11 | /** A simple extension of the JaccLexer that can be used for debugging. 12 | * Replace calls of new JaccLexer(...) with calls to new DebugLexer 13 | * and the lexer will display token codes as they are generated. 14 | */ 15 | public class DebugLexer extends JaccLexer { 16 | public DebugLexer(Handler handler, Source source) { 17 | super(handler, source); 18 | } 19 | 20 | public int nextToken() { 21 | int tok = super.nextToken(); 22 | System.out.println("Token " + tok + " >" + getLexeme() + "<"); 23 | return tok; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/jacc/DotOutput.java: -------------------------------------------------------------------------------- 1 | // %COPYRIGHT! 2 | // %LICENSE! 3 | // %DISTRIBUTION_DATE! 4 | // 5 | 6 | package jacc; 7 | 8 | import java.io.PrintWriter; 9 | import compiler.Handler; 10 | 11 | /** Used to generate a dot format description of the LR(0) machine. 12 | */ 13 | public class DotOutput extends Output { 14 | public DotOutput(Handler handler, JaccJob job) { 15 | super(handler, job); 16 | tables.analyzeRows(); 17 | } 18 | 19 | /** Return a name that can be used for the node for a given state 20 | * in the generated graph. 21 | */ 22 | private String stateName(int st) { 23 | return (st<0) ? "accept" : ("state"+st); 24 | } 25 | 26 | /** Output transitions from a given array of shifts or gotos. 27 | */ 28 | private void edgesFor(PrintWriter out, int st, int[] dsts) { 29 | for (int i=0; i " + stateName(dst) 34 | +"[label=\"" + txt + "\"];"); 35 | } 36 | } 37 | 38 | /** Output a dot description of the LR(0) machine. 39 | */ 40 | public void write(PrintWriter out) { 41 | datestamp(out); 42 | out.println("digraph " + settings.getInterfaceName() + " {"); 43 | out.println("node [shape=box];"); 44 | for (int st=0; st0) { 48 | String punc = "\\nreduces: {"; 49 | for (int i=0; i+ is 10 | * a left associative operator, then a+b+c should 11 | * be parsed as (a+b)+c. 12 | */ 13 | public static final int LEFT = 1; 14 | 15 | /** Specifies a nonassociative operator. If + is 16 | * a nonassociative operator, then a+b+c should 17 | * be treated as an error (ambiguous use of operators). 18 | */ 19 | public static final int NONASS = 2; 20 | 21 | /** Specifies a right associative operator. If + is 22 | * a right associative operator, then a+b+c should 23 | * be parsed as a+(b+c). 24 | */ 25 | public static final int RIGHT = 3; 26 | 27 | /** Records an associativity value (LEFT, NONASS, or RIGHT). 28 | */ 29 | private int assoc; 30 | 31 | /** Records a precedence value. 32 | */ 33 | private int prec; 34 | 35 | /** Construct a fixity object with specified precedence and 36 | * associativity. The constructor is made private to ensure 37 | * that the only ways of constructing a Fixity are by using one 38 | * of the factory methods, left, right, 39 | * or nonass. As a result, we ensure that the 40 | * assoc field will always be valid 41 | * 42 | * @param assoc should be one of LEFT, NONASS, or RIGHT. 43 | * @param prec precedence level; any integer values will 44 | * do, but higher integer values always indicate 45 | * higher precedences. 46 | */ 47 | private Fixity(int assoc, int prec) { 48 | this.assoc = assoc; 49 | this.prec = prec; 50 | } 51 | 52 | /** Construct a fixity for a left associative operator. 53 | */ 54 | public static Fixity left(int prec) { 55 | return new Fixity(LEFT, prec); 56 | } 57 | 58 | /** Construct a fixity for a nonassociative operator. 59 | */ 60 | public static Fixity nonass(int prec) { 61 | return new Fixity(NONASS,prec); 62 | } 63 | 64 | /** Construct a fixity for a right associative operator. 65 | */ 66 | public static Fixity right(int prec) { 67 | return new Fixity(RIGHT,prec); 68 | } 69 | 70 | /** Return the associativity property of this fixity object. 71 | */ 72 | public int getAssoc() { 73 | return assoc; 74 | } 75 | 76 | /** Return the precedence of this fixity object. 77 | */ 78 | public int getPrec() { 79 | return prec; 80 | } 81 | 82 | /** Use precedences to decide which of two operators should be 83 | * applied first. If possible, we apply the operator with the 84 | * highest precedence first. If the two operators have the 85 | * same precedence, and are both left assoc (resp. right assoc) 86 | * then we choose the left (resp. right) one first. If all else 87 | * fails, we determine that the use of the operators together is 88 | * ambiguous. 89 | * 90 | * @param l the fixity of the left operator. 91 | * @param r the fixity of the right operator. 92 | * @return one of: 93 | *
    94 | *
  • LEFT, meaning that the left operator 95 | * should be applied first. 96 | *
  • RIGHT, meaning that the right operator 97 | * should be applied first. 98 | *
  • NONASS, meaning that the expression is 99 | * ambiguous. 100 | *
101 | */ 102 | public static int which(Fixity l, Fixity r) { 103 | if (l!=null && r!=null) { 104 | if (l.prec > r.prec) { 105 | return LEFT; 106 | } else if (l.prec < r.prec) { 107 | return RIGHT; 108 | } else if (l.assoc==LEFT && r.assoc==LEFT) { 109 | return LEFT; 110 | } else if (l.assoc==RIGHT && r.assoc==RIGHT) { 111 | return RIGHT; 112 | } 113 | } 114 | return NONASS; 115 | } 116 | 117 | /** Test to see whether fixity objects are the same. 118 | */ 119 | public boolean equalsFixity(Fixity that) { 120 | return (this.assoc==that.assoc) && (this.prec==that.prec); 121 | } 122 | 123 | /** Standard equality test, comparing this fixity against 124 | * another object. 125 | */ 126 | public boolean equals(Object o) { 127 | if (o instanceof Fixity) { 128 | return equalsFixity((Fixity)o); 129 | } 130 | return false; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/jacc/HTMLOutput.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package jacc; 7 | 8 | import java.io.PrintWriter; 9 | import compiler.Handler; 10 | 11 | /** Used to generate an HTML description of the generated machine. 12 | */ 13 | public class HTMLOutput extends TextOutput { 14 | public HTMLOutput(Handler handler, JaccJob job, boolean wantFirst) { 15 | super(handler, job, wantFirst); 16 | } 17 | 18 | /** Output a textual description of the generated machine with HTML 19 | * hyperlinks for ease of browsing. 20 | */ 21 | public void write(PrintWriter out) { 22 | out.println(""); 23 | out.println("Generated machine for " + settings.getClassName() 24 | + ""); 25 | out.println(""); 26 | out.println("

Generated machine for " + settings.getClassName() 27 | + "

"); 28 | out.println("
");
29 |         super.write(out);
30 |         out.println("
"); 31 | out.println(""); 32 | out.println(""); 33 | } 34 | 35 | /** Generate a description of the entry point to a state. 36 | */ 37 | protected String describeEntry(int st) { 38 | return "
" + super.describeEntry(st) + ""; 39 | } 40 | 41 | /** Generate a description of a shift. 42 | */ 43 | protected String describeShift(int st) { 44 | return "" + super.describeShift(st) + ""; 45 | } 46 | 47 | /** Generate a description of a goto. 48 | */ 49 | protected String describeGoto(int st) { 50 | return "" + super.describeGoto(st) + ""; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/jacc/JaccAbstractParser.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package jacc; 7 | 8 | import compiler.Phase; 9 | import compiler.Failure; 10 | import compiler.Warning; 11 | import compiler.Handler; 12 | import compiler.Position; 13 | import jacc.grammar.Grammar; 14 | 15 | /** Abstract parser for the jacc parser generator. Implements lists 16 | * of terminal and nonterminal symbols from which a grammar can be 17 | * extracted. These lists are populated by a concrete parser that 18 | * extends this class with methods for reading the chosen concrete 19 | * syntax. 20 | */ 21 | public abstract class JaccAbstractParser extends Phase { 22 | 23 | public JaccAbstractParser(Handler handler) { 24 | super(handler); 25 | } 26 | 27 | protected NamedJaccSymbols terminals = new NamedJaccSymbols(); 28 | protected NamedJaccSymbols nonterms = new NamedJaccSymbols(); 29 | protected NumJaccSymbols literals = new NumJaccSymbols(); 30 | protected JaccSymbol start = null; 31 | 32 | /** Return a Grammar object that represents the input grammar. 33 | */ 34 | public Grammar getGrammar() { 35 | // Build an array of symbols, starting with the nonterminals, 36 | // then the terminal symbols, then any literals, and finally 37 | // the end marker $end. 38 | int numNTs = nonterms.getSize(); 39 | int numTs = terminals.getSize() + literals.getSize() + 1; 40 | if (numNTs==0 || start==null) { 41 | report(new Failure("No nonterminals defined")); 42 | return null; 43 | } 44 | JaccSymbol[] symbols = new JaccSymbol[numNTs+numTs]; 45 | literals.fill(symbols, 46 | terminals.fill(symbols, 47 | nonterms.fill(symbols,0))); 48 | symbols[numNTs+numTs-1] = new JaccSymbol("$end"); 49 | 50 | // Assign each terminal a code to be used to represent that 51 | // particular token in lexer/parser communication. The $end 52 | // symbol gets code 0, other numbers are allocated starting 53 | // from 1, but taking care to avoid clashes with codes already 54 | // assigned to literals. 55 | symbols[numNTs+numTs-1].setNum(0); 56 | int tokenCode = 1; 57 | for (int i=0; i0) { 72 | JaccSymbol temp = symbols[0]; 73 | symbols[0] = symbols[i]; 74 | symbols[i] = temp; 75 | } 76 | break; 77 | } 78 | } 79 | 80 | // Record the token number for each symbol. These numbers will 81 | // be used to map the arrays of Symbol objects in each parsed 82 | // production into corresponding arrays of integers, as required 83 | // by the Grammar class. 84 | for (int i=0; i0) { 164 | node = node.right; 165 | } else { 166 | return node.data; 167 | } 168 | } 169 | return null; 170 | } 171 | 172 | public JaccSymbol findOrAdd(String name) { 173 | if (root==null) { 174 | JaccSymbol sym = new JaccSymbol(name); 175 | root = new Node(sym); 176 | size++; 177 | return sym; 178 | } else { 179 | Node node = root; 180 | for (;;) { 181 | int cmp = name.compareTo(node.data.getName()); 182 | if (cmp<0) { 183 | if (node.left==null) { 184 | JaccSymbol sym = new JaccSymbol(name); 185 | node.left = new Node(sym); 186 | size++; 187 | return sym; 188 | } else { 189 | node = node.left; 190 | } 191 | } else if (cmp>0) { 192 | if (node.right==null) { 193 | JaccSymbol sym = new JaccSymbol(name); 194 | node.right = new Node(sym); 195 | size++; 196 | return sym; 197 | } else { 198 | node = node.right; 199 | } 200 | } else { 201 | return node.data; 202 | } 203 | } 204 | } 205 | } 206 | } 207 | 208 | /** Represents a collection of symbols, extending JaccSymbols with 209 | * the ability to add symbols using numbers as index values. 210 | */ 211 | class NumJaccSymbols extends JaccSymbols { 212 | public JaccSymbol find(int num) { 213 | Node node = root; 214 | while (node!=null) { 215 | int key = node.data.getNum(); 216 | if (numkey) { 219 | node = node.right; 220 | } else { 221 | return node.data; 222 | } 223 | } 224 | return null; 225 | } 226 | 227 | public JaccSymbol findOrAdd(String name, int num) { 228 | if (root==null) { 229 | JaccSymbol sym = new JaccSymbol(name,num); 230 | root = new Node(sym); 231 | size++; 232 | return sym; 233 | } else { 234 | Node node = root; 235 | for (;;) { 236 | int key = node.data.getNum(); 237 | if (numkey) { 247 | if (node.right==null) { 248 | JaccSymbol sym = new JaccSymbol(name,num); 249 | node.right = new Node(sym); 250 | size++; 251 | return sym; 252 | } else { 253 | node = node.right; 254 | } 255 | } else { 256 | return node.data; 257 | } 258 | } 259 | } 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/jacc/JaccJob.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package jacc; 7 | 8 | import java.io.Reader; 9 | import java.io.FileReader; 10 | import java.io.FileNotFoundException; 11 | import java.io.PrintWriter; 12 | 13 | import compiler.Handler; 14 | import compiler.Position; 15 | import compiler.Diagnostic; 16 | import compiler.Failure; 17 | import compiler.Warning; 18 | import compiler.JavaSource; 19 | import compiler.Phase; 20 | 21 | import jacc.grammar.Grammar; 22 | import jacc.grammar.Finitary; 23 | import jacc.grammar.LookaheadMachine; 24 | import jacc.grammar.Resolver; 25 | import jacc.grammar.Tables; 26 | import jacc.grammar.Parser; 27 | 28 | /** Encapsulates the process of running a single job for the jacc 29 | * parser generator, with some degree of independence from the 30 | * actual user interface. 31 | */ 32 | public class JaccJob extends Phase { 33 | private Settings settings; 34 | private JaccParser parser; 35 | private JaccTables tables; 36 | private JaccResolver resolver; 37 | private PrintWriter out; 38 | 39 | public JaccJob(Handler handler, PrintWriter out, Settings settings) { 40 | super(handler); 41 | this.out = out; 42 | this.settings = settings; 43 | this.parser = new JaccParser(handler, settings); 44 | } 45 | 46 | /** Return the settings for this job. 47 | */ 48 | Settings getSettings() { 49 | return settings; 50 | } 51 | 52 | /** Return the tables for this job. 53 | */ 54 | JaccTables getTables() { 55 | return tables; 56 | } 57 | 58 | /** Return the resolver for this job. 59 | */ 60 | JaccResolver getResolver() { 61 | return resolver; 62 | } 63 | 64 | /** Create a JaccLexer from an input file name. 65 | */ 66 | private JaccLexer lexerFromFile(String inputFile) { 67 | try { 68 | Reader input = new FileReader(inputFile); 69 | JaccLexer lexer = new JaccLexer(getHandler(), 70 | new JavaSource(getHandler(), inputFile, input)); 71 | lexer.nextToken(); // prime the token stream 72 | return lexer; 73 | } catch (FileNotFoundException e) { 74 | report(new Failure("Could not open file \"" + inputFile + "\"")); 75 | return null; 76 | } 77 | } 78 | 79 | /** Parse a grammar file. 80 | */ 81 | public void parseGrammarFile(String inputFile) { 82 | JaccLexer lexer = lexerFromFile(inputFile); 83 | if (lexer!=null) { 84 | parser.parse(lexer); 85 | } 86 | } 87 | 88 | /** Generate a machine and corresponding parse tables for the 89 | * input grammar. 90 | */ 91 | public void buildTables() { 92 | Grammar grammar = parser.getGrammar(); 93 | 94 | if (grammar==null || !allDeriveFinite(grammar)) { 95 | return; 96 | } 97 | 98 | LookaheadMachine machine = settings.makeMachine(grammar); 99 | 100 | resolver = new JaccResolver(machine); 101 | tables = new JaccTables(machine, resolver); 102 | 103 | if (tables.getProdUnused()>0) { 104 | report(new Warning(tables.getProdUnused() 105 | + " rules never reduced")); 106 | } 107 | 108 | if (resolver.getNumSRConflicts()>0 || resolver.getNumRRConflicts()>0) { 109 | report(new Warning("conflicts: " 110 | + resolver.getNumSRConflicts() 111 | + " shift/reduce, " 112 | + resolver.getNumRRConflicts() 113 | + " reduce/reduce")); 114 | } 115 | } 116 | 117 | /** Check that all nonterminals in the input grammar derive a finite 118 | * string. 119 | */ 120 | private boolean allDeriveFinite(Grammar grammar) { 121 | Finitary finitary = grammar.getFinitary(); 122 | boolean allFinite = true; 123 | for (int nt=0; nt=0; i--) { 49 | Fixity f = prodSyms[i].getFixity(); 50 | if (f!=null) { 51 | return f; 52 | } 53 | } 54 | } 55 | return fixity; 56 | } 57 | 58 | public Position getActionPos() { 59 | return actPos; 60 | } 61 | 62 | public String getAction() { 63 | return action; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/jacc/JaccResolver.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package jacc; 7 | 8 | import jacc.grammar.Grammar; 9 | import jacc.grammar.LR0Items; 10 | import jacc.grammar.LookaheadMachine; 11 | import jacc.grammar.Resolver; 12 | import jacc.grammar.Tables; 13 | import jacc.util.IntSet; 14 | 15 | /** Describes the strategy for resolving conflicts in jacc generated parsers. 16 | */ 17 | public class JaccResolver extends Resolver { 18 | private LookaheadMachine machine; 19 | 20 | /** Construct a conflict resolver for a given machine, following the 21 | * rules and conventions of Jacc/yacc. 22 | */ 23 | public JaccResolver(LookaheadMachine machine) { 24 | this.machine = machine; 25 | conflicts = new Conflicts[machine.getNumStates()]; 26 | } 27 | 28 | /** Records the number of shift/reduce conflicts that we have 29 | * failed to resolve. 30 | */ 31 | private int numSRConflicts = 0; 32 | 33 | /** Records the number of reduce/reduce conflicts that we have 34 | * failed to resolve. 35 | */ 36 | private int numRRConflicts = 0; 37 | 38 | /** Records the conflicts found at each state. 39 | */ 40 | private Conflicts[] conflicts; 41 | 42 | /** Return the number of shift/reduce conflicts detected. 43 | */ 44 | public int getNumSRConflicts() { 45 | return numSRConflicts; 46 | } 47 | 48 | /** Return the number of reduce/reduce conflicts detected. 49 | */ 50 | public int getNumRRConflicts() { 51 | return numRRConflicts; 52 | } 53 | 54 | /** Returns a description of the conflicts at a given state. 55 | */ 56 | public String getConflictsAt(int st) { 57 | return Conflicts.describe(machine, st, conflicts[st]); 58 | } 59 | 60 | /** Resolve a shift/reduce conflict. First, see if the conflict 61 | * can be resolved using fixity information. If that fails, we 62 | * choose the shift over the reduce and report a conflict. 63 | */ 64 | public void srResolve(Tables tables, int st, int tok, int redNo) { 65 | Grammar grammar = machine.getGrammar(); 66 | Grammar.Symbol sym = grammar.getTerminal(tok); 67 | IntSet its = machine.getItemsAt(st); 68 | LR0Items items = machine.getItems(); 69 | Grammar.Prod prod = items.getItem(its.at(redNo)).getProd(); 70 | 71 | if ((sym instanceof JaccSymbol) && (prod instanceof JaccProd)) { 72 | JaccSymbol jsym = (JaccSymbol)sym; 73 | JaccProd jprod = (JaccProd)prod; 74 | switch (Fixity.which(jprod.getFixity(), jsym.getFixity())) { 75 | case Fixity.LEFT: // Choose reduce 76 | tables.setReduce(st, tok, redNo); 77 | return; 78 | case Fixity.RIGHT: // Choose shift, which is already in 79 | return; // the table, so nothing more to do. 80 | } 81 | } 82 | conflicts[st] 83 | = Conflicts.sr(tables.getArgAt(st)[tok], redNo, sym, conflicts[st]); 84 | numSRConflicts++; 85 | } 86 | 87 | /** Resolve a reduce/reduce conflict. We cannot ever avoid a 88 | * reduce/reduce conflict, but the entry that we leave in the 89 | * table must be for the production with the lowest number. 90 | */ 91 | public void rrResolve(Tables tables, int st, int tok, int redNo) { 92 | Grammar grammar = machine.getGrammar(); 93 | int redNo0 = tables.getArgAt(st)[tok]; 94 | IntSet its = machine.getItemsAt(st); 95 | LR0Items items = machine.getItems(); 96 | Grammar.Prod prod0 = items.getItem(its.at(redNo0)).getProd(); 97 | Grammar.Prod prod = items.getItem(its.at(redNo)).getProd(); 98 | Grammar.Symbol sym = grammar.getTerminal(tok); 99 | 100 | if (prod.getSeqNo()=jaccProds.length) { 68 | JaccProd[] newprods = new JaccProd[2*jaccProds.length]; 69 | for (int i=0; i=0; i--) { 177 | heapify(i); 178 | } 179 | for (int i=size-1; i>0; i--) { 180 | int t = idx[i]; 181 | idx[i] = idx[0]; 182 | idx[0] = t; 183 | size--; 184 | heapify(0); 185 | } 186 | index[state] = idx; 187 | 188 | // Determine which row to use as default 189 | defaultRow[state] = findDefault(); 190 | } 191 | 192 | private void heapify(int i) { 193 | int m = i; 194 | int im = idx[m]; 195 | for (;;) { 196 | int l = 2*i+1; 197 | int r = l + 1; 198 | if (la[im] || (a[il]==a[im] && b[il]>b[im])) { 201 | m = l; 202 | im = il; 203 | } 204 | if (ra[im] || (a[ir]==a[im] && b[ir]>b[im])) { 207 | m = r; 208 | im = ir; 209 | } 210 | } 211 | } 212 | if (m==i) { 213 | return; 214 | } 215 | idx[m] = idx[i]; 216 | idx[i] = im; 217 | i = m; 218 | im = idx[m]; 219 | } 220 | } 221 | 222 | public int findDefault() { 223 | int best = 1; // must repeat >=2 times to be used as default 224 | int def = (-1); // negative value indicates no default in use 225 | for (int i=0; ibest) { 238 | def = ii; 239 | best = count; 240 | } 241 | } 242 | } 243 | return def; 244 | } 245 | 246 | private void display() { 247 | for (int i=0; i 40 | int BOPEN = '['; // [ 41 | int BCLOSE = ']'; // ] 42 | int DOT = '.'; // . 43 | } 44 | -------------------------------------------------------------------------------- /src/jacc/Output.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package jacc; 7 | 8 | import java.io.FileWriter; 9 | import java.io.PrintWriter; 10 | import java.io.IOException; 11 | import java.util.Date; 12 | import jacc.grammar.Grammar; 13 | import jacc.grammar.Machine; 14 | import compiler.Phase; 15 | import compiler.Handler; 16 | import compiler.Failure; 17 | 18 | /** Base class for Jacc output routines. 19 | */ 20 | public abstract class Output extends Phase { 21 | protected JaccJob job; 22 | protected Grammar grammar; 23 | protected int numTs; 24 | protected int numNTs; 25 | protected int numSyms; 26 | protected Machine machine; 27 | protected int numStates; 28 | protected JaccTables tables; 29 | protected JaccResolver resolver; 30 | protected Settings settings; 31 | 32 | /** Construct a new output routine. 33 | */ 34 | protected Output(Handler handler, JaccJob job) { 35 | super(handler); 36 | this.job = job; 37 | this.tables = job.getTables(); 38 | this.machine = tables.getMachine(); 39 | this.grammar = machine.getGrammar(); 40 | this.numTs = grammar.getNumTs(); 41 | this.numNTs = grammar.getNumNTs(); 42 | this.numSyms = grammar.getNumSyms(); 43 | this.numStates = machine.getNumStates(); 44 | this.resolver = job.getResolver(); 45 | this.settings = job.getSettings(); 46 | } 47 | 48 | /** Create a file and write output. 49 | */ 50 | public void write(String filename) { 51 | PrintWriter writer = null; 52 | try { 53 | writer = new PrintWriter(new FileWriter(filename)); 54 | write(writer); 55 | } catch (IOException e) { 56 | report(new Failure("Cannot write to file \"" + filename + "\"")); 57 | } 58 | if (writer!=null) { 59 | writer.close(); 60 | } 61 | } 62 | 63 | /** Generate the required output. 64 | */ 65 | public abstract void write(PrintWriter out); 66 | 67 | // Miscellaneous helper functions: --------------------------------------- 68 | 69 | /** Print an array of strings. 70 | */ 71 | protected static void indent(PrintWriter out, int ind, String[] strs) { 72 | for (int i=0; i0) { 68 | for (int j=0; j0) { 90 | for (int nt=0; nt 12 | *
  • Defining a constructor that invokes super(comps), sets up 13 | * the initial data for the analysis, and then invokes either 14 | * topDown() or bottomUp(); 15 | *
  • 16 | *
  • Overides analyze() to do the appropriate calculations for the 17 | * analysis at the point concerned. 18 | *
  • 19 | * 20 | */ 21 | public abstract class Analysis { 22 | /** Records the underlying set of components, starting with the ones 23 | * that have no lower descendents. 24 | */ 25 | private int[][] comps; 26 | 27 | /** Construct a component analysis instance by recording the 28 | * component members. 29 | */ 30 | protected Analysis(int[][] comps) { 31 | this.comps = comps; 32 | } 33 | 34 | /** Method used to run a bottom-up analysis. This is an analysis 35 | * in which each object passes data to the things that depend on it. 36 | */ 37 | protected void bottomUp() { 38 | for (int i=0; i0; ) { 48 | analyzeComponent(comps[i]); 49 | } 50 | } 51 | 52 | /** Method used to run an analysis over the elements of a component. 53 | * The analysis is iterated until no changes are detected. 54 | */ 55 | private void analyzeComponent(int[] comp) { 56 | boolean changed = true; 57 | while (changed) { 58 | changed = false; 59 | for (int j=0; j a1 ... an is a production 10 | * then: 11 | * finitary a1 && ... && finitary an => finitary X 12 | * 13 | * Using also the fact that finitary t = true for any terminal t, we can 14 | * iterate to find finitary values for each nonterminal. 15 | */ 16 | final public class Finitary extends Analysis { 17 | private boolean[] finitary; 18 | private boolean[] consider; 19 | private Grammar grammar; 20 | private int numNTs; 21 | 22 | /** Construct a finitary analysis for a given grammar. 23 | */ 24 | public Finitary(Grammar grammar) { 25 | super(grammar.getComponents()); 26 | this.grammar = grammar; 27 | this.numNTs = grammar.getNumNTs(); 28 | finitary = new boolean[numNTs]; 29 | consider = new boolean[numNTs]; 30 | for (int i=0; i=rhs.length) { 52 | finitary[c] = true; 53 | consider[c] = false; 54 | changed = true; 55 | break; 56 | } else if (!consider[rhs[l]]) { 57 | blocked++; 58 | } 59 | } 60 | if (blocked==prods.length) { 61 | consider[c] = false; 62 | } 63 | } 64 | return changed; 65 | } 66 | 67 | /** Return a boolean true if the given symbol is finitary. 68 | */ 69 | public boolean at(int i) { 70 | return grammar.isTerminal(i) || finitary[i]; 71 | } 72 | 73 | /** Display the results of the analysis for the purposes of debugging 74 | * and inspection. 75 | */ 76 | public void display(java.io.PrintWriter out) { 77 | out.print("Finitary = {"); 78 | int count = 0; 79 | for (int i=0; i0) { 82 | out.print(", "); 83 | } 84 | out.print(grammar.getSymbol(i).getName()); 85 | count++; 86 | } 87 | } 88 | out.println("}"); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/jacc/grammar/First.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package jacc.grammar; 7 | 8 | import jacc.util.BitSet; 9 | 10 | /** Calculation of first sets. The first set of a given nonterminal X 11 | * is the set of all terminal symbols that can appear at the beginning 12 | * of a string derived from X. 13 | */ 14 | public final class First extends Analysis { 15 | private Grammar grammar; 16 | private Nullable nullable; 17 | private int numNTs; 18 | private int numTs; 19 | private int[][] first; 20 | 21 | /** Construct a first set analysis for a given grammar. 22 | */ 23 | public First(Grammar grammar, Nullable nullable) { 24 | super(grammar.getComponents()); 25 | this.grammar = grammar; 26 | this.nullable = nullable; 27 | this.numNTs = grammar.getNumNTs(); 28 | this.numTs = grammar.getNumTs(); 29 | first = new int[numNTs][]; 30 | for (int i=0; i=rhs.length) { 69 | if (BitSet.addTo(follow[rhs[l]], follow[c])) { 70 | changed = true; 71 | } 72 | } 73 | } 74 | } 75 | } 76 | return changed; 77 | } 78 | 79 | /** Return a bitset of the follow symbols for a given nonterminal. 80 | */ 81 | public int[] at(int i) { 82 | return follow[i]; 83 | } 84 | 85 | /** Display the results of the analysis for the purposes of debugging 86 | * and inspection. 87 | */ 88 | public void display(java.io.PrintWriter out) { 89 | out.println("Follow sets:"); 90 | for (int i=0; i v nt _ w) is 129 | * in state st1, w is nullable, and we can reach state k from st by 130 | * going back along v, then the goto from k on Y will be a target 131 | * for g. 132 | * 133 | * @param g is the number of the goto in the global tables 134 | * gotoLA and gotoTargets. We write back the results 135 | * of this call into the slots of these arrays with 136 | * index gn. 137 | */ 138 | private void calcTargets(int g) { 139 | int st = gotoSource[g]; 140 | int st1 = gotoTrans[g]; 141 | int nt = getEntry(st1); 142 | IntSet its = getItemsAt(st1); 143 | int sz = its.size(); 144 | int[] fs = BitSet.make(numTs); 145 | IntSet ts = IntSet.empty(); 146 | for (int j=0; j=0) { 151 | int[] rhs = it.getProd().getRhs(); 152 | if (pos>0 && rhs[--pos]==nt) { 153 | if (calcFirsts(fs, it).canReduce()) { 154 | findTargets(ts, st, lhs, rhs, pos); 155 | } 156 | } 157 | } else if (pos>0) { 158 | BitSet.set(fs, numTs-1); 159 | } 160 | } 161 | gotoLA[g] = fs; 162 | gotoTargets[g] = ts.toArray(); 163 | } 164 | 165 | /** Calculate the tokens that we can reach from a given item without 166 | * leaving the production concerned. If the item is A -> v _ w, 167 | * then we will add all the elements of FIRST(w) to the accumulating 168 | * fs parameter, and return the item A -> v w1 _ w2, where w = w1 w2, 169 | * w1 is nullable, and either w2 is empty or w2 is not nullable. 170 | * 171 | * @param fs An accumulating parameter that holds a bitset of the 172 | * tokens that might occur at the beginning of the string 173 | * to the right of the _ mark in the specified item. 174 | * @param it An item of the grammar. 175 | */ 176 | private LR0Items.Item calcFirsts(int[] fs, LR0Items.Item it) { 177 | while (it.canGoto()) { 178 | int sym = it.getNextSym(); 179 | if (grammar.isTerminal(sym)) { 180 | BitSet.addTo(fs,sym-numNTs); 181 | break; 182 | } else { 183 | BitSet.union(fs, first.at(sym)); 184 | if (!nullable.at(sym)) { 185 | break; 186 | } 187 | it = items.getItem(it.getNextItem()); 188 | } 189 | } 190 | if (it.canAccept()) { 191 | BitSet.set(fs,numTs-1); 192 | } 193 | return it; 194 | } 195 | 196 | /** Find target gotos by tracing back from a state st along a 197 | * prefix of symbols from the rhs of a production. An initial 198 | * accumulating parameter is used to collect any targets that 199 | * are found. 200 | * 201 | * @param ts an accumulating parameter for the set of targets. 202 | * @param st the state in which we are currently looking. 203 | * @param lhs the nonterminal on the lhs of the production. 204 | * @param rhs the right hand side of the production. 205 | * @param pos current position with the production; 0 means 206 | * that we have found our way back to a state 207 | * that (potentially) contains a relevant goto. 208 | */ 209 | private void findTargets(IntSet ts, int st, int lhs, int[] rhs, int pos) { 210 | if (pos==0) { 211 | int[] gotos = getGotosAt(st); 212 | for (int i=0; i0) { 301 | out.print(", "); 302 | } 303 | out.print(gotoTargets[g][j]); 304 | } 305 | out.println("}"); 306 | } 307 | 308 | // Display lookahead information for each reduce item. 309 | for (int st=0; st0) { 312 | out.println("State " + st + ": "); 313 | IntSet its = getItemsAt(st); 314 | for (int j=0; j _ S $ 35 | new Item(-1,0,1); // Represents S' -> S _ $ 36 | for (int i=0; i _ S $ when we recognize S. 163 | * We don't add a goto for the other special production (i.e., 164 | * S' -> S _ $) because we don't have a state to transition too. 165 | * Nevertheless, we do need to account for the $ symbol elsewhere 166 | * when we calculate lookaheads. 167 | */ 168 | public boolean canGoto() { 169 | if (lhs<0) { 170 | return (pos==0); 171 | } else { 172 | return (pos!=getProd().getRhs().length); 173 | } 174 | } 175 | 176 | /** Determine if this is an item on which we can reduce. 177 | * This is not quite a negation of canGoto() because of 178 | * the special case treatment of S' -> S _ $. 179 | */ 180 | public boolean canReduce() { 181 | return (lhs>=0) && (pos==getProd().getRhs().length); 182 | } 183 | 184 | /** Determine if this item can shift the $ end marker to accept. 185 | */ 186 | public boolean canAccept() { 187 | return (lhs<0) && (pos==1); 188 | } 189 | 190 | /** Return the number of the next item that we can goto by 191 | * advancing this item. This method should only be called on 192 | * items for which canGoto() returns 193 | * true. 194 | */ 195 | public int getNextItem() { 196 | if (lhs>=0) { 197 | return itemNo+1; 198 | } else { 199 | return 1; 200 | } 201 | } 202 | 203 | /** Return the number of the symbol that we must pass to 204 | * advance this item. This method should only be called on 205 | * items for which canGoto() returns 206 | * true. 207 | */ 208 | public int getNextSym() { 209 | if (lhs>=0) { 210 | return grammar.getProds(lhs)[prodNo].getRhs()[pos]; 211 | } else { 212 | return 0; // the start symbol 213 | } 214 | } 215 | 216 | /** Return a printable representation of this item. 217 | */ 218 | public void display(java.io.PrintWriter out) { 219 | if (lhs<0) { 220 | if (pos==0) { 221 | out.print("$accept : _" + grammar.getStart() + 222 | " " + grammar.getEnd()); 223 | } else { 224 | out.print("$accept : " + grammar.getStart() + 225 | "_" + grammar.getEnd()); 226 | } 227 | return; 228 | } 229 | out.print(grammar.getSymbol(lhs)); 230 | out.print(" : "); 231 | Grammar.Prod prod = grammar.getProds(lhs)[prodNo]; 232 | int[] rhs = prod.getRhs(); 233 | out.print(grammar.displaySymbols(rhs, 0, pos, "", " ")); 234 | out.print("_"); 235 | if (pos0 && grammar.isNonterminal(rhs[0])) { 43 | if (BitSet.addTo(left[c], left[rhs[0]])) { 44 | changed = true; 45 | } 46 | } 47 | } 48 | return changed; 49 | } 50 | 51 | /** Return a bitset of the left symbols for a given nonterminal. 52 | */ 53 | public int[] at(int i) { 54 | return left[i]; 55 | } 56 | 57 | /** Display the results of the analysis for the purposes of debugging 58 | * and inspection. 59 | */ 60 | public void display(java.io.PrintWriter out) { 61 | out.println("Left nonterminal sets:"); 62 | for (int i=0; i a1 ... an is a production then: 10 | * 11 | * nullable a1 && ... && nullable an => nullable X 12 | * 13 | * Using also the fact that nullable t = false for any terminal t, we can 14 | * iterate to find nullable values for each nonterminal. 15 | */ 16 | public final class Nullable extends Analysis { 17 | private boolean[] nullable; 18 | private boolean[] consider; 19 | private Grammar grammar; 20 | private int numNTs; 21 | 22 | /** Construct a nullable analysis for a given grammar. 23 | */ 24 | public Nullable(Grammar grammar) { 25 | super(grammar.getComponents()); 26 | this.grammar = grammar; 27 | this.numNTs = grammar.getNumNTs(); 28 | nullable = new boolean[numNTs]; 29 | consider = new boolean[numNTs]; 30 | for (int i=0; i=rhs.length) { 52 | nullable[c] = true; 53 | consider[c] = false; 54 | changed = true; 55 | break; 56 | } else if (grammar.isTerminal(rhs[l]) 57 | || (grammar.isNonterminal(rhs[l]) 58 | && !consider[rhs[l]])) { 59 | blocked++; 60 | } 61 | } 62 | if (blocked==prods.length) { 63 | consider[c] = false; 64 | } 65 | } 66 | return changed; 67 | } 68 | 69 | /** Return a boolean true if the given symbol is nullable. 70 | */ 71 | public boolean at(int i) { 72 | return grammar.isNonterminal(i) && nullable[i]; 73 | } 74 | 75 | /** Display the results of the analysis for the purposes of debugging 76 | * and inspection. 77 | */ 78 | public void display(java.io.PrintWriter out) { 79 | out.print("Nullable = {"); 80 | int count = 0; 81 | for (int i=0; i0) { 84 | out.print(", "); 85 | } 86 | out.print(grammar.getSymbol(i).getName()); 87 | count++; 88 | } 89 | } 90 | out.println("}"); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/jacc/grammar/Parser.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package jacc.grammar; 7 | 8 | /** Provides a parsing engine for a given grammar/machine/parse table. 9 | */ 10 | public class Parser { 11 | /** Parse tables for this machine. 12 | */ 13 | private Tables tables; 14 | 15 | /** A stream of input tokens. 16 | */ 17 | private int[] input; 18 | 19 | /** The underlying machine for this parser. 20 | */ 21 | private Machine machine; 22 | 23 | /** The underlying grammar for this parser. 24 | */ 25 | private Grammar grammar; 26 | 27 | /** Construct a machine for a given grammar. 28 | */ 29 | public Parser(Tables tables, int[] input) { 30 | this.tables = tables; 31 | this.input = input; 32 | this.machine = tables.getMachine(); 33 | this.grammar = machine.getGrammar(); 34 | } 35 | 36 | /** Index of next token in input stream. 37 | */ 38 | private int position = 0; 39 | 40 | /** Holds the most recently "read" token (if there is one). 41 | */ 42 | private int currSymbol = (-1); 43 | 44 | /** A non-negative value indicates that we have just executed 45 | * a reduce action on the corresponding non-terminal. 46 | */ 47 | private int reducedNT = (-1); 48 | 49 | /** Holds the current parser stack. 50 | */ 51 | private Stack stack = new Stack(); 52 | 53 | /** Holds the current parser state. 54 | */ 55 | private int state = 0; 56 | 57 | /** Return the current parser state. 58 | */ 59 | public int getState() { 60 | return state; 61 | } 62 | 63 | /** Return the next input symbol. 64 | */ 65 | public int getNextSymbol() { 66 | return (reducedNT>=0) ? reducedNT : currSymbol; 67 | } 68 | 69 | /** Define constants that are returned by the step() method to 70 | * indicate the action that has been taken. The parser will 71 | * typically be invoked by calling step repeatedly until either 72 | * either Parser.ACCEPT or Parser.ERROR are returned. 73 | */ 74 | public final static int ACCEPT = 0; 75 | public final static int ERROR = 1; 76 | public final static int SHIFT = 2; 77 | public final static int GOTO = 3; 78 | public final static int REDUCE = 4; 79 | 80 | /** Advance the parser by one step, returning a tag to indicate 81 | * the type of action that was taken. 82 | */ 83 | public int step() { 84 | if (state<0) { 85 | return ACCEPT; 86 | } 87 | 88 | if (reducedNT>=0) { 89 | shift(reducedNT); 90 | if (!gotoState(reducedNT)) { 91 | return ERROR; // shouldn't occur if machine is correct 92 | } 93 | reducedNT = (-1); 94 | return GOTO; 95 | } 96 | 97 | if (currSymbol<0) { 98 | currSymbol = (position>=input.length) ? grammar.getNumSyms()-1 99 | : input[position++]; 100 | } 101 | 102 | if (grammar.isNonterminal(currSymbol)) { 103 | shift(currSymbol); 104 | if (!gotoState(currSymbol)) { 105 | return ERROR; // could occur for nonterminal in input stream 106 | } 107 | currSymbol = (-1); 108 | return GOTO; 109 | } else { 110 | byte[] action = tables.getActionAt(state); 111 | int[] arg = tables.getArgAt(state); 112 | int t = currSymbol - grammar.getNumNTs(); 113 | switch (action[t]) { 114 | case Tables.SHIFT: { 115 | if (arg[t]<0) { 116 | return ACCEPT; 117 | } 118 | shift(currSymbol); 119 | currSymbol = (-1); 120 | state = arg[t]; 121 | return SHIFT; 122 | } 123 | 124 | case Tables.REDUCE: { 125 | reduce(arg[t]); 126 | return REDUCE; 127 | } 128 | } 129 | } 130 | return ERROR; 131 | } 132 | 133 | /** Shift a particular symbol onto the parse stack. 134 | */ 135 | private void shift(int symbol) { 136 | stack = stack.push(state, symbol); 137 | } 138 | 139 | /** Perform a reduce action, matching items on the top of the 140 | * stack with the right hand of a particular item, and then 141 | * preparing to execute a GOTO for the corresponding left hand 142 | * side on the next step. 143 | */ 144 | private void reduce(int arg) { 145 | LR0Items.Item it = machine.reduceItem(state, arg); 146 | int n = it.getProd().getRhs().length; 147 | if (n>0) { 148 | for (; n>1; n--) { 149 | stack = stack.pop(); 150 | } 151 | state = stack.getState(); 152 | stack = stack.pop(); 153 | } 154 | reducedNT = it.getLhs(); 155 | } 156 | 157 | /** Attempt to execute a goto action on a particular symbol 158 | * in the current state. Return a boolean to indicate if a 159 | * suitable goto was found. 160 | */ 161 | private boolean gotoState(int symbol) { 162 | int[] ts = machine.getGotosAt(state); 163 | for (int i=0; i=0) { 182 | out.print(grammar.getSymbol(reducedNT).toString()); 183 | out.print(" "); 184 | } 185 | if (currSymbol>=0) { 186 | out.print(grammar.getSymbol(currSymbol).toString()); 187 | if (position0) { 59 | out.println("In state " + i + ":"); 60 | for (int j=0; j 93 | *
  • NONE indicates that no action is possible, and hence an 94 | * error has occured. 95 | *
  • SHIFT indicates that a shift is required. 96 | *
  • REDUCE indicates that a reduce step is required. 97 | * 98 | */ 99 | public byte[] getActionAt(int st) { 100 | return action[st]; 101 | } 102 | 103 | /** Return the argument table at a particular state. The 104 | * interpretation of the entries in this table (indexed by 105 | * terminals) varies according to the corresponding action 106 | * entry. 107 | *
      108 | *
    • If the action is NONE, then the argument is not used. 109 | *
    • If the action is SHIFT, then the argument is the number 110 | * of the state to which the machine will shift. 111 | *
    • If the action is REDUCE, then the argument is the offset 112 | * of the item in Machine.getItemsAt(st) by which we should 113 | * reduce. 114 | *
    115 | */ 116 | public int[] getArgAt(int st) { 117 | return arg[st]; 118 | } 119 | 120 | /** Return the number of unused productions. A production is unused 121 | * if there are no entries in the constructed parse tables for the 122 | * corresponding reduction. 123 | */ 124 | public int getProdUnused() { 125 | return prodUnused; 126 | } 127 | 128 | /** Return the array of booleans indicating which of the productions 129 | * for a particular nonterminal have been entered into the table. 130 | */ 131 | public boolean[] getProdsUsedAt(int nt) { 132 | return prodUsed[nt]; 133 | } 134 | 135 | /** Store a SHIFT entry in the table for a particular state. 136 | */ 137 | public void setShift(int st, int tok, int to) { 138 | action[st][tok] = SHIFT; 139 | arg[st][tok] = to; 140 | } 141 | 142 | /** Store a REDUCE entry in the table for a particular state. 143 | */ 144 | public void setReduce(int st, int tok, int num) { 145 | action[st][tok] = REDUCE; 146 | arg[st][tok] = num; 147 | } 148 | 149 | /** Fill in tables for a particular state using info from the machine. 150 | */ 151 | private void fillTablesAt(int st) { 152 | action[st] = new byte[numTs]; // all initialized to NONE 153 | arg[st] = new int[numTs]; 154 | int[] shifts = machine.getShiftsAt(st); 155 | int[] rs = machine.getReducesAt(st); 156 | 157 | // Enter shifts into table. 158 | for (int i=0; i> LOG_BITS_PER_WORD]; 18 | } 19 | 20 | public static int[] copy(int[] set) { 21 | int[] ret = new int[set.length]; 22 | for (int i=0; i=s1.length && i>=s2.length; 51 | } 52 | 53 | public static boolean disjoint(int[] s1, int[] s2) { 54 | int i = 0; 55 | for (; i=s1.length && i>=s2.length; 61 | } 62 | 63 | public static void union(int[] s1, int[] s2) { 64 | for (int i=0; i> LOG_BITS_PER_WORD; 96 | int val = s[pos] | mask; 97 | if (val!=s[pos]) { 98 | s[pos] = val; 99 | return true; 100 | } else { 101 | return false; 102 | } 103 | } 104 | 105 | public static void set(int[] s, int n) { 106 | int mask = 1 << (n & BIT_MASK); 107 | int pos = n >> LOG_BITS_PER_WORD; 108 | s[pos] |= mask; 109 | } 110 | 111 | public static boolean get(int[] s, int n) { 112 | int mask = 1 << (n & BIT_MASK); 113 | int pos = n >> LOG_BITS_PER_WORD; 114 | return (s[pos] & mask)!=0; 115 | } 116 | 117 | public static int[] members(int[] s) { 118 | int count = 0; 119 | for (int i=0; i>= 1; 127 | } 128 | } 129 | } 130 | 131 | int[] mems = new int[count]; 132 | int out = 0; 133 | for (int i=0; i>= 1; 142 | } 143 | } 144 | } 145 | return mems; 146 | } 147 | 148 | public static Interator interator(int[] set, int start) { 149 | return new BitSetInterator(set, start); 150 | } 151 | 152 | private static class BitSetInterator extends Interator { 153 | int[] set; 154 | int pos; 155 | int mask; 156 | int num; 157 | int bitCount; 158 | BitSetInterator(int[] set, int start) { 159 | this.set = set; 160 | this.num = start; 161 | pos = 0; 162 | mask = 1; 163 | bitCount = 0; 164 | } 165 | private void advance() { 166 | num++; 167 | if (++bitCount == BITS_PER_WORD) { 168 | pos++; 169 | bitCount = 0; 170 | mask = 1; 171 | } else { 172 | mask <<= 1; 173 | } 174 | } 175 | public int next() { 176 | int value = num; 177 | advance(); 178 | return value; 179 | } 180 | public boolean hasNext() { 181 | while (pos=elems.length) { 90 | int[] newElems = new int[elems.length*2]; 91 | for (int i=0; ilo; i--) { 101 | elems[i] = elems[i-1]; 102 | } 103 | elems[lo] = val; 104 | } 105 | used++; 106 | } 107 | 108 | public boolean equals(IntSet that) { 109 | if (this.used == that.used) { 110 | for (int i=0; i0) { 115 | out.print(", "); 116 | } 117 | out.print(comps[i][j]); 118 | } 119 | out.println("}"); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/jacc/util/SeqInterator.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Mark P Jones, OGI School of Science & Engineering 2 | // Subject to conditions of distribution and use; see LICENSE for details 3 | // April 24 2004 01:01 AM 4 | // 5 | 6 | package jacc.util; 7 | 8 | public class SeqInterator extends Interator { 9 | private int count, limit; 10 | public SeqInterator(int count, int limit) { 11 | this.count = count; 12 | this.limit = limit; 13 | } 14 | public int next() { 15 | return count++; 16 | } 17 | public boolean hasNext() { 18 | return count < limit; 19 | } 20 | } 21 | --------------------------------------------------------------------------------