1
|
1 package plparser;
|
|
2
|
|
3 import java.io.FileNotFoundException;
|
|
4 import java.io.InputStream;
|
|
5 import java.util.LinkedList;
|
|
6
|
|
7
|
|
8 public class PropertyListParser<Node extends Property> {
|
|
9 PropertyListNodeFactory<Node> lf;
|
|
10 Token<Node> nextToken;
|
|
11 public PropertyListScanner<Node> scanner;
|
|
12 private Dictionary<Node> dict;
|
4
|
13 // scope is necessary if you have to parse nested name scope
|
2
|
14 // private PropertyListScope<Node> scope;
|
4
|
15
|
1
|
16 public PropertyListParser(String string,
|
|
17 PropertyListNodeFactory<Node> lf) {
|
|
18 this.lf = lf;
|
2
|
19 initialize();
|
3
|
20 scanner.set(string);
|
1
|
21 }
|
2
|
22
|
4
|
23 public PropertyListParser(PropertyListNodeFactory<Node> lf) {
|
|
24 this.lf = lf;
|
|
25 initialize();
|
|
26 }
|
|
27
|
1
|
28
|
|
29 public void initReservedWord() {
|
|
30 dict.reserve("=",TokenID.Assign);
|
|
31 dict.reserve(",",TokenID.Comma);
|
|
32 dict.reserve(";",TokenID.Semicolon);
|
|
33 dict.reserve("(",TokenID.Paren);
|
|
34 dict.reserve(")",TokenID.CloseParen);
|
|
35 dict.reserve("{",TokenID.CurParen);
|
|
36 dict.reserve("}",TokenID.CloseCurParen);
|
|
37 dict.reserve("true",TokenID.True);
|
|
38 dict.reserve("false",TokenID.False);
|
|
39 }
|
|
40
|
|
41 public void initialize() {
|
|
42 dict = new Dictionary<Node>();
|
2
|
43 // scope = new PropertyListScope<Node>(null,dict);
|
1
|
44 initReservedWord();
|
|
45 scanner = new PropertyListScanner<Node>(dict);
|
|
46 }
|
|
47
|
|
48 public Node parse() {
|
4
|
49 if (scanner==null) return null; // internal error
|
|
50 if (scanner.cb==null) return null; // nothing to do
|
1
|
51 nextToken();
|
|
52 return term();
|
|
53 }
|
|
54
|
|
55 public Node parse(String exp) {
|
|
56 Node n;
|
|
57 scanner = scanner.pushScanner(exp);
|
|
58 n = parse();
|
|
59 scanner = scanner.popScanner();
|
|
60 nextToken = scanner.nextToken;
|
|
61 return n;
|
|
62
|
|
63 }
|
|
64
|
4
|
65 public Node parseFile(String file) {
|
1
|
66 try {
|
|
67 scanner = scanner.pushScannerFile(file);
|
|
68 } catch (FileNotFoundException e) {
|
|
69 error("Can't open "+file);
|
4
|
70 return null;
|
1
|
71 }
|
4
|
72 return doParse();
|
1
|
73 }
|
|
74
|
4
|
75 public Node parse(InputStream file) {
|
1
|
76 scanner = scanner.pushScannerFile(file,null);
|
4
|
77 return doParse();
|
1
|
78 }
|
|
79
|
4
|
80 public Node parse(InputStream in, String prompt) {
|
1
|
81 scanner = scanner.pushScannerFile(in,prompt);
|
4
|
82 return doParse();
|
1
|
83 }
|
|
84
|
|
85 public Node doParse() {
|
|
86 Node n;
|
|
87 do {
|
|
88 n=parse();
|
|
89 } while(scanner.hasRemaining());
|
|
90 scanner = scanner.popScanner();
|
|
91 nextToken = scanner.nextToken;
|
|
92 return n;
|
|
93 }
|
|
94
|
3
|
95 /**
|
|
96 * expr1 {} Dictionary
|
|
97 * @return list of node (key,value,key,value)
|
|
98 */
|
1
|
99 public LinkedList<Node> expr1() {
|
|
100 LinkedList<Node> list = new LinkedList<Node>();
|
|
101 expr2(list);
|
|
102 while(nextToken.type == TokenID.Semicolon) {
|
|
103 nextToken();
|
3
|
104 if (nextToken.type==TokenID.CloseCurParen ) return list;
|
1
|
105 expr2(list);
|
|
106 }
|
|
107 return list;
|
|
108 }
|
|
109
|
3
|
110 /**
|
|
111 * expr2
|
|
112 * key = value
|
|
113 */
|
1
|
114 public void expr2(LinkedList<Node>list) {
|
|
115 Node n1 = term();
|
|
116 if (nextToken.type!=TokenID.Assign) {
|
|
117 error("needs assignment");
|
|
118 return;
|
|
119 }
|
3
|
120 nextToken();
|
1
|
121 Node n2 = term();
|
|
122 list.add(n1); list.add(n2);
|
|
123 return;
|
|
124 }
|
|
125
|
3
|
126 /**
|
|
127 * expr3 Array
|
|
128 * @return list of node
|
|
129 */
|
1
|
130 public LinkedList<Node> expr3() {
|
|
131 LinkedList<Node>list = new LinkedList<Node>();
|
|
132 Node n1 = term();
|
|
133 list.add(n1);
|
|
134 while (nextToken.type==TokenID.Comma) {
|
|
135 Node n2 = term();
|
3
|
136 if (nextToken.type==TokenID.CloseParen) return list;
|
1
|
137 list.add(n2);
|
|
138 }
|
|
139 return list;
|
|
140 }
|
|
141
|
|
142 protected Node makeVariable(Token<Node> t) {
|
|
143 Node n;
|
|
144 if ((n=t.value())==null) {
|
|
145 n = lf.variableNode(t.name(),true);
|
|
146 t.setValue(n);
|
|
147 }
|
|
148 // n.token = t;
|
|
149 return n;
|
|
150 }
|
|
151
|
|
152 protected Node term() {
|
|
153 Node n = null;
|
|
154 switch (nextToken.type) {
|
|
155 case Paren: // Array
|
|
156 nextToken();
|
|
157 LinkedList<Node>list1 = expr3();
|
|
158 if (nextToken.type==TokenID.CloseParen) {
|
|
159 } else { // syntax error;
|
|
160 scanner.error(") expected but got "+nextToken);
|
|
161 return lf.trueNode();
|
|
162 }
|
|
163 return lf.arrayNode(list1);
|
|
164 case CurParen: // Dictionary
|
|
165 nextToken();
|
|
166 LinkedList<Node> list = expr1();
|
|
167 if (nextToken.type==TokenID.CloseCurParen) {
|
|
168 } else { // syntax error;
|
|
169 scanner.error("} expected");
|
|
170 }
|
|
171 return lf.dictionaryNode(list);
|
|
172 case NUMBER:
|
|
173 n = lf.numberNode(Integer.parseInt(nextToken.name));
|
|
174 break;
|
|
175 case NULL:
|
|
176 break;
|
|
177 default:
|
|
178 if (nextToken.type.isVariable())
|
|
179 n = makeVariable(nextToken);
|
|
180 else {
|
|
181 // error("Internal ununderstandable term '"+nextToken+"' type: "+nextToken.type+".");
|
|
182 n = makeVariable(nextToken); // skip any
|
|
183 }
|
|
184 }
|
|
185 nextToken();
|
|
186 return n;
|
|
187 }
|
|
188
|
|
189
|
|
190 /*
|
|
191 * Syntactical Short cut for scanner.textToken()
|
|
192 *
|
|
193 * It never returns null, to check EOF,
|
|
194 * nextToken.type==TokenID.NULL
|
|
195 */
|
|
196 public Token<Node> nextToken() {
|
|
197 return nextToken = scanner.nextToken();
|
|
198 }
|
|
199
|
|
200
|
|
201 public void error(String err) {
|
|
202 scanner.error(err);
|
|
203 }
|
|
204 }
|