| #!/usr/bin/env python3 |
| # |
| # Copyright 2016 WebAssembly Community Group participants |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| import argparse |
| import os |
| import struct |
| import sys |
| |
| from utils import Error, Hexdump |
| |
| SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) |
| ROOT_DIR = os.path.dirname(SCRIPT_DIR) |
| OUT_DIR = os.path.join(ROOT_DIR, 'out') |
| PLY_DIR = os.path.join(ROOT_DIR, 'third_party', 'ply') |
| |
| sys.path.append(PLY_DIR) |
| |
| try: |
| import ply.lex as lex |
| import ply.yacc as yacc |
| except ImportError: |
| raise Error('Unable to import ply. Did you run "git submodule update"?') |
| |
| # ply stuff ################################################################### |
| NAMED_VALUES = { |
| 'i32': 0x7f, # -1 |
| 'i64': 0x7e, # -2 |
| 'f32': 0x7d, # -3 |
| 'f64': 0x7c, # -4 |
| 'v128': 0x7b, # -5 |
| 'anyfunc': 0x70, # -0x10 |
| 'function': 0x60, # -0x20 |
| 'struct': 0x5f, # -0x21 |
| 'array': 0x5e, # -0x22 |
| 'void': 0x40, # -0x40 |
| 'magic': (0, 0x61, 0x73, 0x6d), |
| 'version': (1, 0, 0, 0), |
| |
| # section codes |
| 'USER': 0, |
| 'TYPE': 1, |
| 'IMPORT': 2, |
| 'FUNCTION': 3, |
| 'TABLE': 4, |
| 'MEMORY': 5, |
| 'GLOBAL': 6, |
| 'EXPORT': 7, |
| 'START': 8, |
| 'ELEM': 9, |
| 'CODE': 10, |
| 'DATA': 11, |
| 'DATACOUNT': 12, |
| 'TAG': 13, |
| |
| # name subsection codes |
| 'NAME_MODULE': 0, |
| 'NAME_FUNCTION': 1, |
| 'NAME_LOCALS': 2, |
| |
| # linking subsection codes |
| 'LINKING_SEGMENT_INFO': 5, |
| 'LINKING_INIT_FUNCTIONS': 6, |
| 'LINKING_COMDAT_INFO': 7, |
| 'LINKING_SYMBOL_TABLE': 8, |
| |
| # dylink.0 subsection codes |
| 'DYLINK_MEM_INFO': 1, |
| 'DYLINK_NEEDED': 2, |
| 'DYLINK_EXPORT_INFO': 3, |
| 'DYLINK_IMPORT_INFO': 4, |
| |
| # external kinds |
| 'func_kind': 0, |
| 'table_kind': 1, |
| 'memory_kind': 2, |
| 'global_kind': 3, |
| "unreachable": 0x00, |
| "nop": 0x01, |
| "block": 0x02, |
| "loop": 0x03, |
| "if": 0x04, |
| "else": 0x05, |
| "end": 0x0b, |
| "br": 0x0c, |
| "br_if": 0x0d, |
| "br_table": 0x0e, |
| "return": 0x0f, |
| "call": 0x10, |
| "call_indirect": 0x11, |
| "return_call": 0x12, |
| "return_call_indirect": 0x13, |
| "drop": 0x1a, |
| "select": 0x1b, |
| "get_local": 0x20, |
| "set_local": 0x21, |
| "tee_local": 0x22, |
| "get_global": 0x23, |
| "set_global": 0x24, |
| "i32.load": 0x28, |
| "i64.load": 0x29, |
| "f32.load": 0x2a, |
| "f64.load": 0x2b, |
| "i32.load8_s": 0x2c, |
| "i32.load8_u": 0x2d, |
| "i32.load16_s": 0x2e, |
| "i32.load16_u": 0x2f, |
| "i64.load8_s": 0x30, |
| "i64.load8_u": 0x31, |
| "i64.load16_s": 0x32, |
| "i64.load16_u": 0x33, |
| "i64.load32_s": 0x34, |
| "i64.load32_u": 0x35, |
| "i32.store": 0x36, |
| "i64.store": 0x37, |
| "f32.store": 0x38, |
| "f64.store": 0x39, |
| "i32.store8": 0x3a, |
| "i32.store16": 0x3b, |
| "i64.store8": 0x3c, |
| "i64.store16": 0x3d, |
| "i64.store32": 0x3e, |
| "memory.size": 0x3f, |
| "memory.grow": 0x40, |
| "i32.const": 0x41, |
| "i64.const": 0x42, |
| "f32.const": 0x43, |
| "f64.const": 0x44, |
| "i32.eqz": 0x45, |
| "i32.eq": 0x46, |
| "i32.ne": 0x47, |
| "i32.lt_s": 0x48, |
| "i32.lt_u": 0x49, |
| "i32.gt_s": 0x4a, |
| "i32.gt_u": 0x4b, |
| "i32.le_s": 0x4c, |
| "i32.le_u": 0x4d, |
| "i32.ge_s": 0x4e, |
| "i32.ge_u": 0x4f, |
| 'i64.eqz': 0x50, |
| "i64.eq": 0x51, |
| "i64.ne": 0x52, |
| "i64.lt_s": 0x53, |
| "i64.lt_u": 0x54, |
| "i64.gt_s": 0x55, |
| "i64.gt_u": 0x56, |
| "i64.le_s": 0x57, |
| "i64.le_u": 0x58, |
| "i64.ge_s": 0x59, |
| "i64.ge_u": 0x5a, |
| "f32.eq": 0x5b, |
| "f32.ne": 0x5c, |
| "f32.lt": 0x5d, |
| "f32.gt": 0x5e, |
| "f32.le": 0x5f, |
| "f32.ge": 0x60, |
| "f64.eq": 0x61, |
| "f64.ne": 0x62, |
| "f64.lt": 0x63, |
| "f64.gt": 0x64, |
| "f64.le": 0x65, |
| "f64.ge": 0x66, |
| "i32.clz": 0x67, |
| "i32.ctz": 0x68, |
| "i32.popcnt": 0x69, |
| "i32.add": 0x6a, |
| "i32.sub": 0x6b, |
| "i32.mul": 0x6c, |
| "i32.div_s": 0x6d, |
| "i32.div_u": 0x6e, |
| "i32.rem_s": 0x6f, |
| "i32.rem_u": 0x70, |
| "i32.and": 0x71, |
| "i32.or": 0x72, |
| "i32.xor": 0x73, |
| "i32.shl": 0x74, |
| "i32.shr_s": 0x75, |
| "i32.shr_u": 0x76, |
| "i32.rotl": 0x77, |
| "i32.rotr": 0x78, |
| "i64.clz": 0x79, |
| "i64.ctz": 0x7a, |
| "i64.popcnt": 0x7b, |
| "i64.add": 0x7c, |
| "i64.sub": 0x7d, |
| "i64.mul": 0x7e, |
| "i64.div_s": 0x7f, |
| "i64.div_u": 0x80, |
| "i64.rem_s": 0x81, |
| "i64.rem_u": 0x82, |
| "i64.and": 0x83, |
| "i64.or": 0x84, |
| "i64.xor": 0x85, |
| "i64.shl": 0x86, |
| "i64.shr_s": 0x87, |
| "i64.shr_u": 0x88, |
| "i64.rotl": 0x89, |
| "i64.rotr": 0x8a, |
| "f32.abs": 0x8b, |
| "f32.neg": 0x8c, |
| "f32.copysign": 0x8d, |
| "f32.ceil": 0x8e, |
| "f32.floor": 0x8f, |
| "f32.trunc": 0x90, |
| "f32.nearest": 0x91, |
| "f32.sqrt": 0x92, |
| "f32.add": 0x93, |
| "f32.sub": 0x94, |
| "f32.mul": 0x95, |
| "f32.div": 0x96, |
| "f32.min": 0x97, |
| "f32.max": 0x98, |
| "f64.abs": 0x99, |
| "f64.neg": 0x9a, |
| "f64.copysign": 0x9b, |
| "f64.ceil": 0x9c, |
| "f64.floor": 0x9d, |
| "f64.trunc": 0x9e, |
| "f64.nearest": 0x9f, |
| "f64.sqrt": 0xa0, |
| "f64.add": 0xa1, |
| "f64.sub": 0xa2, |
| "f64.mul": 0xa3, |
| "f64.div": 0xa4, |
| "f64.min": 0xa5, |
| "f64.max": 0xa6, |
| "i32.wrap/i64": 0xa7, |
| "i32.trunc_s/f32": 0xa8, |
| "i32.trunc_u/f32": 0xa9, |
| "i32.trunc_s/f64": 0xaa, |
| "i32.trunc_u/f64": 0xab, |
| "i64.extend_s/i32": 0xac, |
| "i64.extend_u/i32": 0xad, |
| "i64.trunc_s/f32": 0xae, |
| "i64.trunc_u/f32": 0xaf, |
| "i64.trunc_s/f64": 0xb0, |
| "i64.trunc_u/f64": 0xb1, |
| "f32.convert_s/i32": 0xb2, |
| "f32.convert_u/i32": 0xb3, |
| "f32.convert_s/i64": 0xb4, |
| "f32.convert_u/i64": 0xb5, |
| "f32.demote/f64": 0xb6, |
| "f64.convert_s/i32": 0xb7, |
| "f64.convert_u/i32": 0xb8, |
| "f64.convert_s/i64": 0xb9, |
| "f64.convert_u/i64": 0xba, |
| "f64.promote/f32": 0xbb, |
| "i32.reinterpret/f32": 0xbc, |
| "i64.reinterpret/f64": 0xbd, |
| "f32.reinterpret/i32": 0xbe, |
| "f64.reinterpret/i64": 0xbf, |
| |
| # bulk memory |
| "memory.init": (0xfc, 0x08), |
| "data.drop": (0xfc, 0x09), |
| "memory.copy": (0xfc, 0x0a), |
| "memory.fill": (0xfc, 0x0b), |
| "table.init": (0xfc, 0x0c), |
| "elem.drop": (0xfc, 0x0d), |
| "table.copy": (0xfc, 0x0e), |
| |
| # exceptions |
| "try": 0x06, |
| "catch_all": 0x19, |
| } |
| |
| keywords = { |
| 'func': 'FUNC', |
| 'section': 'SECTION', |
| 'leb_i32': 'LEB_I32', |
| 'leb_i64': 'LEB_I64', |
| 'leb_u32': 'LEB_U32', |
| 'f32': 'F32', |
| 'f64': 'F64', |
| 'str': 'STR', |
| } |
| |
| # lexer ### |
| |
| tokens = tuple(keywords.values()) + ( |
| 'BYTE', |
| 'INT', |
| 'FLOAT', |
| 'STRING', |
| 'NAME', |
| 'NAMED_VALUE', |
| 'LPAREN', |
| 'RPAREN', |
| 'LBRACE', |
| 'RBRACE', |
| 'LBRACKET', |
| 'RBRACKET',) |
| |
| t_LPAREN = r'\(' |
| t_RPAREN = r'\)' |
| t_LBRACE = r'{' |
| t_RBRACE = r'}' |
| t_LBRACKET = r'\[' |
| t_RBRACKET = r'\]' |
| t_ignore = ' \t' |
| |
| |
| def t_COMMENT(t): |
| r';;.*' |
| pass |
| |
| |
| def t_INT(t): |
| r'\-?(0[xX][0-9a-fA-F]+|[0-9]+)' |
| if t.value.lower().startswith('0x'): |
| t.value = int(t.value, 16) |
| else: |
| t.value = int(t.value) |
| |
| if 0 <= t.value < 256: |
| t.type = 'BYTE' |
| return t |
| |
| |
| def t_FLOAT(t): |
| r'\-?([0-9]*\.?[0-9]+|[0-9]+\.?[0-9]*)([eE][0-9]+)?' |
| t.value = float(t.value) |
| return t |
| |
| |
| def t_STRING(t): |
| r'\'[^\']*\'|\"[^\"]*\"' |
| t.value = t.value[1:-1] |
| return t |
| |
| |
| def t_NAME(t): |
| r'[a-zA-Z][a-zA-Z0-9_\.\/]*' |
| if t.value in NAMED_VALUES: |
| t.type = 'NAMED_VALUE' |
| t.value = NAMED_VALUES[t.value] |
| elif t.value in keywords: |
| t.type = keywords[t.value] |
| return t |
| |
| |
| def t_newline(t): |
| r'\n+' |
| t.lexer.lineno += len(t.value) |
| |
| |
| def t_error(t): |
| print("Illegal character '%s'" % t.value[0]) |
| t.lexer.skip(1) |
| |
| |
| lexer = lex.lex() |
| |
| # parser ### |
| |
| |
| def LebLoop(data, v, cond): |
| while True: |
| byte = v & 0x7f |
| v >>= 7 |
| if cond(v, byte): |
| data.append(byte) |
| break |
| else: |
| data.append(byte | 0x80) |
| |
| |
| def WriteUnsignedLeb(data, v, max_size): |
| result = [] |
| LebLoop(result, v, lambda v, byte: v == 0) |
| assert len(result) <= max_size |
| data.extend(result) |
| |
| |
| def WriteLebU32(data, v): |
| WriteUnsignedLeb(data, v, 5) |
| |
| |
| def WriteSignedLeb(data, v, max_size): |
| result = [] |
| if v < 0: |
| LebLoop(result, v, lambda v, byte: v == -1 and byte & 0x40) |
| else: |
| LebLoop(result, v, lambda v, byte: v == 0 and not byte & 0x40) |
| assert len(result) <= max_size |
| data.extend(result) |
| |
| |
| def WriteLebI32(data, v): |
| WriteSignedLeb(data, v, 5) |
| |
| |
| def WriteLebI64(data, v): |
| WriteSignedLeb(data, v, 10) |
| |
| |
| def WriteF32(data, v): |
| data.extend(ord(b) for b in struct.pack('<f', v)) |
| |
| |
| def WriteF64(data, v): |
| data.extend(ord(b) for b in struct.pack('<d', v)) |
| |
| |
| def WriteString(data, s): |
| data.extend(ord(c) for c in s) |
| |
| |
| def p_data_byte(p): |
| 'data : data BYTE' |
| p[0] = p[1] |
| p[0].append(p[2]) |
| |
| |
| def p_data_name(p): |
| '''data : data NAME LBRACKET data RBRACKET |
| | data FUNC LBRACKET data RBRACKET''' |
| p[0] = p[1] |
| # name is only used for documentation |
| p[0].extend(p[4]) |
| |
| |
| def p_data_named_value(p): |
| 'data : data NAMED_VALUE' |
| p[0] = p[1] |
| if type(p[2]) is tuple: |
| p[0].extend(p[2]) |
| else: |
| p[0].append(p[2]) |
| |
| |
| def p_data_section(p): |
| 'data : data SECTION LPAREN NAMED_VALUE RPAREN LBRACE data RBRACE' |
| p[0] = p[1] |
| section_data = p[7] |
| p[0].append(p[4]) |
| WriteLebU32(p[0], len(section_data)) |
| p[0].extend(section_data) |
| |
| |
| def p_data_user_section(p): |
| 'data : data SECTION LPAREN STRING RPAREN LBRACE data RBRACE' |
| p[0] = p[1] |
| name = p[4] |
| section_data = p[7] |
| p[0].append(0) # 0 is the section code for "user" |
| section_name_data = [] |
| WriteLebU32(section_name_data, len(name)) |
| WriteString(section_name_data, name) |
| WriteLebU32(p[0], len(section_name_data) + len(section_data)) |
| p[0].extend(section_name_data) |
| p[0].extend(section_data) |
| |
| |
| def p_data_func(p): |
| 'data : data FUNC LBRACE data RBRACE' |
| p[0] = p[1] |
| func_data = p[4] |
| func_data.append(0xb) # end opcode |
| WriteLebU32(p[0], len(func_data)) |
| p[0].extend(func_data) |
| |
| |
| def p_data_str(p): |
| 'data : data STR LPAREN STRING RPAREN' |
| p[0] = p[1] |
| s = p[4] |
| WriteLebU32(p[0], len(s)) |
| WriteString(p[0], s) |
| |
| |
| def p_data_leb_i32(p): |
| '''data : data LEB_I32 LPAREN INT RPAREN |
| | data LEB_I32 LPAREN BYTE RPAREN''' |
| p[0] = p[1] |
| WriteLebI32(p[0], p[4]) |
| |
| |
| def p_data_leb_i64(p): |
| '''data : data LEB_I64 LPAREN INT RPAREN |
| | data LEB_I64 LPAREN BYTE RPAREN''' |
| p[0] = p[1] |
| WriteLebI64(p[0], p[4]) |
| |
| |
| def p_data_leb_u32(p): |
| '''data : data LEB_U32 LPAREN INT RPAREN |
| | data LEB_U32 LPAREN BYTE RPAREN''' |
| p[0] = p[1] |
| WriteLebU32(p[0], p[4]) |
| |
| |
| def p_data_f32(p): |
| 'data : data F32 LPAREN FLOAT RPAREN' |
| p[0] = p[1] |
| WriteF32(p[0], p[4]) |
| |
| |
| def p_data_f64(p): |
| 'data : data F64 LPAREN FLOAT RPAREN' |
| p[0] = p[1] |
| WriteF64(p[0], p[4]) |
| |
| |
| def p_data_string(p): |
| 'data : data STRING' |
| p[0] = p[1] |
| WriteString(p[0], p[2]) |
| |
| |
| def p_data_empty(p): |
| 'data :' |
| p[0] = [] |
| |
| |
| def p_error(p): |
| raise Error('%d: syntax error, %s' % (p.lineno, p)) |
| |
| |
| parser = yacc.yacc(debug=False, tabmodule='gen_wasm', |
| debugfile='gen_wasm_debug.txt', outputdir=OUT_DIR) |
| |
| ################################################################################ |
| |
| |
| def Run(input_file_name): |
| with open(input_file_name) as input_file: |
| input_data = input_file.read() |
| data = parser.parse(input_data) |
| # convert to bytes |
| data = bytearray(data) |
| return data |
| |
| |
| def main(args): |
| arg_parser = argparse.ArgumentParser() |
| arg_parser.add_argument('-o', '--output', metavar='PATH', |
| help='output file.') |
| arg_parser.add_argument('-v', '--verbose', |
| help='print more diagnotic messages.', |
| action='store_true') |
| arg_parser.add_argument('file', help='input file.') |
| options = arg_parser.parse_args(args) |
| data = Run(options.file) |
| if options.output: |
| with open(options.output, 'wb') as output_file: |
| output_file.write(data) |
| else: |
| sys.stdout.writelines(Hexdump(data)) |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| try: |
| sys.exit(main(sys.argv[1:])) |
| except Error as e: |
| sys.stderr.write(str(e) + '\n') |
| sys.exit(1) |