UNPKG

llparse

Version:

Compile incremental parsers to C code

260 lines 7.97 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Compilation = void 0; const assert = require("assert"); const constants_1 = require("./constants"); const match_sequence_1 = require("./helpers/match-sequence"); // Number of hex words per line of blob declaration const BLOB_GROUP_SIZE = 11; class Compilation { prefix; properties; options; stateMap = new Map(); blobs = new Map(); codeMap = new Map(); matchSequence = new Map(); resumptionTargets = new Set(); constructor(prefix, properties, resumptionTargets, options) { this.prefix = prefix; this.properties = properties; this.options = options; for (const node of resumptionTargets) { this.resumptionTargets.add(constants_1.STATE_PREFIX + node.ref.id.name); } } buildStateEnum(out) { out.push('enum llparse_state_e {'); out.push(` ${constants_1.STATE_ERROR},`); for (const stateName of this.stateMap.keys()) { if (this.resumptionTargets.has(stateName)) { out.push(` ${stateName},`); } } out.push('};'); out.push('typedef enum llparse_state_e llparse_state_t;'); } buildBlobs(out) { if (this.blobs.size === 0) { return; } for (const blob of this.blobs.values()) { const buffer = blob.buffer; let align = ''; if (blob.alignment) { align = ` ALIGN(${blob.alignment})`; } if (blob.alignment) { out.push('#ifdef __SSE4_2__'); } out.push(`static const unsigned char${align} ${blob.name}[] = {`); for (let i = 0; i < buffer.length; i += BLOB_GROUP_SIZE) { const limit = Math.min(buffer.length, i + BLOB_GROUP_SIZE); const hex = []; for (let j = i; j < limit; j++) { const value = buffer[j]; assert(value !== undefined); hex.push(this.toChar(value)); } let line = ' ' + hex.join(', '); if (limit !== buffer.length) { line += ','; } out.push(line); } out.push(`};`); if (blob.alignment) { out.push('#endif /* __SSE4_2__ */'); } } out.push(''); } buildMatchSequence(out) { if (this.matchSequence.size === 0) { return; } match_sequence_1.MatchSequence.buildGlobals(out); out.push(''); for (const match of this.matchSequence.values()) { match.build(this, out); out.push(''); } } reserveSpans(spans) { for (const span of spans) { for (const callback of span.callbacks) { this.buildCode(this.unwrapCode(callback)); } } } debug(out, message) { if (this.options.debug === undefined) { return; } const args = [ this.stateArg(), `(const char*) ${this.posArg()}`, `(const char*) ${this.endPosArg()}`, ]; out.push(`${this.options.debug}(${args.join(', ')},`); out.push(` ${this.cstring(message)});`); } buildGlobals(out) { if (this.options.debug !== undefined) { out.push(`void ${this.options.debug}(`); out.push(` ${this.prefix}_t* s, const char* p, const char* endp,`); out.push(' const char* msg);'); } this.buildBlobs(out); this.buildMatchSequence(out); this.buildStateEnum(out); for (const code of this.codeMap.values()) { out.push(''); code.build(this, out); } } buildResumptionStates(out) { this.stateMap.forEach((lines, name) => { if (!this.resumptionTargets.has(name)) { return; } out.push(`case ${name}:`); out.push(`${constants_1.LABEL_PREFIX}${name}: {`); lines.forEach((line) => out.push(` ${line}`)); out.push(' UNREACHABLE;'); out.push('}'); }); } buildInternalStates(out) { this.stateMap.forEach((lines, name) => { if (this.resumptionTargets.has(name)) { return; } out.push(`${constants_1.LABEL_PREFIX}${name}: {`); lines.forEach((line) => out.push(` ${line}`)); out.push(' UNREACHABLE;'); out.push('}'); }); } addState(state, lines) { assert(!this.stateMap.has(state)); this.stateMap.set(state, lines); } buildCode(code) { if (this.codeMap.has(code.ref.name)) { assert.strictEqual(this.codeMap.get(code.ref.name), code, `Code name conflict for "${code.ref.name}"`); } else { this.codeMap.set(code.ref.name, code); } return code.ref.name; } getFieldType(field) { for (const property of this.properties) { if (property.name === field) { return property.ty; } } throw new Error(`Field "${field}" not found`); } // Helpers unwrapCode(code) { const container = code; return container.get(constants_1.CONTAINER_KEY); } unwrapNode(node) { const container = node; return container.get(constants_1.CONTAINER_KEY); } unwrapTransform(node) { const container = node; return container.get(constants_1.CONTAINER_KEY); } indent(out, lines, pad) { for (const line of lines) { out.push(`${pad}${line}`); } } // MatchSequence cache getMatchSequence(transform, select) { const wrap = this.unwrapTransform(transform); let res; if (this.matchSequence.has(wrap.ref.name)) { res = this.matchSequence.get(wrap.ref.name); } else { res = new match_sequence_1.MatchSequence(wrap); this.matchSequence.set(wrap.ref.name, res); } return res.getName(); } // Arguments stateArg() { return constants_1.ARG_STATE; } posArg() { return constants_1.ARG_POS; } endPosArg() { return constants_1.ARG_ENDPOS; } matchVar() { return constants_1.VAR_MATCH; } // State fields indexField() { return this.stateField('_index'); } currentField() { return this.stateField('_current'); } errorField() { return this.stateField('error'); } reasonField() { return this.stateField('reason'); } errorPosField() { return this.stateField('error_pos'); } spanPosField(index) { return this.stateField(`_span_pos${index}`); } spanCbField(index) { return this.stateField(`_span_cb${index}`); } stateField(name) { return `${this.stateArg()}->${name}`; } // Globals cstring(value) { return JSON.stringify(value); } blob(value, alignment) { if (this.blobs.has(value)) { return this.blobs.get(value).name; } const res = constants_1.BLOB_PREFIX + this.blobs.size; this.blobs.set(value, { alignment, buffer: value, name: res, }); return res; } toChar(value) { const ch = String.fromCharCode(value); // `'`, `\` if (value === 0x27 || value === 0x5c) { return `'\\${ch}'`; } else if (value >= 0x20 && value <= 0x7e) { return `'${ch}'`; } else { return `0x${value.toString(16)}`; } } } exports.Compilation = Compilation; //# sourceMappingURL=compilation.js.map