UNPKG

walt-compiler

Version:

Alternative syntax for WebAssembly text format

146 lines (129 loc) 3.74 kB
/** * Static value plugin * * @flow */ import Syntax from 'walt-syntax'; import { stringEncoder } from '../utils/string'; import OutputStream from '../utils/output-stream'; import wasmTypes from 'wasm-types'; import type { SemanticPlugin } from '../flow/types'; const escapeMap = { ['\\0']: 0x00, ['\\a']: 0x07, ['\\b']: 0x08, ['\\t']: 0x09, ['\\n']: 0x0a, ['\\v']: 0x0b, ['\\f']: 0x0c, ['\\r']: 0x0d, ["\\'"]: 0x27, }; const sizeMap = { i64: 8, f64: 8, i32: 4, f32: 4, }; function encodeArray(array, type) { const stream = new OutputStream(); const encodeType = wasmTypes[type]; array.forEach(v => { stream.push(encodeType, v, String(v)); }); return stream; } export default function Strings(): SemanticPlugin { let count = 0; return { semantics: ({ stmt }) => ({ [Syntax.StaticDeclaration]: _next => ([node, context], transform) => { const { userTypes, statics } = context; const bareType = String(node.type).slice(0, -2); const typeSize = sizeMap[bareType]; const meta = node.params.reduce( (acc, v, i) => { const n = transform([v, context]); acc.OBJECT_SIZE += typeSize; acc.TYPE_OBJECT[i] = i * typeSize; acc.OBJECT_KEY_TYPES[i] = bareType; acc.VALUES.push(Number(n.value)); return acc; }, { OBJECT_SIZE: 0, TYPE_OBJECT: {}, OBJECT_KEY_TYPES: {}, VALUES: [], STATIC: bareType, } ); const uid = `__auto_gen_${node.value}_${count}`; count += 1; userTypes[uid] = { ...node, value: uid, Type: Syntax.Type, meta, params: [], }; statics[uid] = encodeArray(meta.VALUES, bareType); // Short circuit the middleware and instead transform a declaration return transform([ { ...node, meta, type: uid, Type: Syntax.ImmutableDeclaration, params: [ { ...node.params[0], value: uid, Type: Syntax.StaticValueList, }, ], }, context, ]); }, [Syntax.ArraySubscript]: next => ([node, context], transform) => { const [target, offset] = node.params.map(p => transform([p, context])); if (!target.meta.STATIC) { return next([node, context]); } const shift = { i32: 2, f32: 2, i64: 3, f64: 3 }[target.meta.STATIC]; return transform([ stmt`${ target.meta.STATIC }.load(${target} + (${offset} << ${shift}));`, context, ]); }, [Syntax.CharacterLiteral]: _ => ([node, context], transform) => { const codePoint = escapeMap[node.value] || node.value.codePointAt(0); return transform([ { ...node, Type: 'Constant', type: 'i32', value: String(codePoint), }, context, ]); }, [Syntax.StringLiteral]: _ignore => args => { const [stringLiteral, context] = args; const { statics } = context; const { value } = stringLiteral; // did we already encode the static? if (!(value in statics)) { statics[value] = stringEncoder(value); } // It's too early to transform a string at this point // we need additional information, only available in the generator. // This also avoids doing the work in two places, in semantics AND gen return stringLiteral; }, }), }; }