def __init__(self, is_intaractive_run, an_errhandler): """ EvalVisitorを初期化して応答する. """ self.is_test = False self.is_intaractive_run = is_intaractive_run self.errhandler = an_errhandler self.scopes = ScopeList() self.is_break = False self.is_return = False self.return_value = UnitXObject(value=None, varname=None, unit=None, token=None, is_none=True) this_dir, _ = os.path.split(__file__) data_path = os.path.join(this_dir, Constants.SYSTEM_UNIT_DATA) self.unit_manager = UnitManager(data_path) # Sets a database(data/unit_table.dat) for calculating units. self.stdlib = Stdlib() self.NULL_UNITX_OBJ = UnitXObject(value=None, varname=None, is_none=True, unit=Unit(), token=None) # # Sets a mediator to each classes for a management, # because this class is a mediator class. # Also, UnitXObject, Unit, Scope classes have to create many new instances. # So, We set a mediator by using classmethod. # self.scopes.set_mediator(self) self.unit_manager.set_mediator(self) self.stdlib.set_mediator(self) UnitXObject.set_mediator(self) Unit.set_mediator(self) Scope.set_mediator(self) DefinedFunction.set_mediator(self) UnitXObject.manager = self.get_unit_manager() UnitXObject.scopes = self.get_scopes()
class EvalVisitor(UnitXVisitor, Mediator): """ UnitXの構文木をたどり,その振る舞いを行うクラス. UnitXParserから,このクラスにある各visit関数が呼ばれ,実行される.それぞれの構文ごとに振る舞いが行われ,それが言語としてのアウトプットとなる. また,このファイルを呼び出す側のUnitXParserは,Grammarファイル(UnitX.g4)のBNFを元にANTLRが生成したクラスであるため,UnitX.g4のBNFとこのクラスの関数は対応関係にある.例えば,<statement>というルールは,このクラスのvisitStatement(ctx)に対応する. Attributes: scopes: すべてのスコープ情報が入っているリスト """ def __init__(self, is_intaractive_run, an_errhandler): """ EvalVisitorを初期化して応答する. """ self.is_test = False self.is_intaractive_run = is_intaractive_run self.errhandler = an_errhandler self.scopes = ScopeList() self.is_break = False self.is_return = False self.return_value = UnitXObject(value=None, varname=None, unit=None, token=None, is_none=True) this_dir, _ = os.path.split(__file__) data_path = os.path.join(this_dir, Constants.SYSTEM_UNIT_DATA) self.unit_manager = UnitManager(data_path) # Sets a database(data/unit_table.dat) for calculating units. self.stdlib = Stdlib() self.NULL_UNITX_OBJ = UnitXObject(value=None, varname=None, is_none=True, unit=Unit(), token=None) # # Sets a mediator to each classes for a management, # because this class is a mediator class. # Also, UnitXObject, Unit, Scope classes have to create many new instances. # So, We set a mediator by using classmethod. # self.scopes.set_mediator(self) self.unit_manager.set_mediator(self) self.stdlib.set_mediator(self) UnitXObject.set_mediator(self) Unit.set_mediator(self) Scope.set_mediator(self) DefinedFunction.set_mediator(self) UnitXObject.manager = self.get_unit_manager() UnitXObject.scopes = self.get_scopes() # # Implementations of Mediator are below. # ======================================= # def set_parser(self, parser): self.parser = parser def get_parser(self): return self.parser def get_scopes(self): return self.scopes def get_errhandler(self): return self.errhandler def get_is_intaractive_run(self): return self.is_intaractive_run def get_unit_manager(self): return self.unit_manager def set_errlistener(self, errlistener): self._listener = errlistener def get_errlistener(self): return self._listener def is_context_in_ancestor(self, crr_ctx, seeking_ctx): """ Args: Return: """ if crr_ctx.parentCtx: if isinstance(crr_ctx.parentCtx, seeking_ctx): return True return self.is_context_in_ancestor(crr_ctx.parentCtx, seeking_ctx) return False def build_stdlib(self): """ """ for func in self.stdlib.funcs: #func.code = self.get_errlistener().get_code() var_unitx_obj = UnitXObject(value=None, varname=func.name, unit=Unit()) unitx_obj = UnitXObject(value=func, varname=func.name, unit=Unit()) var_unitx_obj.assign(unitx_obj, None) def visit(self, tree): """ """ self.build_stdlib() # Sets a standard library if self.is_intaractive_run: self.get_errlistener().reset_exit() super(EvalVisitor, self).visit(tree) # # Implementations of UnitXVisitor are below. # ======================================= # def visitProgram(self, ctx): """ Just visiting child nodes of UnitX syntax. ALSO, THIS <program> RULE IS A STARTING POINT OF UNITX PARSER. """ return self.visitChildren(ctx) def visitTypeDeclaration(self, ctx): """ Just visiting child nodes of UnitX syntax.""" return self.visitChildren(ctx) def visitFunctionDeclaration(self, ctx): """ 関数宣言をする. """ if self._is_passing_block(): return func_token = ctx.Identifier().getSymbol() func_name = func_token.text func_args = self.visitFormalParameters(ctx.formalParameters()) code = self.get_errlistener().get_code() def_func = DefinedFunction(func_name, func_args, ctx, code) var_unitx_obj = UnitXObject(value=None, varname=func_name, unit=Unit(), token=func_token) unitx_obj = UnitXObject(value=def_func, varname=func_name, unit=Unit(), token=func_token) var_unitx_obj.assign(unitx_obj, None) return def visitFormalParameters(self, ctx): """ """ if ctx.formalParameterList(): return self.visitFormalParameterList(ctx.formalParameterList()) return [] def visitFormalParameterList(self, ctx): """ """ return [self.visitFormalParameter(a_param) for a_param in ctx.formalParameter()] def visitFormalParameter(self, ctx): """ varname -- A key registing in a scope """ var_token = ctx.Identifier().getSymbol() variable = UnitXObject(value = None, varname = var_token.text, unit=Unit(), token=var_token) if ctx.expression(): default_value = self.visitExpression(ctx.expression()) else: default_value = None return [variable, default_value] def _is_passing_block(self): """ Returns whether ignored block exists for intaractive programing. Ignored block is like bellow. example: $ unitx unitx> rep(i,5) { ...... } In an intaractive programing, we have to ignore runtime errors in a block statement. So, we control it by turning on a variable "is_ignored_block" in EvalError. And the controled result is turned off by a user input an empty string. """ return self.is_intaractive_run and self.errhandler.is_ignored_block def visitBlock(self, ctx): """ """ a_parent, a_grandparent = ctx.parentCtx, ctx.parentCtx.parentCtx is_special_block = (isinstance(a_grandparent, UnitXParser.RepStatementContext) or isinstance(a_grandparent, UnitXParser.IfStatementContext) or isinstance(a_parent, UnitXParser.FunctionDeclarationContext)) # # If the block is "rep", "if", and "fucntion" statements, # don't create a scope in this visitBlock function because of initializing it in another function. # Also, the block is that a "block" statement such as '{' .... '}' must create a scope. # if is_special_block: self.visitChildren(ctx) else: self.scopes.new_scope() self.visitChildren(ctx) self.scopes.del_scope() return def visitBlockStatement(self, ctx): """ Just visiting child nodes of UnitX syntax.""" return self.visitChildren(ctx) def visitStatement(self, ctx): """ それぞれの文を辿って,応答する. """ if self.is_intaractive_run: if self.get_errlistener().is_exit(): return if self.is_break or self.is_return: return if ctx.block(): self.visitBlock(ctx.block()) elif ctx.repStatement(): self.visitRepStatement(ctx.repStatement()) elif ctx.ifStatement(): self.visitIfStatement(ctx.ifStatement()) elif ctx.expressionStatement(): self.visitExpressionStatement(ctx.expressionStatement()) elif ctx.start.type == UnitXLexer.RETURN: if self.is_context_in_ancestor(ctx, UnitXParser.FunctionDeclarationContext): self.is_return = True if ctx.expression(): self.return_value = self.visitExpression(ctx.expression()) else: msg = Constants.SYNTAX_ERR_RETURN_OUTSIDE self.get_parser().notifyErrorListeners(msg, ctx.start, Exception(msg)) elif ctx.start.type == UnitXLexer.BREAK: if self.is_context_in_ancestor(ctx, UnitXParser.RepStatementContext): self.is_break = True else: msg = Constants.SYNTAX_ERR_BREAK_OUTSIDE self.get_parser().notifyErrorListeners(msg, ctx.start, Exception(msg)) elif ctx.start.type == UnitXLexer.CONTINUE: pass #not yet elif ctx.printStatement(): self.visitPrintStatement(ctx.printStatement()) elif ctx.dumpStatement(): self.visitDumpStatement(ctx.dumpStatement()) elif ctx.assertStatement(): self.visitAssertStatement(ctx.assertStatement()) elif ctx.borderStatement(): self.visitBorderStatement(ctx.borderStatement()) else: if not self.is_intaractive_run: raise Exception("Syntax error. EvalVisitor#visitStatement") # Never happen. return def visitBorderStatement(self, ctx): """ 線を出力して応答する(borderとして3~10個の-を使える). ex: ---, ----, ----- """ sys.stdout.write(ctx.start.text + '\n') return # Visit a parse tree produced by UnitXParser#repStatement. def visitRepStatement(self, ctx): """ 与えられた回数の繰り返し処理を実行し,応答する. また,繰り返し処理の前にスコープのメモリ領域を確保し,繰り返し処理の後にそのスコープのメモリ領域を解放する.すなわち,スコープを管理する. ex: rep(i,5){...}, rep(i,[1,2,3]){...}, rep(i,['B','KB','MB']) """ if self._is_passing_block(): return #Clean! var_obj, end_control = self.visitRepControl(ctx.repControl()) end_value = end_control.get_value() if isinstance(end_value, int): repeat_list = [UnitXObject(value=x,varname=None,unit=Unit()) for x in range(end_value)] else: repeat_list = end_value self.scopes.new_scope() for unitx_obj in repeat_list: var_obj.assign(unitx_obj, None) self.visitStatement(ctx.statement()) self.scopes.del_scope() self.is_break = False return def visitIfStatement(self, ctx): """ 与えられたexpressionの結果 BNF: 'if' parExpression statement ('else' statement)? """ unitx_obj = self.visitParExpression(ctx.parExpression()) is_run_ifStatement = unitx_obj.get_value() if is_run_ifStatement: if self._is_passing_block(): return self.visitStatement(ctx.statement(i=0)) else: if ctx.getChildCount() > 3: if self._is_passing_block(): return self.visitStatement(ctx.statement(i=1)) else: pass # do nothing return def visitExpressionStatement(self, ctx): """ Just visiting child nodes of UnitX syntax.""" unitx_obj = self.visitExpression(ctx.expression()) if self.get_errhandler().is_ignored_block: return if self.is_intaractive_run: if unitx_obj.is_none or not unitx_obj.get_value(): return Util.printf(self.is_test, self.visitExpressionStatement, unitx_obj.get_unit_value()) return def visitPrintStatement(self, ctx): """ 与えられたexpressionのUnitXObjectたちを出力して,応答する. printモードでself._print_variablesを起動する. """ self._print_variables(ctx, 'print') return def visitDumpStatement(self, ctx): """ 与えられたexpressionのUnitXObjectたちを出力して,応答する. dumpモードでself._print_variablesを起動する. """ self._print_variables(ctx, 'dump') return def _print_variables(self, ctx, mode): """ 与えられたexpressionのUnitXObjectたちを出力して,応答する. dumpモードでは,変数名とその変数に束縛されたUnitXObjectの値を出力する. printモードでは,UnitXObjectの値のみを出力する. """ #print self.scopes unitx_strs = [] for an_expr in ctx.expression(): unitx_obj = self.visitExpression(an_expr) if unitx_obj.is_none: dump_line = 'NULL' #None else: varname = unitx_obj.varname if varname and mode == 'dump': dump_line = "%s: %s" % (varname, unitx_obj.get_unit_value()) else: dump_line = unitx_obj.get_unit_value() unitx_strs.append(dump_line) sys.stdout.write(' '.join(unitx_strs) + '\n') return def visitAssertStatement(self, ctx): """ 与えられたexpressionの if False or None """ if not ctx.expression(): return unitx_obj = self.visitExpression(ctx.expression()) if not unitx_obj.get_value(): msg = Constants.ASSERT_ERR self.get_parser().notifyErrorListeners(msg, unitx_obj.token, Exception(msg)) return def visitExpressionList(self, ctx): """ Just visiting child nodes of UnitX syntax.""" return [self.visitExpression(an_expr) for an_expr in ctx.expression()] def visitParExpression(self, ctx): """ LPARENとRPARENは無視して,expressionのみを辿って,結果を応答する. """ return self.visitExpression(ctx.expression()) def visitParExpressionList(self, ctx): """ LPARENとRPARENは無視して,expressionListのみを辿って,結果を応答する. """ return self.visitExpressionList(ctx.expressionList()) def visitRepControl(self, ctx): """ Visit a parse tree produced by UnitXParser#repControl. ex: [i,5], [i,[1,2,3]] """ varname = ctx.Identifier().getText() return [UnitXObject(value=None, varname=varname, unit=Unit()), self.visitEndRep(ctx.endRep())] def visitEndRep(self, ctx): """ It checks a value of expr to be able to cast by int(). """ an_expr = self.visitExpression(ctx.expression()) # You must check value of expr to be able to cast by int(). return an_expr def __find_called_func(self, ctx): if ctx == None: return None if isinstance(ctx, UnitXParser.FunctionDeclarationContext): func_name = ctx.Identifier().getSymbol().text found_scope = self.get_scopes().peek().find_scope_of(func_name) called_func = found_scope[func_name].get_value() return called_func return self.__find_called_func(ctx.parentCtx) def visitExpression(self, ctx): """ UnitXObject同士を計算した結果を返す. return: UnitXObject """ if ctx.expression(i=0): x = self.visitExpression(ctx.expression(i=0)) # x,y: UnitXObject if ctx.start.type == UnitXLexer.INC: unitx_obj = x.increment() elif ctx.start.type == UnitXLexer.DEC: unitx_obj = x.decrement() elif ctx.getChild(i=1).getSymbol().type == UnitXLexer.LPAREN: # # Calls a function. # x: A UnitXObject of called function. # called_func_name = x.varname called_args = [] if ctx.expressionList(): called_args = self.visitExpressionList(ctx.expressionList()) found_scope = self.get_scopes().peek().find_scope_of(called_func_name) if found_scope: def_func = found_scope[called_func_name].get_value() self.get_scopes().new_scope() called_func = self.__find_called_func(ctx) self.get_errlistener().set_last_called_func(x.get_value()) unitx_obj = def_func.call(called_args, x, called_func) self.get_errlistener().set_last_called_func(None) self.get_scopes().del_scope() else: msg = Constants.NAME_ERR % called_func_name self.get_parser().notifyErrorListeners(msg, x.token, Exception(msg)) unitx_obj = UnitXObject(value=None, varname=None, unit=None, token=x, is_none=True) self.is_return = False else: second_token = ctx.getChild(i=1).getSymbol() y = self.visitExpression(ctx.expression(i=1)) if second_token.type == UnitXLexer.ADD: unitx_obj = x.add(y, second_token) elif second_token.type == UnitXLexer.SUB: unitx_obj = x.subtract(y, second_token) elif second_token.type == UnitXLexer.MUL: unitx_obj = x.multiply(y, second_token) elif second_token.type == UnitXLexer.DIV: unitx_obj = x.divide(y, second_token) elif second_token.type == UnitXLexer.MOD: unitx_obj = x.modulo(y, second_token) elif second_token.type == UnitXLexer.ASSIGN: unitx_obj = x.assign(y, second_token) elif second_token.type == UnitXLexer.ADD_ASSIGN: unitx_obj = x.add_assign(y, second_token) elif second_token.type == UnitXLexer.SUB_ASSIGN: unitx_obj = x.substract_assign(y, second_token) elif second_token.type == UnitXLexer.MUL_ASSIGN: unitx_obj = x.multiply_assign(y, second_token) elif second_token.type == UnitXLexer.DIV_ASSIGN: unitx_obj = x.divide_assign(y, second_token) elif second_token.type == UnitXLexer.MOD_ASSIGN: unitx_obj = x.modulo_assign(y, second_token) elif second_token.type == UnitXLexer.EQUAL: unitx_obj = x.equals(y) elif second_token.type == UnitXLexer.EQUAL_X: unitx_obj = x.equals(y) elif second_token.type == UnitXLexer.NOTEQUAL: unitx_obj = x.equals(y) unitx_obj.set_value(not unitx_obj.get_value()) else: unitx_obj = None elif ctx.primary(): unitx_obj = self.visitPrimary(ctx.primary()) else: if not self.is_intaractive_run: raise Exception("Syntax error. EvalVisitor#visitExpression") # Never happen. assert(isinstance(unitx_obj, UnitXObject)) return unitx_obj def visitUnit(self, ctx): """ Just visiting child nodes of UnitX syntax.""" unit = self.visitUnitSingleOrPairOperator(ctx.unitSingleOrPairOperator()) unit.replace_tokens() unit.token = ctx.start return unit def visitUnitSingleOrPairOperator(self, ctx): """ """ if ctx.start.type == UnitXLexer.AT: return Unit() if ctx.unitOperator(i=1): unit = Unit() numer_tokens = self.visitUnitOperator(ctx.unitOperator(i=0)) denom_tokens = self.visitUnitOperator(ctx.unitOperator(i=1)) if len(numer_tokens) == 2: unit.ex_numer, unit.numer = numer_tokens[0].text, numer_tokens[1].text else: unit.numer = numer_tokens[0].text if len(denom_tokens) == 2: unit.ex_denom, unit.denom = denom_tokens[0].text, denom_tokens[1].text else: unit.denom = denom_tokens[0].text return unit else: unit = Unit() numer_tokens = self.visitUnitOperator(ctx.unitOperator(i=0)) if len(numer_tokens) == 2: unit.ex_numer, unit.numer = numer_tokens[0].text, numer_tokens[1].text else: unit.numer = numer_tokens[0].text return unit def visitUnitOperator(self, ctx): """ """ if ctx.Identifier(i=1): return [ctx.Identifier(i=0).getSymbol(), ctx.Identifier(i=1).getSymbol()] else: return [ctx.Identifier(i=0).getSymbol()] def visitPrimary(self, ctx): """ それぞれのPrimaryの値をUnitXObjectにラップして,応答する. Identifier: variable or function literal: number, string, boolean, none PAREN=(): expression BRACK=[]: list """ unit = Unit() if ctx.unit(): unit = self.visitUnit(ctx.unit()) if ctx.Identifier(): # Here varname = ctx.Identifier().getText() unitx_obj = UnitXObject(value=None, varname=varname, unit=unit) """ current_scope = self.get_scopes().peek() if varname in current_scope: unitx_obj = current_scope[varname] if not unit.is_empty(): unitx_obj.unit = unit else: unitx_obj = UnitXObject(value=None, varname=varname, unit=unit) """ found_scope = self.get_scopes().peek().find_scope_of(varname) if found_scope: unitx_obj = found_scope[varname] if not unit.is_empty(): unitx_obj.unit = unit else: unitx_obj = UnitXObject(value=None, varname=varname, unit=unit) unitx_obj.token = ctx.Identifier().getSymbol() elif ctx.literal(): unitx_obj = self.visitLiteral(ctx.literal()) unitx_obj.unit = unit elif ctx.start.type == UnitXLexer.LPAREN: unitx_obj = self.visitExpression(ctx.expression(i=0)) if not unit.is_empty(): unitx_obj.unit = unit unitx_obj.token = ctx.start elif ctx.start.type == UnitXLexer.LBRACK: unitx_objs = [] for an_expr in ctx.expression(): an_obj = self.visitExpression(an_expr) if not unit.is_empty(): an_obj.unit = unit unitx_objs.append(an_obj) unitx_obj = UnitXObject(value = unitx_objs, varname = None, unit=unit, token=ctx.start) else: if not self.is_intaractive_run: raise Exception("Syntax error. EvalVisitor#visitPrimary") # Never happen. assert(isinstance(unitx_obj, UnitXObject)) return unitx_obj def visitLiteral(self, ctx): """ それぞれのリテラルごとのvisitメソッドを呼び出し,その結果の値を応答する. """ if ctx.number(): return self.visitNumber(ctx.number()) elif ctx.string(): return self.visitString(ctx.string()) elif ctx.boolean(): return self.visitBoolean(ctx.boolean()) elif ctx.none(): return self.visitNone(ctx.none()) else: if not self.is_intaractive_run: raise Exception("Syntax error. EvalVisitor#visitLiteral") # Never happen. def visitString(self, ctx): """ 文字列から,両端にあるダブルクォーテーション(\"),シングルクォーテーション(\'),トリプルダブルクォーテーション(\"\"\"),トリプルシングルクォーテーション(\'\'\')を排除し,応答する. """ value = ctx.start.text.strip('"\'') if ctx.STRING_LITERAL(): token = ctx.STRING_LITERAL().getSymbol() elif ctx.BYTES_LITERAL(): token = ctx.BYTES_LITERAL().getSymbol() elif ctx.halfString(): token = None if self.is_intaractive_run: self.visitHalfString(ctx.halfString()) return UnitXObject(value=value, varname=None, unit=Unit(), token=token) def visitHalfString(self, ctx): """ """ self.get_errhandler().is_ignored_block = True def visitNumber(self, ctx): """ 文字列から,int型,float型,複素数型へ変換し,応答する. """ if ctx.integer(): return self.visitInteger(ctx.integer()) elif ctx.FLOAT_NUMBER(): value = float(ctx.FLOAT_NUMBER().getText()) token = ctx.FLOAT_NUMBER().getSymbol() elif ctx.IMAG_NUMBER(): value = complex(ctx.IMAG_NUMBER().getText()) token = ctx.IMAG_NUMBER().getSymbol() return UnitXObject(value=value, varname=None, unit=Unit(), token=token) def visitInteger(self, ctx): """ 文字列からその文字列に属する進数へint変換し,応答する. 変換する進数は,2,8,10,16進数. """ if ctx.DECIMAL_INTEGER(): value = int(ctx.DECIMAL_INTEGER().getText(),10) token = ctx.DECIMAL_INTEGER().getSymbol() elif ctx.OCT_INTEGER(): value = int(ctx.OCT_INTEGER().getText(),8) token = ctx.OCT_INTEGER().getSymbol() elif ctx.HEX_INTEGER(): value = int(ctx.HEX_INTEGER().getText(),16) token = ctx.HEX_INTEGER().getSymbol() elif ctx.BIN_INTEGER(): value = int(ctx.BIN_INTEGER().getText(),2) token = ctx.BIN_INTEGER().getSymbol() return UnitXObject(value=value, varname=None, unit=None, token=token) def visitBoolean(self, ctx): """ 文字列からbooleanへ変換し,応答する. """ value = True if ctx.start.text == 'true' else False return UnitXObject(value=value, varname=None, unit=None, token=ctx.start) def visitNone(self, ctx): """ 文字列からNoneへ変換し,応答する. """ return UnitXObject(value=None, varname=None, unit=None, token=ctx.start, is_none=True) def visitComment(self, ctx): """ """ return UnitXObject(value=None, varname=None, unit=None, token=ctx.start, is_none=True)