UNPKG

walt-compiler

Version:

Alternative syntax for WebAssembly text format

242 lines (226 loc) 6.93 kB
/* istanbul ignore file */ // @flow import walkNode from 'walt-parser-tools/walk-node'; import Syntax from 'walt-syntax'; import { GLOBAL_INDEX } from '../semantics/metadata'; import { opcodeFromOperator, getTypecastOpcode } from '../emitter/opcode'; import { parseBounds } from './resizable-limits'; import type { NodeType, TypeCastType } from '../flow/types'; const getText = (node: NodeType): string => { const value = node.value || '??'; const hasType = node.type; const type = hasType || 'i32'; const op = opcodeFromOperator({ value, type }); if (!hasType) { return op.text.replace('i32', '??'); } return op.text; }; const parseParams = (node: NodeType): string => { const params = []; walkNode({ [Syntax.Pair]: (pair, _) => { params.push(`${pair.params[0].value} ${pair.params[1].value}`); }, [Syntax.Type]: p => { params.push(p.value); }, })(node); return params.length ? ' param(' + params.join(' ') + ')' : ''; }; const parseResult = (node: ?NodeType): string => { if (node == null) { return ''; } return ' (result ' + (node.type || '??') + ')'; }; const typedefString = (node: NodeType): string => { const [paramsNode, resultNode] = node.params; return ( '(type ' + node.value + ` (func${parseParams(paramsNode)}${parseResult(resultNode)}))` ); }; const printFormatted = (add, print, { value, params }) => { if (params.filter(Boolean).length) { add(`(${value}`, 2); params.forEach(print); add(')', 0, -2); } else { add(`(${value})`, 0, 0); } }; const getPrinters = add => ({ [Syntax.Import]: (node, _print) => { const [nodes, mod] = node.params; walkNode({ [Syntax.Pair]: ({ params }, _) => { const { value: field } = params[0]; const type = params[1]; if (type.value === 'Memory') { const memory = parseBounds(type); add( `(import "${mod.value}" "${field}" (memory ${memory.initial}${ memory.max ? memory.max : '' }))` ); } else { add(`(import "${mod.value}" "${field}" ${typedefString(type)})`); } }, [Syntax.Identifier]: (missing, _) => { const { value } = missing; add(`(import "${mod.value}" "${value}" (type ??))`); }, })(nodes); }, [Syntax.Export]: (node, print) => { add('(export', 2); node.params.forEach(print); add(')', 0, -2); }, [Syntax.GenericType]: (node, _print) => { add(';; Pseudo type', 0, 0); add('(type-generic ' + node.value + ')', 0, 0); }, [Syntax.FunctionCall]: ({ value, params }, print) => { printFormatted(add, print, { value: `call ${value}`, params }); }, [Syntax.Block]: ({ params }, print) => { printFormatted(add, print, { value: 'block', params }); }, [Syntax.NativeMethod]: (node, print) => { printFormatted(add, print, node); }, [Syntax.BinaryExpression]: (node: NodeType, print) => { const text = getText(node); printFormatted(add, print, { value: text, params: node.params }); }, [Syntax.ArraySubscript]: ({ params }, print) => { add(';; unparsed', 0, 0); printFormatted(add, print, { value: 'subscript', params }); }, [Syntax.Typedef]: (node, _) => { add(typedefString(node)); }, [Syntax.Struct]: (node, print) => { add(';; Pseudo struct type', 0, 0); printFormatted(add, print, { value: 'type-struct ' + node.value, params: node.params, }); }, [Syntax.Identifier]: node => { const scope = node.meta[GLOBAL_INDEX] != null ? 'global' : 'local'; add(`(get_${scope} ${node.value})`); }, [Syntax.Constant]: node => { add(`(${String(node.type)}.const ${node.value})`); }, [Syntax.FunctionPointer]: node => { add(`(${String(node.type)}.table_pointer ${node.value})`); }, [Syntax.FunctionDeclaration]: (node, print) => { const [params, result, ...rest] = node.params; add(`(func ${node.value}${parseParams(params)}${parseResult(result)}`, 2); rest.forEach(print); add(')', 0, -2); }, [Syntax.ReturnStatement]: ({ params }, print) => { printFormatted(add, print, { value: 'return', params }); }, [Syntax.Declaration]: (node, print) => { add('(local ' + node.value + ' ' + String(node.type), 2, 0); node.params.forEach(print); add(')', 0, -2); }, [Syntax.ImmutableDeclaration]: (node, print) => { const scope = node.meta[GLOBAL_INDEX] != null ? 'global' : 'local'; if (node.type === 'Memory') { const memory = parseBounds(node); add(`(memory ${memory.initial}${memory.max ? ` ${memory.max}` : ''})`); } else { add(`(${scope} ` + node.value + ' ' + String(node.type), 2, 0); node.params.forEach(print); add(')', 0, -2); } }, [Syntax.StringLiteral]: node => { add(`; string "${node.value}"`, 0, 0); add('(i32.const ??)', 0, 0); }, [Syntax.Type]: node => { add(node.value); }, [Syntax.TypeCast]: (node: TypeCastType, print) => { const from = node.params[0]; const op = getTypecastOpcode(String(node.type), from.type); add('(' + op.text, 2); node.params.forEach(print); add(')', 0, -2); }, [Syntax.Access]: ({ params }, print) => { add(';; unparsed', 0, 0); printFormatted(add, print, { value: 'access', params }); }, [Syntax.MemoryAssignment]: (node, print) => { add('(' + String(node.type) + '.store', 2, 0); node.params.forEach(print); add(')', 0, -2); }, [Syntax.Assignment]: (node, print) => { const [target, ...params] = node.params; const scope = target.meta[GLOBAL_INDEX] != null ? 'global' : 'local'; add(`(set_${scope} ${target.value}`, 2); if ([Syntax.ArraySubscript, Syntax.Access].includes(target.Type)) { print(target); } params.forEach(print); add(')', 0, -2); }, [Syntax.TernaryExpression]: (node, print) => { const [lhs, rhs, condition] = node.params; add('(select', 2); print(lhs); print(rhs); print(condition); add(')', 0, -2); }, [Syntax.IfThenElse]: (node, print) => { const [condition, then, ...rest] = node.params; add('(if', 2); print(condition); add('(then', 2); print(then); add(')', 0, -2); if (rest.length > 0) { add('(else', 2); rest.forEach(print); add(')', 0, -2); } add(')', 0, -2); }, [Syntax.ObjectLiteral]: (_, __) => {}, }); const printNode = (node?: NodeType): string => { if (node == null) { return ''; } let depth = 0; const offsets = []; const pieces = []; const add = (piece, post = 0, pre = 0) => { depth += pre; pieces.push(piece); offsets.push(depth + piece.length); depth += post; }; walkNode(getPrinters(add))(node); const result = pieces.reduce((acc, val, i) => { acc += val.padStart(offsets[i], ' ') + '\n'; return acc; }, ''); return result; }; export default printNode;