blob: 767647288139baabf721b501ebf86ccb6d029bab [file] [edit]
#!/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)