def process_special_form_or_expression(self, tokens): """ Process command special form OR a parenthesized expression. Command token and all args will be in the token stream. """ primitives = self.primitives procedures = self.procedures command_token = tokens.popleft() command = command_token.lower() second_token = None if len(tokens) > 0: second_token = tokens.peek() if isinstance(second_token, str) and second_token in ('-', '+', '*', '/', '=', '<>', '>=', '<='): tokens.appendleft(command_token) return self.evaluate(tokens) if command in primitives: proc = primitives[command] elif command in procedures: proc = procedures[command] else: tokens.appendleft(command_token) return self.evaluate(tokens) args = [] while len(tokens) > 0: args.append(self.evaluate(tokens)) max_arity = proc.max_arity if max_arity != -1 and len(args) > max_arity: raise errors.LogoError("There are too many arguments for `{}`.".format(command_token)) if len(args) < proc.min_arity: raise errors.LogoError("Not enough arguments for `{}`.".format(command_token)) if self.debug_primitives and command in primitives: print("PRIMITIVE:", command, "ARGS:", args) if self.debug_procs and command in procedures: print("PROCEDURE:", command, "ARGS:", args) return self.execute_procedure(proc, args)
def get_variable_value(self, varname): """ Get the value of the named variable from the dynamic scope. """ scopes = self.scope_stack for scope in reversed(scopes): if varname in scope: value = scope[varname] if value is None: raise errors.LogoError("`{}` has no value.".format(varname)) return value raise errors.LogoError("No scope has a variable named `{}`.".format(varname))
def evaluate_value(self, tokens, quoted=False): """ Evaluate the next value from the token stream. """ token = tokens.peek() if token is None: raise errors.LogoError("Expected a value but instead got EOF.") if is_list(token): lst_tokens = TokenStream.make_stream(tokens.popleft()) return self.evaluate_list(lst_tokens) if is_special_form(token): spcl_frm_tokens = TokenStream.make_stream(tokens.popleft()) return self.process_special_form_or_expression(spcl_frm_tokens) if is_paren_expr(token): expr_tokens = TokenStream.make_stream(tokens.popleft()) return self.evaluate(expr_tokens) if isinstance(token, numbers.Number): num = tokens.popleft() return num if not quoted: if token.startswith('"'): return tokens.popleft()[1:] if token.startswith(':'): return self.get_variable_value(tokens.popleft()[1:]) if token.startswith("-") and token != '-': temp_token = tokens.popleft() temp_token = temp_token[1:] tokens.appendleft(temp_token) return -1 * self.evaluate(tokens) return self.process_command(tokens) else: return tokens.popleft()
def execute_procedure(self, proc, args): """ Execute a procedure with args, `args`. """ if proc.primitive_func: return proc.primitive_func(self, *args) tokens = TokenStream.make_stream(proc.tokens) scope = {} scope_stack = self.scope_stack scope_stack.append(scope) formal_params = list(itertools.chain( [(name, None) for name in proc.required_inputs], proc.optional_inputs)) rest_args = [] rest_input = proc.rest_input for vardef, value in itertools.zip_longest(formal_params, args): if vardef is None: rest_args.append(value) else: varname, default_value = vardef if value is None: if default_value != ':' and hasattr(default_value, 'startswith') and default_value.startswith(':'): name = default_value[1:] for a_scope in reversed(scope_stack): if name in a_scope: value = a_scope[name] break if value is None: raise errors.LogoError("Default parameter `{}` could not find `:{}` in any scope.".format(varname, name)) else: value = default_value if value is None: raise errors.LogoError("Must have a value for formal parameter `{}` in procedure `{}`.".format(varname, proc.name)) scope[varname] = value if rest_input: scope[rest_input] = rest_args result = None try: self.process_commands(tokens) except errors.StopSignal: result = None except errors.OutputSignal as output: result = output.value scope_stack.pop() return result
def process_command(self, tokens): """ Process a command. """ if self.halt: raise errors.HaltSignal("Received HALT") primitives = self.primitives procedures = self.procedures while len(tokens) > 0: token = tokens.popleft() token = transform_qmark(token) is_cmd = is_command(token) is_spcl_frm = is_special_form(token) if not is_cmd and not is_spcl_frm: raise errors.LogoError("Expected a command. Instead, got `{}`.".format(token)) if is_spcl_frm: stream = TokenStream.make_stream(token) return self.process_special_form_or_expression(stream) else: command = token.lower() if command == 'to': procedure.process_to(self, tokens) elif command in primitives: proc = primitives[command] args = self.evaluate_args_for_command(proc.default_arity, tokens) for n, arg in enumerate(args): if arg is None: raise errors.LogoError("Primitive `{}` received a null value for argument {}.".format(command.upper(), n+1)) if self.debug_primitives: print("PRIMITIVE:", command, "ARGS:", args) return self.execute_procedure(proc, args) elif command in procedures: proc = procedures[command] args = self.evaluate_args_for_command(proc.default_arity, tokens) for n, arg in enumerate(args): if arg is None: raise errors.LogoError("Procedure `{}` received a null value for argument {}.".format(command.upper(), n+1)) if self.debug_procs: print("PROCEDURE:", command, "ARGS:", args) return self.execute_procedure(proc, args) else: raise errors.LogoError("I don't know how to `{}`.".format(token))
def receive_input(self, data): """ Handles input received from GUI. """ try: grammar = self.grammar tokens = parse_tokens(grammar, data, debug=self.debug_tokens) result = self.process_commands(tokens) if result is not None: raise errors.LogoError("You don't say what to do with `{}`.".format(result)) except errors.HaltSignal: self.halt = False
def load_script(self, filename): """ Attempt to load a Logo script and insert its contents into the curent token stream. """ script_folders = self.script_folders for folder in script_folders: pth = os.path.join(folder, filename) if os.path.exists(pth): with open(pth, "r") as f: data = f.read() return self.process_instructionlist(data) raise errors.LogoError("Could not locate script `{}`.".format(filename))
def main(args): """ Parse Logo """ grammar = make_token_grammar() interpreter = LogoInterpreter.create_interpreter() interpreter.turtle_backend_args = dict(input_handler=interpreter.receive_input) if args.turtle == 'tk': interpreter.turtle_backend_args['maximize'] = args.maximize interpreter.init_turtle_graphics() interpreter.debug_tokens = args.debug_tokens interpreter.grammar = grammar interpreter.debug_primitives = args.debug_primitives interpreter.debug_procs = args.debug_procs script_folders = args.script_folder if script_folders is None: script_folders = [] interpreter.script_folders = script_folders if args.turtle == 'svg': interpreter.turtle_backend = svgturtle.SVGTurtleEnv.create_turtle_env() svg_args = dict( output_file=args.outfile, html_folder=args.html, ) html_args = {} d = vars(args) for k in ('html_title', 'animation_duration', 'animation_type'): v = d.get(k) if v is not None: html_args[k] = v if args.html_width: html_args['html_width'] = args.html_width if args.html_scale: html_args['html_scale'] = args.html_scale if args.animation_duration: html_args['animation_duration'] = args.animation_duration if args.animation_type: animation_type = args.animation_type if animation_type == 'onebyone': animation_type = 'oneByOne' html_args['animation_type'] = animation_type if args.animation_start: animation_start = args.animation_start html_args['animation_start'] = animation_start svg_args['html_args'] = html_args interpreter.turtle_backend_args = svg_args if args.file is not None: script = args.file.read() tokens = parse_tokens(grammar, script, debug=args.debug_tokens) if args.tokenize_only: return try: result = interpreter.process_commands(tokens) except Exception as ex: print("Processed tokens: {}".format(tokens.processed), file=sys.stderr) raise ex if result is not None: raise errors.LogoError("You don't say what to do with `{}`.".format(result)) if interpreter.is_turtle_active(): interpreter.turtle_backend.wait_complete() if args.debug_interpreter: print("") import pprint pprint.pprint(interpreter)