def plug_arglist(func_def, arglist, caller_env, callee_env): pos_parmlist = func_def['pos_parmlist'] pos_arglist = arglist.args if len(pos_parmlist) < len(pos_arglist): raise EvalError('Unmatched Function Positional Parameters', get_topenv().get('$INPUT_LINES').get_content_around_pos(arglist.pos)) kw_parmlist = func_def['kw_parmlist'] kw_arglist = {} for kwarg in arglist.kwargs: kw_arglist[kwarg.parm_name.name] = kwarg.value.eval(caller_env) if not set.issubset(set(kw_arglist.keys()), set(kw_parmlist.keys())): raise EvalError('Unmatched Function Keyword Parameters', get_topenv().get('$INPUT_LINES').get_content_around_pos(arglist.pos)) # plug positional arguments for ii in range(len(pos_parmlist)): value = pos_arglist[ii].eval(caller_env) if ii < len(pos_arglist) else None callee_env.set(pos_parmlist[ii], value) # plug keyword arguments for key in kw_parmlist.keys(): value = kw_arglist[key] if key in kw_arglist.keys() else kw_parmlist[key] callee_env.set(key, value)
def eval(self, env): var = self.collection.eval(env) if type(var) != list: raise EvalError('Can Only Slice a List: ' + str(var), get_topenv().get('$INPUT_LINES').get_content_around_pos(self.pos)) idxlist = self.idxlist.eval(env) try: return var[idxlist] except IndexError: raise EvalError('List Index Out of Range', get_topenv().get('$INPUT_LINES').get_content_around_pos(self.pos))
def parse_idxlist(tokenlist): 'Parse the idxlist for array like variable' parsed = PT_Node(PN_IDXLIST) tokenlist.match(EPW_OP_L_BRACKET) # The start of the index if tokenlist.get().tag != EPW_OP_COLON: parsed.append(parse_expression(tokenlist)) else: if tokenlist.get().tag != EPW_OP_COLON: raise ParseError('Incorrect index list format', get_topenv().get('$INPUT_LINES').get_content_around_pos(tokenlist.get().pos)) parsed.append(PT_Node(PN_NONE)) # The optional end of the index if tokenlist.get().tag == EPW_OP_COLON: tokenlist.match(EPW_OP_COLON) if tokenlist.get().tag not in [EPW_OP_COLON, EPW_OP_R_BRACKET]: parsed.append(parse_expression(tokenlist)) else: parsed.append(PT_Node(PN_NONE)) # The optional step of the index if tokenlist.get().tag == EPW_OP_COLON: tokenlist.match(EPW_OP_COLON) if tokenlist.get().tag != EPW_OP_R_BRACKET: parsed.append(parse_expression(tokenlist)) else: parsed.append(PT_Node(PN_NONE)) tokenlist.match(EPW_OP_R_BRACKET) return parsed
def try_builtin_func(func_node, caller_env): func = func_node.func # if not a builtin func, we do not check name = func.name if name not in builtin_func_names: return (0, 0) else: nargs = len(func_node.arglist.args) range = builtin_func_def[name][0] if nargs < range[0] or nargs > range[1]: raise EvalError('Incorrect Parameters for Builtin Function', name) topenv = get_topenv() if name == 'debug': topenv.set('$DEBUG', 1 - topenv.get('$DEBUG')) return (1, None) elif name == 'list': if len(func_node.arglist.args) == 0: return (1, []) else: return (1, [0] * func_node.arglist.args[0].eval(caller_env))
def try_builtin_func(func_node, caller_env): func = func_node.func # if not a builtin func, we do not check name = func.name if name not in builtin_func_names: return (0, 0) else: nargs = len(func_node.arglist.args) range = builtin_func_def[name][0] if nargs < range[0] or nargs > range[1]: raise EvalError('Incorrect Parameters for Builtin Function', name) topenv = get_topenv() if name == 'debug': topenv.set('$DEBUG', 1-topenv.get('$DEBUG')) return (1, None) elif name == 'list': if len(func_node.arglist.args) == 0: return (1, []) else: return (1, [0] * func_node.arglist.args[0].eval(caller_env))
def __init__(self): self.lex = Lexer() self.compiler = Compiler() self.vm = VM() for token_type in specs.token_type_list: self.lex.add_token_type(token_type) self.topenv = get_topenv() self.topenv.update(specs.SETTINGS)
def match(self, tag_to_match): cur_token = self.get() if cur_token.tag == tag_to_match: self.pos += 1 else: raise ParseError('Expected: ' + tag_to_match, get_topenv().get('$INPUT_LINES').get_content_around_pos(cur_token.pos)) return cur_token
def parse_simple_stmt(tokenlist): 'A simple_stmt is a SINGLE stmt' token = tokenlist.get() if token.tag == EPW_KW_PRINT: parsed = parse_print_stmt(tokenlist) elif token.tag == EPW_KW_CONTINUE: tokenlist.match(EPW_KW_CONTINUE) parsed = PT_Node(PN_CONTINUE) elif token.tag == EPW_KW_BREAK: tokenlist.match(EPW_KW_BREAK) parsed = PT_Node(PN_BREAK) elif token.tag == EPW_KW_RETURN: tokenlist.match(EPW_KW_RETURN) # This parsing may not be optimal though it may works. parsed = PT_Node(PN_RETURN) if tokenlist.get().tag not in [EPW_OP_EOL, EPW_OP_SEMICOLON, EPW_OP_R_PAREN]: parsed.append(parse_r_expression(tokenlist)) elif token.tag in [EPW_INT, EPW_FLOAT, EPW_STR]: parsed = parse_r_expression(tokenlist) elif token.tag == EPW_OP_SEMICOLON: parsed = parse_empty_stmt(tokenlist) elif is_unaryop(token): # a leading +/-/not, it can only be an expression parsed = parse_r_expression(tokenlist) elif token.tag == EPW_ID: # Both ID, slice all start with an ID. # If the next token is a "=", it is an assignment with an ID. # If it is not, we need to do a regular expression match to # find out whether it is a assignment to a slice. # A slice can be either a simple ID with [], ID[], or # a function call with [], f()[] if tokenlist.get(1).tag == EPW_OP_ASSIGN: # simple case of ID assignment parsed = parse_assign_stmt(tokenlist) else: # complicate case for possible slice assignment rest = tokenlist.get_rest_line() # Try to see if we have any function call or slices matched = regex_func_slice_mix_assign.match(rest) if matched: mstring = matched.group(0) if mstring[-3] == EPW_OP_R_PAREN: raise ParseError('Cannot Assign to a Function Call', get_topenv().get('$INPUT_LINES').get_content_around_pos(tokenlist.get().pos)) parsed = parse_ID(tokenlist) for c in mstring: if c == EPW_OP_L_BRACKET: parsed = parse_brackets(tokenlist, parsed) elif c == EPW_OP_L_PAREN: parsed = parse_parenthesis(tokenlist, parsed) parsed = parse_assign_stmt(tokenlist, parsed) else: parsed = parse_r_expression(tokenlist) else: tokenlist.match('token for a simple_stmt') return parsed
def eval(self, env): ret = None top_env = get_topenv() for node in self.node_list: ret = node.eval(env) # Set value for magic variable if ret is not None: top_env.set('_', ret) return ret
def eval(self, env): # Function can only be defined at the Top Level if env != get_topenv(): raise EvalError('Function Definition Can Only Be at Top Level', get_topenv().get('$INPUT_LINES').get_content_around_pos(self.pos)) # Process any parameter list if self.parmlist: pos_parmlist = [] for arg in self.parmlist.args: pos_parmlist.append(arg.name) kw_parmlist = {} for kwarg in self.parmlist.kwargs: kw_parmlist[kwarg.parm_name.name] = kwarg.value.eval(env) # Create the function definition and store in the top level func_def = {'body': self.body, 'pos_parmlist': pos_parmlist, 'kw_parmlist': kw_parmlist} env.set(self.func.name, func_def) return func_def
def match(self, tag_to_match): cur_token = self.get() if cur_token.tag == tag_to_match: self.pos += 1 else: raise ParseError( 'Expected: ' + tag_to_match, get_topenv().get('$INPUT_LINES').get_content_around_pos( cur_token.pos)) return cur_token
def eval(self, env): if self.op == '+': return self.operand.eval(env) elif self.op == '-': return -self.operand.eval(env) elif self.op == 'not': return 1 if not self.operand.eval(env) else 0 else: raise EvalError('Unrecognized Unary Operator: ' + self.op, get_topenv().get('$INPUT_LINES').get_content_around_pos(self.pos))
def eval(self, env): try: # TODO: Still need to work on coerce if self.op == '+': return self.l.eval(env) + self.r.eval(env) elif self.op == '-': return self.l.eval(env) - self.r.eval(env) elif self.op == '*': return self.l.eval(env) * self.r.eval(env) elif self.op == '/': return self.l.eval(env) / self.r.eval(env) elif self.op == '%': return self.l.eval(env) % self.r.eval(env) elif self.op == '>': return self.l.eval(env) > self.r.eval(env) elif self.op == '<': return self.l.eval(env) < self.r.eval(env) elif self.op == '>=': return 1 if self.l.eval(env) >= self.r.eval(env) else 0 elif self.op == '<=': return 1 if self.l.eval(env) <= self.r.eval(env) else 0 elif self.op == '==': return 1 if self.l.eval(env) == self.r.eval(env) else 0 elif self.op == '!=': return 1 if self.l.eval(env) != self.r.eval(env) else 0 elif self.op == 'and': return 1 if self.l.eval(env) and self.r.eval(env) else 0 elif self.op == 'or': return 1 if self.l.eval(env) or self.r.eval(env) else 0 elif self.op == 'xor': return 1 if bool(self.l.eval(env)) != bool(self.r.eval(env)) else 0 else: raise EvalError('Unrecognized Binary Operator: ' + self.op, get_topenv().get('$INPUT_LINES').get_content_around_pos(self.pos)) except TypeError as e: raise EvalError('Type Mismatch: ' + self.op, get_topenv().get('$INPUT_LINES').get_content_around_pos(self.pos))
def eval(self, caller_env): # Get the top env topenv = get_topenv() func = self.func # If the function call is a simple ID() form, we check whether it # is a builtin function. # or get its definition from topenv. # This is not ideal, but since the language does not allow assign # another variable name to a builtin function. So it may work for # now. if repr(func).find('Variable(') == 0: # Check and run if its a builtin func flag, ret = try_builtin_func(self, caller_env) if flag: return ret # Otherwise, Get the func definition from top level name = func.name if topenv.has(name): func_def = get_topenv().get(name) else: raise EvalError('Undefined Function: ' + name, get_topenv().get('$INPUT_LINES').get_content_around_pos(self.pos)) else: func_def = func.eval(caller_env) # Plug the arglist callee_env = Environment(outer=caller_env) plug_arglist(func_def, self.arglist, caller_env, callee_env) # Evaluation try: ret = None ret = func_def['body'].eval(callee_env) except ReturnControl as e: if len(e.args) > 0: ret = e.args[0] return ret
def eval(self, env): theEnv = env.find(self.name) if theEnv is None: raise EvalError('Variable Not Defined: ' + self.name, get_topenv().get('$INPUT_LINES').get_content_around_pos(self.pos)) return theEnv.get(self.name)