Mercurial > lcfOS
comparison python/c3/parser.py @ 148:e5263f74b287
Added c3 language frontend initial parser
author | Windel Bouwman |
---|---|
date | Fri, 01 Mar 2013 10:24:01 +0100 |
parents | |
children | 74241ca312cc |
comparison
equal
deleted
inserted
replaced
147:4e79484a9d47 | 148:e5263f74b287 |
---|---|
1 from . import astnodes, lexer, semantics | |
2 from ppci.errors import CompilerException, SourceLocation | |
3 | |
4 # binop precedence for expressions: | |
5 binopPrecs = {'or': 5, 'and': 10, \ | |
6 '<': 20, '>': 20, '==': 20, '<=': 20, '>=': 20, '!=': 20, \ | |
7 '+': 30, '-': 30, '*': 40, '/': 40 } | |
8 | |
9 def getTokenPrecedence(typ): | |
10 if typ in binopPrecs: | |
11 return binopPrecs[typ] | |
12 return -1 | |
13 | |
14 class Parser: | |
15 """ Parses sourcecode into an abstract syntax tree (AST) """ | |
16 def __init__(self, sema, diag): | |
17 self.sema = sema | |
18 self.diag = diag | |
19 def parseSource(self, source): | |
20 self.initLex(source) | |
21 try: | |
22 self.parsePackage() | |
23 except CompilerException as e: | |
24 self.diag.diag(e) | |
25 def Error(self, msg): | |
26 raise CompilerException(msg, self.token.loc) | |
27 def skipToSemiCol(self): | |
28 while not (self.Peak == ';' or self.Peak == 'END'): | |
29 self.NextToken() | |
30 # Lexer helpers: | |
31 def Consume(self, typ): | |
32 if self.Peak == typ: | |
33 return self.NextToken() | |
34 else: | |
35 self.Error('Excected: "{0}", got "{1}"'.format(typ, self.Peak)) | |
36 @property | |
37 def Peak(self): | |
38 return self.token.typ | |
39 @property | |
40 def PeakPrec(self): | |
41 return getTokenPrecedence(self.Peak) | |
42 def hasConsumed(self, typ): | |
43 if self.Peak == typ: | |
44 self.Consume(typ) | |
45 return True | |
46 return False | |
47 def NextToken(self): | |
48 t = self.token | |
49 if t.typ != 'END': | |
50 self.token = self.tokens.__next__() | |
51 return t | |
52 def initLex(self, source): | |
53 self.tokens = lexer.tokenize(source) # Lexical stage | |
54 self.token = self.tokens.__next__() | |
55 | |
56 def parsePackage(self): | |
57 self.Consume('package') | |
58 name = self.Consume('ID') | |
59 self.Consume(';') | |
60 self.sema.handlePackage(name.val, name.loc) | |
61 # TODO: parse uses | |
62 while self.Peak != 'END': | |
63 self.parseTopLevel() | |
64 self.Consume('END') | |
65 | |
66 def parseTopLevel(self): | |
67 is_public = self.hasConsumed('public') | |
68 if self.Peak == 'function': | |
69 self.parseFunctionDefinition(is_public) | |
70 elif self.Peak == 'var': | |
71 self.parseVarDef(is_public) | |
72 | |
73 def parseDesignator(self): | |
74 """ A designator designates an object """ | |
75 name = self.Consume('ID') | |
76 return name | |
77 | |
78 # Type system | |
79 def parseType(self): | |
80 d = self.parseDesignator() | |
81 return d | |
82 | |
83 # Variable declarations: | |
84 def parseVarDef(self, is_public): | |
85 self.Consume('var') | |
86 typ = self.parseType() | |
87 ID = self.Consume('ID') | |
88 self.Consume(';') | |
89 v = Variable(i.name, typename, public=is_public) | |
90 self.curScope.add(v) | |
91 | |
92 # Procedures | |
93 def parseFunctionDefinition(self, is_pub): | |
94 self.Consume('function') | |
95 returntype = self.parseType() | |
96 procname = self.Consume('ID') | |
97 self.Consume('(') | |
98 parameters = [] | |
99 if not self.hasConsumed(')'): | |
100 typ = self.parseType() | |
101 name = self.Consume('ID') | |
102 parameters.append(astnodes.Parameter(name, typ)) | |
103 while self.hasConsumed(','): | |
104 typ = self.parseType() | |
105 name = self.Consume('ID') | |
106 parameters.append(astnodes.Parameter(name, typ)) | |
107 self.Consume(')') | |
108 proctyp = astnodes.FunctionType(parameters, returntype) | |
109 body = self.parseCompoundStatement() | |
110 return astnodes.Procedure(procname, proctyp, body) | |
111 | |
112 # Statements: | |
113 def parseAssignment(self, lval): | |
114 self.Consume('=') | |
115 rval = self.parseExpression() | |
116 return astnodes.Assignment(lval, rval) | |
117 | |
118 def parseProcedureCall(self, procedure): | |
119 self.Consume('(') | |
120 args = [] | |
121 if not self.hasConsumed(')'): | |
122 args.append(self.parseExpression()) | |
123 while self.hasConsumed(','): | |
124 args.append(self.parseExpression()) | |
125 self.Consume(')') | |
126 return ProcedureCall(procedure, args) | |
127 | |
128 def parseLocal(self, t): | |
129 name = self.Consume('ID') | |
130 if self.hasConsumed('='): | |
131 ival = self.parseExpression() | |
132 else: | |
133 ival = None | |
134 self.sema.actOnLocal(t, name, ival) | |
135 def parseLocalDeclaration(self): | |
136 self.Consume('var') | |
137 t = self.parseType() | |
138 self.parseLocal(t) | |
139 while self.hasConsumed(','): | |
140 self.parseLocal(t) | |
141 | |
142 def parseIfStatement(self): | |
143 self.Consume('if') | |
144 self.Consume('(') | |
145 condition = self.parseExpression() | |
146 self.Consume(')') | |
147 truestatement = self.parseStatement() | |
148 if self.hasConsumed('else'): | |
149 els = self.parseStatement() | |
150 return astnodes.IfStatement(condition, truestatement, els) | |
151 return astnodes.IfStatement(condition, truestatement) | |
152 | |
153 def parseWhileStatement(self): | |
154 self.Consume('while') | |
155 self.Consume('(') | |
156 condition = self.parseExpression() | |
157 self.Consume(')') | |
158 statements = self.parseStatement() | |
159 def parseReturnStatement(self): | |
160 self.Consume('return') | |
161 expr = self.parseExpression() | |
162 | |
163 def parseCompoundStatement(self): | |
164 self.Consume('{') | |
165 statements = [self.parseStatement()] | |
166 while self.hasConsumed(';'): | |
167 statements.append(self.parseStatement()) | |
168 self.Consume('}') | |
169 return astnodes.CompoundStatement(statements) | |
170 | |
171 def parseStatement(self): | |
172 # Determine statement type based on the pending token: | |
173 if self.Peak == 'if': | |
174 return self.parseIfStatement() | |
175 elif self.Peak == 'while': | |
176 return self.parseWhileStatement() | |
177 elif self.Peak == '{': | |
178 return self.parseCompoundStatement() | |
179 elif self.Peak == 'var': | |
180 return self.parseLocalDeclaration() | |
181 elif self.Peak == 'return': | |
182 return self.parseReturnStatement() | |
183 elif self.Peak == 'ID': | |
184 # Assignment or procedure call | |
185 designator = self.parseDesignator() | |
186 if self.Peak == '(': | |
187 return self.parseProcedureCall(designator) | |
188 elif self.Peak == '=': | |
189 return self.parseAssignment(designator) | |
190 self.Error('Unable to determine statement') | |
191 | |
192 # Parsing expressions: | |
193 def parseExpression(self): | |
194 return self.parseBinopRhs(self.parsePrimary(), 0) | |
195 def parsePrimary(self): | |
196 if self.hasConsumed('('): | |
197 e = self.parseExpression() | |
198 self.Consume(')') | |
199 return e | |
200 elif self.Peak == 'NUMBER': | |
201 val = self.Consume('NUMBER') | |
202 return astnodes.Constant(val, val) | |
203 elif self.Peak == 'ID': | |
204 d = self.parseDesignator() | |
205 return d | |
206 self.Error('Expected NUM, ID or (expr), got {0}'.format(self.Peak)) | |
207 | |
208 def parseBinopRhs(self, lhs, min_prec): | |
209 while self.PeakPrec >= min_prec: | |
210 op_prec = self.PeakPrec | |
211 op = self.Consume(self.Peak) | |
212 rhs = self.parsePrimary() | |
213 while self.PeakPrec > op_prec: | |
214 rhs = self.parseBinopRhs(rhs, self.PeakPrec) | |
215 lhs = astnodes.Binop(lhs, op, rhs) | |
216 return lhs | |
217 |