walt-compiler
Version:
Alternative syntax for WebAssembly text format
146 lines (129 loc) • 3.74 kB
JavaScript
/**
* 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;
},
}),
};
}