def bool_opr(lval, rval, op): if isAthValue(lval): lval = AthSymbol(bool(lval), left=lval) if isAthValue(rval): rval = AthSymbol(bool(rval), left=rval) if op == 'l&': return lval and rval elif op == 'l|': return lval or rval elif op == 'l^': return (lval if lval and not rval else rval if rval and not lval else AthSymbol(False)) raise SyntaxError(f'Invalid comparison operator: {op}')
def exec_stmts(self, fname, stmts): # AST Execution trampoline. athloops = 0 while True: try: ath_builtins['THIS'] = ThisSymbol(fname, stmts) self.stack.append(AthStackFrame(iter_nodes=stmts.iter_nodes())) self.ast = self.stack[-1].iter_nodes while True: try: node = next(self.ast) except StopIteration: state = self.stack[-1].exec_state if state == self.TOPLEVEL_STATE: sys.exit(0) # override athloops += 1 if athloops >= 612: sys.stderr.write( 'UnboundedLoopError: THIS.DIE() never called' ) else: self.ast.reset() elif state == self.FUNCEXEC_STATE: self.stack.pop() self.eval_return(AthSymbol(False)) self.ast = self.stack[-1].iter_nodes else: if (self.get_symbol(self.ast.pendant).alive == bool(state - self.TILDEATH_STATE)): self.stack.pop() self.ast = self.stack[-1].iter_nodes else: self.ast.reset() continue # print(repr(node)) if isinstance(node, TildeAthLoop): if self.get_symbol( node.body.pendant).alive == node.state: continue self.stack.append( AthStackFrame( iter_nodes=node.body.iter_nodes(), exec_state=self.TILDEATH_STATE + int(node.state), )) self.ast = self.stack[-1].iter_nodes continue self.stack[-1].eval_state.append(node.prepare()) ret_value = self.eval_stmt() if (self.stack[-1].get_current().name == 'DIVULGATE' and ret_value is not None): self.stack.pop() self.eval_return(ret_value) except KeyboardInterrupt: raise # override self.stack.clear() continue except Exception as exc: raise exc
def math_proxy_func(env, *values): args = [] for value in values: if isinstance(value, AthSymbol): value = value.left if isinstance(value, dtypes): args.append(value) else: raise TypeError('invalid numerical type') return AthSymbol(left=func(*args))
def symbol_opr(lval, rval, op): if not (isinstance(lval, AthSymbol) and isinstance(rval, AthSymbol)): raise TypeError('May only perform living assertions on symbols') try: if op == '!=!': value = lval is rval elif op == '?=!': value = lval.left.alive and rval.left.alive elif op == '!=?': value = lval.right.alive and rval.right.alive elif op == '~=!': value = not (lval.left.alive or rval.left.alive) elif op == '!=~': value = not (lval.right.alive or rval.right.alive) elif op == '~=~': value = lval is not rval else: raise SyntaxError('Invalid comparison operator: {}', op) except AttributeError: raise SymbolError('The relevant side(s) must be symbols') return AthSymbol(value)
def eval_stmt(self, ret_value=None): # Statement evaluation trampoline. eval_state = self.stack[-1].eval_state node = eval_state[-1] if ret_value is not None: node.set_argv(ret_value) while True: try: # Try to get the next argument AST node from the expression. arg = node.get_arg() except IndexError: # If there are no more left, execute the associated function. try: ret_value = node.execute(self) except SymbolDeath as exc: # In a DIE statement, SymbolDeath will be raised. if 'THIS' in exc.args[0]: # End the program only when THIS is killed. sys.exit(0) elif self.stack[-1].iter_nodes.pendant in exc.args[0]: # If the current frame's control variable is killed, evaluate: state = self.stack[-1].exec_state if state == self.TILALIVE_STATE: # If in keep-dead loop, force the loop to repeat. self.stack[-1].iter_nodes.reset() else: # Otherwise, pop the execution stack and move on. self.stack.pop() self.ast = self.stack[-1].iter_nodes if state == self.FUNCEXEC_STATE: # If popping from a function, evaluate from here instead. eval_state = self.stack[-1].eval_state ret_val = AthSymbol(False) continue eval_state.clear() return AthSymbol(False) except Exception as exc: # When other errors occur, pass the exception upward. raise exc else: # When the function has finished, move down the evaluation stack. if node.stmt.name == 'EXECUTE': # Function call eval_state.pop() func, scope_vars = ret_value if isinstance(func, AthBuiltinFunction): node = eval_state[-1] node.set_argv(func(self, *scope_vars)) continue if self.is_tail_call(len(eval_state) + 1): frame = self.stack[-1] frame.scope_vars = scope_vars frame.iter_nodes = func.body.iter_nodes() frame.exec_state = self.FUNCEXEC_STATE eval_state.clear() else: self.stack.append( AthStackFrame( scope_vars=scope_vars, iter_nodes=func.body.iter_nodes(), exec_state=self.FUNCEXEC_STATE, )) self.ast = self.stack[-1].iter_nodes return None if len(eval_state) > 1: eval_state.pop() node = eval_state[-1] node.set_argv(ret_value) continue # If this is the last expression in the stack, return to the AST. eval_state.clear() return ret_value else: # If there is an argument left to evaluate, deal with it first. if arg is None or isinstance(arg, (int, str, AthCustomFunction)): # Name, Number, Function, and Empty items are passed as is. node.set_argv(arg) elif isinstance(arg, LiteralToken): # Tokens pass their values. node.set_argv(arg.value) elif isinstance(arg, IdentifierToken): # Name tokens pass either their names or evaluate their values. if node.is_name_arg(): node.set_argv(arg.name) else: node.set_argv(self.get_symbol(arg.name)) elif isinstance(arg, AthStatement): # Evaluate expressions for their values before passing the result. node = arg.prepare() eval_state.append(node)
def unopr_expression(env, opr, val): ans = unops[opr](val) if isAthValue(ans): return AthSymbol(left=ans) return ans
def bool_not(val): if isAthValue(val): val = AthSymbol(bool(val), left=val) return AthSymbol(not val, val.left, val.right)
def on_dead_jump(env, expr, jlen): if not expr: env.stack[-1].iter_nodes.index += jlen return AthSymbol(False)
def biopr_expression(env, opr, lft, rht): ans = biops[opr](lft, rht) if isAthValue(ans): return AthSymbol(left=ans) return ans
def cmp_opr(lval, rval, op): if isAthValue(lval): lval = AthSymbol(bool(lval), left=lval) return op(lval, rval)