148
|
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
|