def _while(states, scope, arguments, block): if not len(block.lines): raise InvalidArguments('A block is required in `while`') if len(arguments.args) != 1 \ or len(arguments.kwargs): raise InvalidArguments('`while` only accepts one conditional argument') var = arguments.args[0] retval = None while var.python_value(scope): if retval is None: retval = '' try: retval += str(block.python_value(scope)) except BreakException as e: retval += e.retval break except ContinueException as e: retval += e.retval continue else: if retval is None: return [Continue(), EmptyState(), ElseState()], '' return [Continue(), ElseState()], retval return [Continue()], retval
def binary_or(left, right, scope): if isinstance(right, PlywoodCallOperator): new_op = right.copy() new_op.right.args.insert(0, left.get_value(scope)) arguments = new_op.right _, retval = new_op.run([Continue()], scope) else: arguments = PlywoodParens(right.location, [left]) block = PlywoodBlock(right.location, []) _, retval = right.call([Continue()], scope, arguments, block) return retval
def _elif(states, scope, arguments, block): if not len(block.lines): raise InvalidArguments('A block is required in `elif`') if len(arguments.args) != 1: raise InvalidArguments( 'A condition (and only one condition) is required in `elif`') if len(arguments.args) != 1 or len(arguments.kwargs): raise InvalidArguments('`elif` only accepts one argument') arg = arguments.args[0].python_value(scope) if arg: return [Continue()], block.get_value(scope) return [Continue(), ElseState()], ''
def _for(states, scope, arguments, block): if not len(block.lines): raise InvalidArguments('A block is required in `for`') if len(arguments.args) != 1 \ or len(arguments.kwargs) \ or not isinstance(arguments.args[0], PlywoodOperator) \ or arguments.args[0].operator != 'in': raise InvalidArguments('`for` only accepts an `in` operation') var = arguments.args[0].left if not isinstance(var, PlywoodVariable) and not isinstance( var, PlywoodParens): raise InvalidArguments( '`for` expects a variable name or tuple of variable names') if isinstance(var, PlywoodParens): if var.kwargs: raise InvalidArguments( 'keyword arguments are not in appropriate in the list of `for` variable names' ) for arg in var.arguments: if not isinstance(arg, PlywoodVariable): raise InvalidArguments( '`for` expects a variable name or tuple of variable names') iterator = arguments.args[0].right.python_value(scope) retval = None if not iterator: return [Continue(), EmptyState(), ElseState()], '' for for_value in iterator: if retval is None: retval = '' if isinstance(var, PlywoodVariable): varname = var.get_name() scope[varname] = for_value elif isinstance(var, PlywoodParens): raise Exception('huh?') # for_values should be a PlywoodList, PlywoodDict, # if len(var.arguments) != # scope[] pass try: retval += str(block.python_value(scope)) except BreakException as e: retval += e.retval break except ContinueException as e: retval += e.retval continue else: if retval is None: return [Continue(), EmptyState(), ElseState()], '' return [Continue(), ElseState()], retval return [Continue()], retval
def the_function(states, called_scope, called_arguments, called_block): args = called_arguments.args kwargs = called_arguments.kwargs local_arglist = [] + final_arglist called_scope.push() for plywood_kwarg in kwargs: local_value = plywood_kwarg.value.python_value(scope) local_var_name = plywood_kwarg.key.python_value(scope) if local_var_name in local_arglist: local_arglist.remove(local_var_name) else: raise InvalidArguments('Unknown keyword argument {0!r}'.format(local_var_name)) called_scope[local_var_name] = local_value for plywood_value in args: local_value = plywood_value.python_value(called_scope) if not local_arglist: raise InvalidArguments('Too many arguments passed to {0}'.format(function_name)) local_var_name = local_arglist.pop(0) called_scope[local_var_name] = local_value for local_var_name, local_value in defaults.items(): if local_var_name in local_arglist: local_arglist.remove(local_var_name) called_scope[local_var_name] = local_value if local_arglist: raise InvalidArguments('Unassigned values: {0}'.format(', '.join(local_arglist))) def return_function(arg): raise ReturnException(arg) called_scope['return'] = PlywoodFunction(return_function) try: retval = block.get_value(called_scope) except ReturnException as e: retval = e.retval called_scope.pop() return [Continue()], retval
def markdown(states, scope, arguments, block): code = block.get_value(scope).python_value(scope) markup = misaka.smartypants(compiler(code).strip()) return [Continue()], markup
def _def(states, scope, arguments, block): ''' Example: def foo(bar, baz, quux='value') foo => name of function [bar, baz] => arguments, no default values. These must be variable names. Sorry, no support for *args and **kwargs just yet. ''' if not len(block.lines): raise InvalidArguments('A block is required in `def`') if len(arguments.args) != 1 \ or len(arguments.kwargs) \ or not isinstance(arguments.args[0], PlywoodCallOperator) \ or arguments.args[0].operator != '()': raise InvalidArguments('`def` should be followed by a function definition') function_name = arguments.args[0].left.get_name() arglist = arguments.args[0].right.args kwarglist = arguments.args[0].right.kwargs final_arglist = [] defaults = {} # all arglist.args should be a varname for var_name in arglist: if not isinstance(var_name, PlywoodVariable): raise InvalidArguments('`def` should be given a list of variable names. ' '{0!r} is invalid'.format(var_name.python_value(scope))) final_arglist.append(var_name.get_name()) for kwarg in kwarglist: var_name = kwarg.key.python_value(scope) final_arglist.append(var_name) value = kwarg.value.python_value(scope) defaults[var_name] = value # Now we have to re-produce python's way of accepting arguments, but I'm # gonna get away with being sloppy about it... # First, any kwargs can be assigned, and removed from the final_arglist. # Then, args is scanned and values are assigned to corresponding names in # final_arglist. If any values are left over in final_arglist, args, or # kwargs, something went wrong. # # The sloppy part comes because unlike python, you can specify args and # kwargs in pretty much any order. All kwargs are assigned first, then # the args are assigned to whatever is left in the local_arglist, then # defaults. Only after all that is local_arglist checked to make sure it is # empty. def the_function(states, called_scope, called_arguments, called_block): args = called_arguments.args kwargs = called_arguments.kwargs local_arglist = [] + final_arglist called_scope.push() for plywood_kwarg in kwargs: local_value = plywood_kwarg.value.python_value(scope) local_var_name = plywood_kwarg.key.python_value(scope) if local_var_name in local_arglist: local_arglist.remove(local_var_name) else: raise InvalidArguments('Unknown keyword argument {0!r}'.format(local_var_name)) called_scope[local_var_name] = local_value for plywood_value in args: local_value = plywood_value.python_value(called_scope) if not local_arglist: raise InvalidArguments('Too many arguments passed to {0}'.format(function_name)) local_var_name = local_arglist.pop(0) called_scope[local_var_name] = local_value for local_var_name, local_value in defaults.items(): if local_var_name in local_arglist: local_arglist.remove(local_var_name) called_scope[local_var_name] = local_value if local_arglist: raise InvalidArguments('Unassigned values: {0}'.format(', '.join(local_arglist))) def return_function(arg): raise ReturnException(arg) called_scope['return'] = PlywoodFunction(return_function) try: retval = block.get_value(called_scope) except ReturnException as e: retval = e.retval called_scope.pop() return [Continue()], retval the_function.__name__ = function_name scope[function_name] = PlywoodRuntime(the_function) return [Continue()], ''
def _else(states, scope, arguments, block): if not len(block.lines): raise InvalidArguments('A block is required in `else`') if len(arguments.args) or len(arguments.kwargs): raise InvalidArguments('`else` does not accept any arguments') return [Continue()], block.get_value(scope)
def empty(state, scope, arguments, block): if not len(block.lines): raise ParseException('A block is required in `empty`') if len(arguments.args) or len(arguments.kwargs): raise ParseException('`empty` does not accept any arguments') return [Continue()], block.get_value(scope)