def parse_integer(self, value: str, base): value = value.replace('_', '') if value.lower().endswith('l'): value = int(value[:-1], base) type = LONG_TYPE if value > 0x7FFFFFFFFFFFFFFF or value < -0x8000000000000000: raise CompileException('int out of range {}'.format(value)) else: value = int(value, base) if value > 0x7FFFFFFF or value < -0x80000000: raise CompileException('int out of range {}'.format(value)) type = INT_TYPE return Literal(value, type)
def class_creation(self, ctx: PlayParser.ClassCreationContext) -> Expr: cls = lookup_class(ctx.classType().IDENTIFIER().getText(), self.current_class) if cls.is_interface: raise CompileException( "Cannot instantiate interface {}".format(cls)) if cls.is_abstract: raise CompileException( "Cannot instantiate abstract class {}".format(cls)) arguments = self.expression_list(ctx.expressionList()) types = [arg.type for arg in arguments] return ClassCreation(cls, cls.lookup_method('<init>', types), arguments)
def enter(self, value: Variable): current = self.scopes[-1] old: Variable = current.get(value.name) if old and old.type != value.type: raise CompileException('Redefine variable {} with different type {}'.format(old.name, value.type)) else: current[value.name] = value
def lookup_field(self, name: str, silence=False): # type: (str) -> Field for field in self.inherited_fields: if field.name == name: return field if not silence: raise CompileException('Cannot find field {}'.format(name))
def while_statement(self, ctx: PlayParser.WhileStatementContext) -> Statement: expr = self.expression(ctx.expression()) if not expr.type == BOOLEAN_TYPE: raise CompileException( "while expression must be boolean expression") return WhileStatement(expr, self.block(ctx.block()))
def enter_field(self, cls: Class, ctx: PlayParser.FieldDeclarationContext): if cls.is_interface or cls.is_native: raise CompileException( "Interface/native class {} cannot have fields".format(cls)) field = Field(ctx.variable().IDENTIFIER().getText(), cls, build_type(ctx.variable().typeName(), cls)) Option().nodes[field] = ctx cls.put_field(field)
def put_method(self, method): methods = self.static_methods if method.is_static else self.methods for m in methods: if m.has_same_signature(method): raise CompileException( 'Duplicated method {} in class {}'.format( method.name, self)) methods.append(method)
def translate_field(self, field: Field): ctx: PlayParser.FieldDeclarationContext = self.nodes[field] if ctx.expression(): expr = self.expression(ctx.expression()) if not can_cast_to(expr.type, field.type): raise CompileException( "Cannot assign to field {} with type {}".format( field.name, expr.type)) field.initializer = expr
def cast_expression(self, ctx: PlayParser.CastExpressionContext) -> Expr: if ctx.unaryExpression(): return self.unary_expression(ctx.unaryExpression()) expr = self.not_super(self.cast_expression(ctx.castExpression())) type = build_type(ctx.typeName(), self.current_class) if not can_cast_to(expr.type, type, force=True): raise CompileException('Cannot cast type {} to {}'.format( expr.type, type)) return CastExpr(expr, type)
def return_statement(self, ctx: PlayParser.ReturnStatementContext) -> Statement: expr = self.expression(ctx.expression()) if ctx.expression() else None return_type = expr.type if expr else None if not can_cast_to(return_type, self.current_method.return_type): raise CompileException( 'Cannot return type {} for method {}, expect {}'.format( return_type, self.current_method.name, self.current_method.return_type)) return ReturnStatement(expr)
def enter_class(self, package: Package, src: SourceFile, ctx: PlayParser.ClassDeclarationContext): cls = Class(ctx.IDENTIFIER().getText(), package, src) cls.is_interface = ctx.INTERFACE() is not None cls.is_native = ctx.NATIVE() is not None if cls.is_native and cls.is_interface: raise CompileException('interface {} cannot be native'.format(cls)) Option().nodes[cls] = ctx package.put(cls) SymbolTable().enter_class(cls)
def if_statement(self, ctx: PlayParser.IfStatementContext) -> Statement: # unwrap always condition condition = self.expression(ctx.expression()) if not condition.type == BOOLEAN_TYPE: raise CompileException("if expression must be boolean expression") else_ifs: List[Tuple[Expr, Block]] = [] for else_if_ctx in ctx.elifClause(): cond = self.expression(else_if_ctx.expression()) if not cond.type == BOOLEAN_TYPE: raise CompileException( "if expression must be boolean expression") else_ifs.append((cond, self.block(else_if_ctx.block()))) otherwise = None if ctx.elseClause(): otherwise = self.block(ctx.elseClause().block()) if else_ifs: return IfStatement(condition, self.block(ctx.block()), Block([self.build_else(else_ifs, otherwise)])) else: return IfStatement(condition, self.block(ctx.block()), otherwise)
def lookup_class(name: str, start: Class) -> Class: if name == start.name: return start if name in start.source.imports: return start.source.imports[name] found = start.owner.children.get(name) if isinstance(found, Class): return found if isinstance(PLAY_PACKAGE.children.get(name), Class): return PLAY_PACKAGE.children[name] raise CompileException("Cannot find class {}".format(name))
def assign_statement(self, ctx: PlayParser.AssignStatementContext) -> Statement: if ctx.IDENTIFIER(): target = ctx.IDENTIFIER().getText() expr = self.not_super(self.expression(ctx.expression(0))) type = build_type(ctx.typeName(), self.current_class) if ctx.typeName() else None if type: if not can_cast_to(expr.type, type): raise CompileException( 'Cannot assign type {} to {}'.format(expr.type, type)) else: type = expr.type # infer type var = Variable(target, type) self.env.enter(var) return AssignStatement(var, expr) else: var = self.expression(ctx.expression(0)) if not isinstance(var, FieldAccess): raise CompileException('left value must be variable or field') return AssignStatement(var, self.expression(ctx.expression(1)))
def lookup_super_method(self, name: str, arguments): # type: (str, List[Type]) -> Method method = self.superclass.lookup_method(name, arguments) if not method: for interface in self.interfaces: method = interface.lookup_method(name, arguments) if method.is_abstract: method = None if method: return method raise CompileException( 'Cannot find method {} with parameters {}'.format(name, arguments))
def build(self, cls: Class): ctx: PlayParser.ClassDeclarationContext = Option().nodes[cls] if ctx.classTypeList(): for sc in ctx.classTypeList().classType(): superclass = lookup_class(sc.IDENTIFIER().getText(), cls) if superclass.is_native: raise CompileException( 'Cannot inherit from native class {}'.format( superclass)) if superclass.is_interface: cls.interfaces.append(superclass) elif cls.superclass: raise CompileException( "Class {} has more than one superclass".format(cls)) elif superclass.is_native and superclass.qualified_name != 'play.Object': raise CompileException( "Cannot inherit native class {}".format(cls)) else: cls.superclass = superclass obj_cls = PLAY_PACKAGE.children['Object'] if cls != obj_cls and not cls.is_interface and not cls.superclass: cls.superclass = obj_cls
def check_inside_loop(self, statement: Statement, loops: List[LoopStatement] = None): if not statement: return if not loops: loops = [] if isinstance(statement, BreakStatement): if not loops: raise CompileException('break must inside loop') elif isinstance(statement, ContinueStatement): if not loops: raise CompileException('continue must inside loop') elif isinstance(statement, IfStatement): self.check_inside_loop(statement.then, loops) if statement.otherwise: self.check_inside_loop(statement.otherwise, loops) elif isinstance(statement, LoopStatement): self.check_inside_loop(statement.block, [] + [statement]) elif isinstance(statement, Block): for s in statement.statements: self.check_inside_loop(s, loops)
def lookup(self, name, static) -> Expr: for scope in reversed(self.scopes): if name in scope: return scope[name] if not static: field = self.cls.lookup_field(name, silence=True) if field: return FieldAccess(None, field) try: return ClassExpr(lookup_class(name, self.cls)) except CompileException: pass raise CompileException('Cannot find symbol {}'.format(name))
def enter_package(self, package: str) -> Package: symbol = self.__symbols.get(package) if isinstance(symbol, Package): return symbol elif not symbol: owner = self.enter_package(package[:package.rfind('.')] if '.' in package else '') symbol = Package(package, owner) owner.put(symbol) self.__symbols[package] = symbol return symbol else: raise CompileException( "Expect package symbol, but it is {}".format(symbol))
def lookup_method(self, name: str, arguments, is_static=False): # type: (str, List[Type], bool) -> Method methods = self.static_methods if is_static else self.inherited_methods for method in methods: if method.name != name: continue if len(method.parameters) != len(arguments): continue for i, parameter in enumerate(method.parameters): if arguments[i] != parameter.type: break else: return method raise CompileException( 'Cannot find method {} with parameters {}'.format(name, arguments))
def build_type(ctx: PlayParser.TypeNameContext, start: Class) -> Type: if ctx.classType(): return ObjectType( lookup_class(ctx.classType().IDENTIFIER().getText(), start)) if ctx.BOOLEAN(): return BOOLEAN_TYPE if ctx.BYTE(): return BYTE_TYPE if ctx.SHORT(): return SHORT_TYPE if ctx.INT(): return INT_TYPE if ctx.LONG(): return LONG_TYPE if ctx.FLOAT(): return FLOAT_TYPE if ctx.DOUBLE(): return DOUBLE_TYPE raise CompileException("Error")
def primary_expression(self, ctx: PlayParser.PrimaryExpressionContext) -> Expr: if self.current_method.is_static and (ctx.THIS() or ctx.SUPER()): raise CompileException('Cannot use this/super in static method') if ctx.methodCall(): return self.method_call(ctx.methodCall()) if ctx.classCreation(): return self.class_creation(ctx.classCreation()) if ctx.expression(): return self.expression(ctx.expression()) if ctx.THIS(): return self.this_var if ctx.SUPER(): return self.super_var if ctx.IDENTIFIER(): return self.env.lookup(ctx.IDENTIFIER().getText(), self.current_method.is_static) if ctx.literal(): return self.literal(ctx.literal()) never_be_here()
def enter_method(self, cls: Class, ctx: PlayParser.MethodDeclarationContext): name = ctx.IDENTIFIER().getText() if name == cls.name: name = '<init>' method = Method(name, cls) method.is_native = ctx.NATIVE() is not None method.is_static = ctx.STATIC() is not None method.is_abstract = not method.is_native and ctx.block() is None if method.is_native and ctx.block() is not None: raise CompileException( 'Native method {} in class {} cannot have body'.format( method.name, method.owner)) if ctx.parameters(): for v in ctx.parameters().variable(): parameter_type = build_type(v.typeName(), cls) method.parameters.append( Parameter(v.IDENTIFIER().getText(), parameter_type)) if ctx.typeName(): method.return_type = build_type(ctx.typeName(), cls) Option().nodes[method] = ctx cls.put_method(method)
def check(self, current: Class, path: List[Class]): if current in self.visited: return self.visited.add(current) superclasses = current.superclasses for sc in superclasses: try: index = path.index(sc) chain = [] for c in path[index:]: chain.append(c.qualified_name) chain.append(current.qualified_name) chain.append(path[index].qualified_name) raise CompileException( "There's a circular hierarchy dependency: {}".format( ' -> '.join(chain))) except: pass new_path = path + [current] for sc in superclasses: self.check(sc, new_path) self.checked.add(current)
def method_call(self, ctx: PlayParser.MethodCallContext, select: Expr = None) -> Expr: if self.current_method.is_static and ctx.THIS() or ctx.SUPER(): raise CompileException('Cannot use this/super in static method') if select and (ctx.THIS() or ctx.SUPER()): raise CompileException('this() and super() cannot has prefix') arguments = self.expression_list(ctx.expressionList()) types = [arg.type for arg in arguments] if ctx.THIS(): method = self.current_class.lookup_method('<init>', types) elif ctx.SUPER(): if self.current_method.name != '<init>': raise CompileException( 'Cannot call super outside constructor {}'.format( self.current_class)) method = self.current_class.superclass.lookup_method( '<init>', types) if method.is_private: raise CompileException('access private method in {}'.format( self.current_class.superclass)) else: method_name = ctx.IDENTIFIER().getText() if select: if not isinstance(select.type, ObjectType): raise CompileException("cannot lookup method in {}".format( select.type)) if select == self.super_var: method = self.current_class.lookup_super_method( method_name, types) else: method = select.type.cls.lookup_method( method_name, types, isinstance(select.type, ClassType)) if method.is_private and select.type.cls != self.current_class: raise CompileException( 'access private method in {}'.format(select.type)) else: method = self.current_class.lookup_method( method_name, types, is_static=self.current_method.is_static) return MethodCall(select, method, arguments)
def put_field(self, field): for f in self.fields: if f.name == field.name: raise CompileException( 'Duplicated field {} in class {}'.format(field.name, self)) self.fields.append(field)
def not_super(self, expr: Expr) -> Expr: if expr == self.super_var: raise CompileException('Cannot user super') return expr
def parse_bool(self, value: str): if value == 'true': return Literal(True, BOOLEAN_TYPE) if value == 'false': return Literal(False, BOOLEAN_TYPE) raise CompileException('Invalid boolean type')
def assert_type(actual, expect): if not isinstance(expect, (set, list)): expect = [expect] if actual not in expect: raise CompileException('Invalid type {}, expect {}'.format( actual, expect))
def put(self, child): if child.name in self.children and self.children[child.name] != child: raise CompileException('Duplicated symbol {} in package {}'.format( child.name, self.qualified_name)) self.children[child.name] = child