UNPKG

llparse

Version:

Compile incremental parsers to C code

164 lines 7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CCompiler = void 0; const constants_1 = require("./constants"); const compilation_1 = require("./compilation"); const code_1 = require("./code"); const node_1 = require("./node"); const transform_1 = require("./transform"); class CCompiler { options; constructor(container, options) { this.options = options; container.add(constants_1.CONTAINER_KEY, { code: code_1.default, node: node_1.default, transform: transform_1.default }); } compile(info) { const compilation = new compilation_1.Compilation(info.prefix, info.properties, info.resumptionTargets, this.options); const out = []; out.push('#include <stdlib.h>'); out.push('#include <stdint.h>'); out.push('#include <string.h>'); out.push(''); // NOTE: Inspired by https://github.com/h2o/picohttpparser // TODO(indutny): Windows support for SSE4.2. // See: https://github.com/nodejs/llparse/pull/24#discussion_r299789676 // (There is no `__SSE4_2__` define for MSVC) out.push('#ifdef __SSE4_2__'); out.push(' #ifdef _MSC_VER'); out.push(' #include <nmmintrin.h>'); out.push(' #else /* !_MSC_VER */'); out.push(' #include <x86intrin.h>'); out.push(' #endif /* _MSC_VER */'); out.push('#endif /* __SSE4_2__ */'); out.push(''); out.push('#if defined(__ARM_NEON__) || defined(__ARM_NEON)'); out.push(' #include <arm_neon.h>'); out.push('#endif /* __ARM_NEON__ */'); out.push(''); out.push('#ifdef __wasm__'); out.push(' #include <wasm_simd128.h>'); out.push('#endif /* __wasm__ */'); out.push(''); out.push('#ifdef _MSC_VER'); out.push(' #define ALIGN(n) _declspec(align(n))'); out.push(' #define UNREACHABLE __assume(0)'); out.push('#else /* !_MSC_VER */'); out.push(' #define ALIGN(n) __attribute__((aligned(n)))'); out.push(' #define UNREACHABLE __builtin_unreachable()'); out.push('#endif /* _MSC_VER */'); out.push(''); out.push(`#include "${this.options.header || info.prefix}.h"`); out.push(``); out.push(`typedef int (*${info.prefix}__span_cb)(`); out.push(` ${info.prefix}_t*, const char*, const char*);`); out.push(''); // Queue span callbacks to be built before `executeSpans()` code gets called // below. compilation.reserveSpans(info.spans); const root = info.root; const rootState = root.get(constants_1.CONTAINER_KEY) .build(compilation); compilation.buildGlobals(out); out.push(''); out.push(`int ${info.prefix}_init(${info.prefix}_t* ${constants_1.ARG_STATE}) {`); out.push(` memset(${constants_1.ARG_STATE}, 0, sizeof(*${constants_1.ARG_STATE}));`); out.push(` ${constants_1.ARG_STATE}->_current = (void*) (intptr_t) ${rootState};`); out.push(' return 0;'); out.push('}'); out.push(''); out.push(`static llparse_state_t ${info.prefix}__run(`); out.push(` ${info.prefix}_t* ${constants_1.ARG_STATE},`); out.push(` const unsigned char* ${constants_1.ARG_POS},`); out.push(` const unsigned char* ${constants_1.ARG_ENDPOS}) {`); out.push(` int ${constants_1.VAR_MATCH};`); out.push(` switch ((llparse_state_t) (intptr_t) ` + `${compilation.currentField()}) {`); let tmp = []; compilation.buildResumptionStates(tmp); compilation.indent(out, tmp, ' '); out.push(' default:'); out.push(' UNREACHABLE;'); out.push(' }'); tmp = []; compilation.buildInternalStates(tmp); compilation.indent(out, tmp, ' '); out.push('}'); out.push(''); out.push(`int ${info.prefix}_execute(${info.prefix}_t* ${constants_1.ARG_STATE}, ` + `const char* ${constants_1.ARG_POS}, const char* ${constants_1.ARG_ENDPOS}) {`); out.push(' llparse_state_t next;'); out.push(''); out.push(' /* check lingering errors */'); out.push(` if (${compilation.errorField()} != 0) {`); out.push(` return ${compilation.errorField()};`); out.push(' }'); out.push(''); tmp = []; this.restartSpans(compilation, info, tmp); compilation.indent(out, tmp, ' '); const args = [ compilation.stateArg(), `(const unsigned char*) ${compilation.posArg()}`, `(const unsigned char*) ${compilation.endPosArg()}`, ]; out.push(` next = ${info.prefix}__run(${args.join(', ')});`); out.push(` if (next == ${constants_1.STATE_ERROR}) {`); out.push(` return ${compilation.errorField()};`); out.push(' }'); out.push(` ${compilation.currentField()} = (void*) (intptr_t) next;`); out.push(''); tmp = []; this.executeSpans(compilation, info, tmp); compilation.indent(out, tmp, ' '); out.push(' return 0;'); out.push('}'); return out.join('\n'); } restartSpans(ctx, info, out) { if (info.spans.length === 0) { return; } out.push('/* restart spans */'); for (const span of info.spans) { const posField = ctx.spanPosField(span.index); out.push(`if (${posField} != NULL) {`); out.push(` ${posField} = (void*) ${ctx.posArg()};`); out.push('}'); } out.push(''); } executeSpans(ctx, info, out) { if (info.spans.length === 0) { return; } out.push('/* execute spans */'); for (const span of info.spans) { const posField = ctx.spanPosField(span.index); let callback; if (span.callbacks.length === 1) { callback = ctx.buildCode(ctx.unwrapCode(span.callbacks[0])); } else { callback = `(${info.prefix}__span_cb) ` + ctx.spanCbField(span.index); callback = `(${callback})`; } const args = [ ctx.stateArg(), posField, `(const char*) ${ctx.endPosArg()}`, ]; out.push(`if (${posField} != NULL) {`); out.push(' int error;'); out.push(''); out.push(` error = ${callback}(${args.join(', ')});`); // TODO(indutny): de-duplicate this here and in SpanEnd out.push(' if (error != 0) {'); out.push(` ${ctx.errorField()} = error;`); out.push(` ${ctx.errorPosField()} = ${ctx.endPosArg()};`); out.push(' return error;'); out.push(' }'); out.push('}'); } out.push(''); } } exports.CCompiler = CCompiler; //# sourceMappingURL=index.js.map