1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
|
package com.seaofnodes.simple.parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.CompUnit;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import com.seaofnodes.simple.util.Ary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Stack;
/** Source-unit entry point and shared cursor, scope, and IR utilities.
* Grammar productions live in the package-local components below. */
public class Parser {
final Statements statements = new Statements(this);
final ControlFlow controlflow = new ControlFlow(this);
final Expressions expressions = new Expressions(this);
final Declarations declarations = new Declarations(this);
final Types types = new Types(this);
final Functions functions = new Functions(this);
final Structs structs = new Structs(this);
final Allocations allocations = new Allocations(this);
final References references = new References(this);
final Conversions conversions = new Conversions(this);
// Compile driver
public final CodeGen _code;
// The Lexer. Thin wrapper over a byte[] buffer with a cursor.
Lexer _lexer;
// Class-object name, distinct from its instance type.
public static final String CLZ = "class:";
// Source file for compilation
public CompUnit _ref;
// Current classname being parsed
String _nestedType;
/**
* Current ScopeNode - ScopeNodes change as we parse code, but at any point of time
* there is one current ScopeNode. The reason the current ScopeNode can change is to do with how
* we handle branching. See {@link Statements#parseIf()}.
* <p>
* Each ScopeNode contains a stack of lexical scopes, each scope is a symbol table that binds
* variable names to Nodes. The top of this stack represents current scope.
* <p>
* We keep a list of all ScopeNodes so that we can show them in graphs.
* @see Statements#parseIf()
* @see #_xScopes
*/
public ScopeNode _scope;
/**
* List of keywords disallowed as identifiers
*/
public static final HashSet<String> KEYWORDS = new HashSet<>(){{
add("bool");
add("break");
add("byte");
add("continue");
add("else");
add("f32");
add("f64");
add("false");
add("flt");
add("i16");
add("i32");
add("i64");
add("i8");
add("if");
add("int");
add("new");
add("null");
add("return");
add("struct");
add("true");
add("u1");
add("u16");
add("u32");
add("u8");
add("while");
}};
/**
* We clone ScopeNodes when control flows branch; it is useful to have
* a list of all active ScopeNodes for purposes of visualization of the SoN graph
*/
public final Stack<ScopeNode> _xScopes = new Stack<>();
// Layouts needed by field accesses in this source file. Resolve after
// parsing, since a later local struct declaration can satisfy the use.
public final LinkedHashSet<String> _requiredTypes = new LinkedHashSet<>();
// Mapping from a type name to a Type. The string name matches
// `type.str()` call.
public static final HashMap<String, Type> TYPES = new HashMap<>();
public static final HashMap<String, Type> INIT_TYPES =
new HashMap<>() {{
put("bool",TypeInteger.U1 );
put("byte",TypeInteger.U8 );
put("f32" ,TypeFloat .F32);
put("f64" ,TypeFloat .F64);
put("flt" ,TypeFloat .F64);
put("i16" ,TypeInteger.I16);
put("i32" ,TypeInteger.I32);
put("i64" ,TypeInteger.BOT);
put("i8" ,TypeInteger.I8 );
put("int" ,TypeInteger.BOT);
put("u1" ,TypeInteger.U1 );
put("u16" ,TypeInteger.U16);
put("u32" ,TypeInteger.U32);
put("u8" ,TypeInteger.U8 );
put("val" ,Type.TOP); // Marker type, indicates type inference
put("var" ,Type.BOTTOM); // Marker type, indicates type inference
}};
public static class ParseException extends RuntimeException {
public final Lexer _loc;
ParseException( String msg, Lexer loc ) { super(msg); _loc = loc; }
}
public Parser(CodeGen code ) {
_code = code;
}
public Ary<FRefNode> parse( CompUnit ref ) {
assert _scope == null && statements._breakScope == null && statements._continueScope == null && functions._returnScope == null;
assert references._frefs == null;
_lexer = new Lexer(ref);
references._frefs = new Ary<>(FRefNode.class);
_requiredTypes.clear();
// Starting Scope has control, memory, initial arguments
_scope = new ScopeNode();
_scope.define(ScopeNode.CTRL, Type.CONTROL , false, null, _lexer);
_scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null, _lexer);
// Track active scopes for Graph display
_xScopes.push(_scope);
ctrl(_code.XCTRL);
mem(con(TypeMem.BOT));
// File-level struct declaration
_ref = ref;
_nestedType = ref._cname;
String typeName = addClzPrefix(ref._cname);
// Parse whole file, which is a struct-level class initializer
structs.parseInitializer( true, typeName );
if( !_lexer.isEOF() ) throw _errorSyntax("unexpected");
Ary<FRefNode> frefs = references.resolve();
// The class being published
ref._clz = (TypeStruct)TYPES.get(typeName);
// Clean up and reset
_xScopes.pop();
_scope.kill();
_scope = null;
references._frefs = null;
return frefs;
}
@Override
public String toString() { return _lexer==null ? "" : _lexer.toString(); }
// Debugging utility to find a Node by index
public Node f(int nid) { return _code.f(nid); }
public static String addClzPrefix( String x ) {
assert !x.startsWith(CLZ);
return (CLZ+x).intern();
}
public static boolean startsClzPrefix( String s ) {
return s.startsWith(CLZ);
}
public static boolean isPublicClassName( String name ) {
if( startsClzPrefix(name) ) name = name.substring(CLZ.length());
return !name.isEmpty() && name.charAt(0)!='_' && name.indexOf("._")==-1;
}
// Read and set CTRL
Node ctrl() { return _scope.ctrl(); }
<N extends Node> N ctrl(N n) { return _scope.ctrl(n); }
// Read and set Memory
Node mem() { return _scope.mem(1); }
void mem(Node mem) { _scope.mem(mem); }
// Structural label for precise memory Phis. The '$' prefix cannot be
// referenced in Simple source.
public static String memName(int alias) { return ("$"+alias).intern(); }
// Ordinary parsing carries one whole-memory value. Alias partitioning is
// graph optimization, not parser state.
void storeMem( Node st, int alias, Node prior ) {
mem(alias==1 ? st : mergeAlias(prior,alias,st));
}
// A semantic graph partition representing the complete memory after a
// precise alias update. ScopeNode treats this as one opaque value.
Node mergeAlias(Node bulk, int alias, Node precise) {
assert bulk != null : "Missing bulk memory for alias #"+alias;
MemMergeNode merge = new MemMergeNode(null,bulk);
merge.alias(alias,precise);
return peep(merge);
}
ConstantNode con( long con ) { return _code.con(con); }
ConstantNode con( Type t ) { return _code.con(t ); }
void classInit(Lexer site, Node ptr) {
// Ordinary load machinery resolves the flag alias and offset when
// the receiver is still a forward reference.
Field flag = ptr._type instanceof TypeMemPtr tmp ? tmp._obj.field("$init") : null;
int alias = flag==null ? 1 : flag._alias;
Node ready = new LoadNode(site,"$init",alias,null,mem(),ptr,fldoff(ptr,"$init")).peephole();
CLInitNode init = new CLInitNode(site,ctrl(),mem(),ptr,ready).peephole().keep();
ctrl(new CProjNode(init,0,ScopeNode.CTRL).peephole());
Node bulk = new ProjNode(init,1,ScopeNode.MEM0).peephole().keep();
flag = init.flag();
mem(flag==null ? bulk : mergeAlias(bulk,flag._alias,
new ProjNode(init,2,memName(flag._alias)).peephole()));
bulk.unkeep();
init.unkeep();
}
// Field offset for a known type (but unknown offset)
public static ConFldOffNode off( TypeStruct ts, String fname ) {
return (ConFldOffNode)(new ConFldOffNode(ts,fname).peephole());
}
// Field offset for an unknown type
public Node fldoff( Node n, String name ) {
if( n._type instanceof TypeMemPtr ptr && ptr._obj._fref )
_requiredTypes.add(ptr._obj._name);
return new FldOffNode(n,name,loc()).peephole();
}
public Node peep( Node n ) {
// Peephole, then improve with lexically scoped guards
return _scope.upcastGuard(n.peephole());
}
// Parse a string or null
//////////////////////////////////
// Utilities for lexical analysis
// Return true and skip if "syntax" is next in the stream.
boolean match (String syntax) { return _lexer.match (syntax); }
// Match must be "exact", not be followed by more id letters
boolean matchx(String syntax) { return _lexer.matchx(syntax); }
boolean matchOp(char c0 ) { return _lexer.matchOp(c0); }
boolean matchOpx(char c0, char c1) { return _lexer.matchOpx(c0,c1); }
// Return true and do NOT skip if 'ch' is next
boolean peek(char ch) { return _lexer.peek(ch); }
// ch is +/- 1, means oper++ or oper-- means postfix
static boolean postfix(char ch) {
return (byte)ch== 1 || (byte)ch== -1;
}
public int pos() { return _lexer._position; }
int pos(int pos) {
int old = _lexer._position;
_lexer._position = pos;
return old;
}
Type posT(int pos) { _lexer._position = pos; return null; }
// Source code location
Lexer loc() { return new Lexer(_lexer); }
// Require and return an identifier
String requireId() {
String id = _lexer.matchId();
if (id != null && !KEYWORDS.contains(id) ) return id.intern();
throw error("Expected an identifier, found '"+id+"'");
}
String matchId() {
int old = pos();
String id = _lexer.matchId();
if( id==null ) return null;
if( !KEYWORDS.contains(id) ) return id;
pos(old);
return null;
}
// Require an exact match
Parser require(String syntax) { require(null, syntax); return this; }
<N> N require(N n, String syntax) {
if (match(syntax)) return n;
throw errorSyntax(syntax);
}
ParseException errorSyntax(String syntax) { return _errorSyntax("expected `"+syntax+"`"); }
private ParseException _errorSyntax(String msg) {
return error("Syntax error, "+msg+" but found `" + _lexer.getAnyNextToken()+"`");
}
ParseException error(String msg) { return error(msg,_lexer); }
public static ParseException error(String msg, Lexer loc) { return new ParseException(msg,loc); }
}
|