|
1 package G4ip; |
|
2 |
|
3 import java.util.*; |
|
4 import pizza.lang.Pair; |
|
5 import G4ip.Form.*; |
|
6 import G4ip.Token.*; |
|
7 |
|
8 public class Token { |
|
9 public static final int AND = 0; |
|
10 public static final int OR = 1; |
|
11 public static final int IMP = 2; |
|
12 public static final int EQU = 3; |
|
13 |
|
14 /** End-of-Input */ |
|
15 public case EOI(); // end-of-input |
|
16 public case LPAREN(); |
|
17 public case RPAREN(); |
|
18 public case ID(String id); |
|
19 public case FALSE(); |
|
20 public case BINOP(int op); // binary operation |
|
21 public case COMMA(); |
|
22 } |
|
23 |
|
24 /** A left-to-right, rightmost-derivation parser.<p> |
|
25 * The following grammar is implemented:<p> |
|
26 * <dl> |
|
27 * <dd><u>Formula</u> ::= <u>Id</u> </dd> |
|
28 * <dd><code>| false</code> </dd> |
|
29 * <dd><code>|</code> ( <u>Formula</u> ) </dd> |
|
30 * <dd><code>|</code> <u>Formula</u> <u>Binop</u> <u>Formula</u></dd> |
|
31 * </dl><p> |
|
32 * <dl> |
|
33 * <dd><u>Id</u> is a string of lower case letters</dd> |
|
34 * </dl><p> |
|
35 * <dl> |
|
36 * <dd><u>Binop</u> is either <code>&, v, -></code> or <code><-></code></dd> |
|
37 * </dl><p> |
|
38 * <dl> |
|
39 * <dd><u>FormulaList</u> ::= <em>empty</em></dd> |
|
40 * <dd><code>|</code> [ <u>FormulaList</u> ,]* <u>Formula</u></dd> |
|
41 * <dl><p> |
|
42 * The parser uses a stack where two actions are performed: |
|
43 * <dl> |
|
44 * <dd><b>shift</b> moves the next token to the top of the stack (getNextToken)</dd> |
|
45 * <dd><b>reduce</b> chooses a grammar rule, X -> A B C; pops A, B, C from the |
|
46 * top of the stack and pushes X onto the stack |
|
47 * </dl> |
|
48 * @author Christian Urban |
|
49 */ |
|
50 public class Parser { |
|
51 final Pair<String,Token> keywords[] = |
|
52 { new Pair("(", LPAREN()), |
|
53 new Pair(")", RPAREN()), |
|
54 new Pair(",", COMMA()), |
|
55 new Pair("&", BINOP(Token.AND)), |
|
56 new Pair("v ", BINOP(Token.OR)), |
|
57 new Pair("->", BINOP(Token.IMP)), |
|
58 new Pair("<->", BINOP(Token.EQU)), |
|
59 new Pair("false",FALSE()) }; |
|
60 String in; // string to be parsed |
|
61 int index; // position of the current input |
|
62 Stack stack; // stack for the left-to-right parser 'LR(0)' |
|
63 |
|
64 public Parser(String init_in){ |
|
65 index = 0; |
|
66 in = init_in; |
|
67 stack = new Stack(); |
|
68 } |
|
69 |
|
70 /** tokens are: identifiers<code>( ) , & v -> <-> false</code> |
|
71 * and <code>EOI</code> <em>(end of input)</em> |
|
72 */ |
|
73 public Token getNextToken() throws Exception { |
|
74 while ( index < in.length() && Character.isSpace(in.charAt(index)) ) |
|
75 { index++; } //delete white-spaces |
|
76 if (index == in.length()) { return EOI(); } //end-of-string |
|
77 for (int i=0;i<keywords.length;i++) //keywords |
|
78 { if (in.startsWith(keywords[i].fst,index)) { |
|
79 index=index+keywords[i].fst.length(); |
|
80 return keywords[i].snd; |
|
81 } |
|
82 } |
|
83 if (Character.isLowerCase(in.charAt(index)) || Character.isDigit(in.charAt(index))) |
|
84 { //reads identifiers |
|
85 String s = ""; |
|
86 while ( index < in.length() && |
|
87 ( Character.isLowerCase(in.charAt(index)) |
|
88 || Character.isDigit(in.charAt(index)) |
|
89 ) |
|
90 ) |
|
91 { s=s.concat(in.substring(index,index+1)); |
|
92 index++; |
|
93 } |
|
94 return ID(s); |
|
95 } |
|
96 throw new Exception("Syntax error at: '" + in.charAt(index) + "'"); |
|
97 // no match at all: probably an unknown character |
|
98 return null; |
|
99 } |
|
100 |
|
101 /** Implements the grammar rules. */ |
|
102 public void reduce() |
|
103 { boolean again = false; |
|
104 /* ID -> Atm(string) */ |
|
105 if (stack.size() > 0 && |
|
106 (stack.elementAt(stack.size()-1) instanceof ID)) |
|
107 { ID id = (ID)stack.pop(); |
|
108 stack.push(Atm(id.id)); |
|
109 again = true; |
|
110 } |
|
111 /* FALSE -> False() */ |
|
112 if (stack.size() > 0 && |
|
113 (stack.elementAt(stack.size()-1) instanceof FALSE)) |
|
114 { stack.pop(); |
|
115 stack.push(False()); |
|
116 again = true; |
|
117 } |
|
118 /* ( Formula ) -> Formula */ |
|
119 if (stack.size() > 2 && |
|
120 (stack.elementAt(stack.size()-3) instanceof LPAREN) && |
|
121 (stack.elementAt(stack.size()-2) instanceof Form) && |
|
122 (stack.elementAt(stack.size()-1) instanceof RPAREN)) |
|
123 { stack.pop(); |
|
124 Form form = (Form)stack.pop(); |
|
125 stack.pop(); |
|
126 stack.push(form); |
|
127 again = true; |
|
128 } |
|
129 /* Formula BINOP Formula -> Formula */ |
|
130 if (stack.size() > 2 && |
|
131 (stack.elementAt(stack.size()-3) instanceof Form) && |
|
132 (stack.elementAt(stack.size()-2) instanceof BINOP) && |
|
133 (stack.elementAt(stack.size()-1) instanceof Form)) |
|
134 { Form c2 = (Form)stack.pop(); |
|
135 BINOP op = (BINOP)stack.pop(); |
|
136 Form c1 = (Form)stack.pop(); |
|
137 switch(op.op) { |
|
138 case Token.AND: stack.push(new And(c1,c2)); again = true;break; |
|
139 case Token.OR: stack.push(new Or(c1,c2)); again = true;break; |
|
140 case Token.IMP: stack.push(new Imp(c1,c2)); again = true;break; |
|
141 case Token.EQU: stack.push(new And(Imp(c1,c2),Imp(c2,c1))); again = true;break; |
|
142 } |
|
143 } |
|
144 if (again == true) { reduce(); } // do as many "reduces" as possible |
|
145 } |
|
146 |
|
147 /** parses a single formula |
|
148 */ |
|
149 public Form parseFormula() throws Exception { |
|
150 Token tok; |
|
151 while (!((tok = getNextToken()) instanceof EOI)) { |
|
152 stack.push(tok); |
|
153 reduce(); |
|
154 } |
|
155 if (stack.size() == 1 && |
|
156 (stack.elementAt(stack.size()-1) instanceof Form)) |
|
157 { return (Form)stack.pop(); } |
|
158 else throw new Exception("Grammar error"); |
|
159 return null; |
|
160 } |
|
161 |
|
162 /** parses a list of formulae separated by commas |
|
163 */ |
|
164 public Context parseFormulae() throws Exception { |
|
165 Token tok; |
|
166 Context erg = new Context(); |
|
167 while (!((tok = getNextToken()) instanceof EOI)) { |
|
168 stack.push(tok); |
|
169 reduce(); |
|
170 } |
|
171 if (stack.empty()) return erg; // LHS can be empty !! |
|
172 stack.push(new COMMA()); |
|
173 for(int i=0;i<stack.size()-1;i=i+2){ |
|
174 if (stack.elementAt(i) instanceof Form) |
|
175 { erg.addElement(stack.elementAt(i)); |
|
176 } else throw new Exception("Grammar error"); |
|
177 if (stack.elementAt(i+1) instanceof COMMA) |
|
178 {} else throw new Exception("Grammar error"); |
|
179 } |
|
180 return erg; |
|
181 } |
|
182 |
|
183 } |