def variable_declaration(self): """variable_declaration : ID (COMMA ID)* COLON type_spec VAR a : INTEGER; b : REAL; """ var_nodes = [ast.Var(self.current_token)] self.eat(TokenType.ID) # while contain ',' just like var a,b,c : INTEGER; while self.current_token.type == TokenType.COMMA: self.eat(TokenType.COMMA) var_nodes.append(ast.Var(self.current_token)) self.eat(TokenType.ID) # : self.eat(TokenType.COLON) # type special int or float type_node = self.type_spec() # [a, INTEGER], [b, INTEGER] [c, float] var_declarations = [ ast.VarDecl(var_node, type_node) for var_node in var_nodes ] return var_declarations
def formal_parameters(self): """ formal_parameters : ID (COMMA ID)* COLON type_spec procedure's params, just like Foo(a, b : int) current process is a, b : int """ param_nodes = [] param_tokens = [self.current_token] self.eat(TokenType.ID) # while contain ',' while self.current_token.type == TokenType.COMMA: self.eat(TokenType.COMMA) # add all ID param_tokens.append(self.current_token) self.eat(TokenType.ID) # eat ':' self.eat(TokenType.COLON) # start params type type_node = self.type_spec() for param_token in param_tokens: var = ast.Var(param_token) param_node = ast.Param(var, type_node) param_nodes.append(param_node) return param_nodes
def p_cond(p): """cond : VAR LT NUM | VAR LE NUM | VAR EQ NUM | VAR NE NUM | VAR GT NUM | VAR GE NUM""" p[0] = comparison_dict[p[2]](ast.Var(p[1]), ast.Num(p[3]))
def parse_funcliteral(self, cur): name = None if self.cur.type == "id": name = ast.Value(self.cur.value) self.consume() args = self.funcargs() block = self.block() func = ast.Func(name, args, block).set_origin(cur) if name: v = ast.Var(str(name.value)) self.addVar(v) return ast.Assign(v, func).set_origin(cur) else: return func
def parse_objectliteral(self, cur): obj = [] if self.maybe("}"): return ast.Value(cur, EspDict()) while not self.maybe("}"): tok = self.cur if tok.type in {"val", "id", "op"}: key = ast.Value(tok.value) elif tok.type == "kw": if tok.value in {"get", "set"}: raise NotImplementedError("get/set") key = ast.Value(tok.value) elif tok.type == "punc": if tok.value == "[": raise NotImplementedError("Computed properties") raise self.error(tok, "Unexpected punctuation") else: raise self.error(tok, f"Unknown token {tok!r}") key = key.set_origin(tok) self.consume() if self.peek("("): # Object method functok = self.cur args = self.funcargs() value = ast.Func(key, args, self.block()).set_origin(functok) elif self.maybe(":"): # Normal property value = self.expr() else: # NOTE: Doesn't work for computed properties value = ast.Var(key.value) obj.append((key, value)) # Should commas be optional? self.maybe(",") return ast.ObjectLiteral(obj).set_origin(cur)
def parse_atom(self): token = self.get_token if token.tag == Token.LPAREN: e = self.parse_expr() self.expect(Token.RPAREN) return e elif token.tag == Token.NUMBER: return E.ArithLit(token.value) elif token.tag == Token.ID: # variable or funcall if self.peek.tag == Token.LPAREN: name = token.value self.expect(Token.LPAREN) args = self.parse_args() return E.FunCall(name, args) else: return E.Var(token.value) else: msg = f"Unexpected token: {token.tag}\n" \ "Expected expression, namely one of LPAREN, " \ "NUMBER or ID" raise ParseError(token, msg)
def variable(self): """variable : ID """ node = ast.Var(self.current_token) self.eat(TokenType.ID) return node
def atom(self): if self.cur is None: return None cur = self.cur val = cur.value ct = cur.type # Exceptions which shouldn't be consumed if ct == "punc" and val in {")", ";", "}", "]"}: return None elif ct == "kw" and val in {"case", "else"}: return None self.consume() if ct == "val": return ast.Value(cur.value).set_origin(cur) elif ct == "str": return self.process_string(cur) elif ct == "id": v = ast.Var(cur.value).set_origin(cur) # Check string call s = self.maybe(type="str") if s: return ast.Call(v, [self.process_string(s)]).set_origin(s) return v elif ct == "punc": if val == "(": if self.maybe(")"): return ast.Tuple([]).set_origin(cur) x = self.semichain() self.expect(")") return semiAsExpr(x) elif val == "{": return self.parse_objectliteral(cur) elif val == "[": return self.parse_listliteral(cur) #elif val == ")": pass #elif val == "}": pass # TODO: dot functions else: raise NotImplementedError(val) elif ct == "op": return self.parse_unary(cur) elif ct == "kw": if val == "if": return self.parse_if(cur) elif val == "proto": return self.parse_proto(cur) elif val == "import": return self.parse_import(cur) elif val == "loop": return self.parse_loop(cur) elif val == "while": cond, bl, th, el = self.parse_while() # Not account for el return ast.Loop(ast.Block( [ast.If(cond, bl, ast.Branch("break"))]), el=el).set_origin(cur) elif val == "for": return self.parse_for(cur) elif val == "switch": return self.parse_switch(cur) elif val in {"break", "continue", "redo"}: return ast.Branch(val).set_origin(cur) # Todo: targeted branches elif val in {"var", "const"}: return semiAsExpr(self.parse_declgroup(cur)) elif val == "function": return self.parse_funcliteral(cur) elif val == "try": raise NotImplementedError("try") else: raise self.error(f"Unimplemented keyword {val}") raise self.error(f"Unknown token {val}")
def parse_declvar(self, mut): name = self.expect(type="id") v = ast.Var(name.value, mut).set_origin(name) self.addVar(v) return v
def parse_proto(self, cur): # TODO: anonymous proto name = self.maybe(type="id") if self.maybe("is"): parent = self.relaxid() else: parent = None self.expect("{") pub = [] priv = [] stat = [] while not self.maybe("}"): qual = self.maybe({"public", "var", "private", "static"}) member = self.maybe(type="id") if self.maybe("("): params = self.listing(",", self.lvalue) self.expect(")") body = self.block() members = [ ast.Func(member.value, params, body).set_origin(member) ] else: members = [ast.Var(member.value).set_origin(member)] while self.maybe(","): mem = self.maybe(type="id") if mem is None: raise RuntimeError("None name") members.append(ast.Var(mem, mem.value)) if not qual or qual.value in {"public", "var"}: pub += members elif qual.value == "private": priv += members elif qual.value == "static": stat += members else: raise RuntimeError("Shouldn't happen") while self.maybe(";"): pass name = name.value if name else None p = ast.Proto(name, parent, pub, priv, stat).set_origin(cur) if name is None: return p else: v = ast.Var(name, mutable=False) self.addVar(v) return ast.Assign(cur, v, p)
def statement(self): if self.match("let"): line = self.pop().line ty = self.type() if self.match_val("="): name = ty[1] ty = None #this is where more complete type parsing would happen #for the time being, just error out elif self.match("ident"): name = self.pop().val else: raise ValueError( f"expected type in var declaration on line {line}, saw {self.tl[0].val}" ) self.consume( "=", f"expected '=' in let declaration on line {line}, saw {self.tl[0].val}" ) ex = self.expr() if ty != None: return ast.Let(name, (ty, None), ex) else: return ast.Let(name, None, ex) elif self.match("var"): line = self.pop().line ty = self.type() if self.match_val("="): name = ty[0] ty = None #this is where more complete type parsing would happen #for the time being, just error out elif self.match("ident"): name = self.pop().val else: raise ValueError( f"expected type in var declaration on line {line}, saw {self.tl[0].val}" ) self.consume( "=", f"expected '=' in var declaration on line {line}, saw {self.tl[0].val}" ) ex = self.expr() return ast.Var(name, ty, ex) elif self.match("if"): line = self.pop().line ex = self.expr() thenstmnts = self.statementlist("if statement", line) if not self.match("else"): elsestmnts = None else: line = self.pop().line elsestmnts = self.statementlist("else statement", line) return ast.If(ex, thenstmnts, elsestmnts) elif self.match('return'): self.pop() returning = self.expr() return ast.Return(returning) elif self.match('while'): line = self.pop().line condition = self.expr() body = self.statementlist("while body", line) return ast.While(condition, body) else: lhs = self.expr() if self.match_val('=') or self.match_val('+=') or self.match_val( '-=') or self.match_val('*=') or self.match_val( '/=') or self.match_val('%='): op = self.pop().val rhs = self.expr() if (op[0] != '='): rhs = ast.Binary(lhs, op[0], rhs) return ast.Assign(lhs, rhs) else: return lhs
def p_expr_4(p): """expr : VAR""" p[0] = ast.Var(p[1])
def p_comm_2(p): """comm : VAR ASSIGN expr""" p[0] = ast.Assign(ast.Var(p[1]), p[3])