├── .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 | 
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 |
--------------------------------------------------------------------------------