def wasmify(ast): """ Turn ast of this toy language into WASM. """ # Generate WASM instructions from the ast instructions = [('f64.const', 0) ] # the initial value of the number is zero for op in ast: instructions.append(('f64.const', op.operand.value)) if op.value == '+': instructions.append(('f64.add')) elif op.value == '-': instructions.append(('f64.sub')) elif op.value == '*': instructions.append(('f64.mul')) elif op.value == '/': instructions.append(('f64.div')) # Add call to print the result instructions.append(('call', 0)) # Put instructions in a the main function of a WASM module module = wf.Module( wf.ImportedFuncion('print_ln', ['f64'], [], 'js', 'print_ln'), wf.Function('$main', [], [], [], instructions), ) return module
def simplepy2wasm(code): """ Compile Python code to wasm, by using Python's ast parser and compiling a very specific subset to WASM instructions. """ # Verify / convert input if isinstance(code, ast.AST): root = code elif isinstance(code, str): root = ast.parse(code) else: raise TypeError('simplepy2wasm() requires (str) code or AST.') if not isinstance(root, ast.Module): raise ValueError( 'simplepy2wasm() expecteded root node to be a ast.Module.') # Compile to instructions ctx = Context() for node in root.body: _compile_expr(node, ctx, False) locals = ['f64' for i in ctx.names] # Produce wasm module = wf.Module( wf.ImportedFuncion('print_ln', ['f64'], [], 'js', 'print_ln'), wf.ImportedFuncion('perf_counter', [], ['f64'], 'js', 'perf_counter'), wf.Function('$main', [], [], locals, ctx.instructions), ) return module
def to_wasm(self): """ Create wasm Function object. """ arg_types = self._arg_types ret_types = self._ret_types or [] locals = ['f64' for i in self.names] return wf.Function(self._name, arg_types, ret_types, locals, self.instructions)
def brainfuck2wasm(code): """ Compile brainfuck code to a WASM module. """ commands = [c for c in code if c in '><+-.,[]'] instructions = _commands2instructions(commands) module = wf.Module( wf.ImportedFuncion('print_charcode', ['i32'], [], 'js', 'print_charcode'), wf.Function('$main', [], [], ['i32'], instructions), wf.MemorySection((1, 1)), # 1 page of 64KiB > 30000 minimum for Brainfuck wf.DataSection(), # no initial data ) return module
def to_wasm(self): """ Create wasm Module object. """ # This is also the main function locals = ['f64' for i in self.names] main_func = wf.Function('$main', [], [], locals, self.instructions) # Add imported funcs funcs = [] funcs.append( wf.ImportedFuncion('print_ln', ['f64'], [], 'js', 'print_ln')) funcs.append( wf.ImportedFuncion('perf_counter', [], ['f64'], 'js', 'perf_counter')) # Add main funcs and other funcs funcs.append(main_func) for ctx in self.all_functions[1:]: funcs.append(ctx.to_wasm()) # Compose module return wf.Module(*funcs)
# These instructions are equivalent. The latter might be easier to write thanks # to autocompletion, but the former looks nicer IMO instructions = [(I.loop, 'emptyblock'), (I.get_local, 0), (I.call, 'print_ln'), (I.f64.const, 1), (I.get_local, 0), (I.f64.add), (I.tee_local, 0), (I.f64.const, 10), (I.f64.lt), (I.br_if, 0), (I.end)] # Compose functions into a module root = wf.Module( wf.ImportedFuncion('alert', ['f64'], [], 'js', 'alert'), wf.ImportedFuncion('print_ln', ['f64'], [], 'js', 'print_ln'), wf.Function( 'add', params=['f64', 'f64'], returns=['f64'], locals=[], instructions=[('get_local', 0), ('get_local', 1), ('f64.add')], ), wf.Function('$main', params=[], returns=[], locals=['f64'], instructions=instructions), ) # For reference, one could also write it like this, using an explicit TypeSection # and CodeSection. It needs more work to get the binding right, which in the above # is done automatically. # # root = wf.Module(