UNPKG

struct-compile

Version:

Create a JavaScript class from a C structure

162 lines (141 loc) 4.36 kB
import { parserInstance } from './parser.js'; import { trim, isCapital } from './strings.js'; //const BaseCstVisitor = parserInstance.getBaseCstVisitorConstructor(); const BaseCstVisitor = parserInstance.getBaseCstVisitorConstructorWithDefaults(); export class StructVisitor extends BaseCstVisitor { constructor() { super(); this.metaRex = /@\w+/g; this.validateVisitor(); } structs(ctx) { // ctx.columnsList is an array, while this.visit accepts a CSTNode // but if an array is passed to this.visit it will act as though the first element of the array has been passed. // this means "this.visit(ctx.columnsList)" is equivalent to "this.visit(ctx.columnsList[0])" if (!ctx?.struct?.length) return []; const structs = Array.from({ length: ctx.struct.length }, (_, i) => this.visit(ctx.struct[i])); return structs; } struct(ctx) { const name = ctx.Identifier?.[0]?.image ?? null; const attributes = ctx.attributes ? this.visit(ctx.attributes) : null; const members = Array.from({ length: ctx.member.length ?? 0 }, (_, i) => this.visit(ctx.member[i])); const comment = ctx.OneLineComment?.[0]?.image ?? null; const meta = this._buildMeta(comment); return this._buildComment({ name, attributes, members, meta, }, comment); } _buildComment(res, comment) { if (comment) { res.comment = comment.slice(comment.indexOf('//') + 2).trim(); } return res; } _buildMeta(comment) { const meta = {}; let match; while (match = this.metaRex.exec(comment)) { meta[match[0].slice(1)] = true; } return meta; } member(ctx) { const comment = ctx.OneLineComment?.[0]?.image ?? null; const vars = Array.from({ length: ctx?.memberName?.length ?? 0 }, (_, i) => this.visit(ctx.memberName[i])); let type = trim(ctx.TypeKeyword[0].image); if (ctx.Pointer) { type += ' '; type += ctx.Pointer.map(e => e.image).join(''); } const meta = this._buildMeta(comment); return this._buildComment({ type, vars, meta, }, comment); } memberName(ctx) { const name = ctx.Identifier[0].image; const d = Array.from({ length: ctx.squareBracketExpression?.length ?? 0 }, (_, i) => this.visit(ctx.squareBracketExpression[i], { forMember: true })?.value); const res = { name, d, }; if (ctx.Colon) { const { Num } = ctx; res.bits = parseInt(Num[0].image); } return res; } attributes(ctx) { const tokens = this.visit(ctx.bracketExpression, { isAttr: true }); const res = {}; for (let i = 0; i < tokens.length; ++i) { if (tokens[i] == 'packed' || tokens[i] == '__packed__') { res.packed = true; } if (tokens[i] == 'aligned' || tokens[i] == '__aligned__') { let num = 0; if (i < tokens.length - 1 && !Number.isNaN(num = parseInt(tokens[i + 1]))) { res.aligned = num; } else { res.aligned = true; } } } return res; } bracketExpression(ctx, params = {}) { if (params.isAttr) { const toLift = !params.isChild; params.isChild = true; let res = []; for (const key of Object.keys(ctx)) { if (key == 'RoundBracketOpen' || key == 'RoundBracketClose') continue; if (isCapital(key)) { res.push(...ctx[key].map(e => ({ value: e, type: key, offset: e.startOffset }))); } else { res.push(...Array.from({ length: ctx[key].length }, (_, i) => this.visit(ctx[key][i], params))); } } res = res.flat().sort((a, b) => a.offset - b.offset); if (toLift) { res = res.map(e => e.value.image); } return res; } else { throw new Error('Unable to parse'); } } squareBracketExpression(ctx, params = {}) { if (params.forMember) { const offset = ctx.SquareBracketOpen[0].startOffset; if (ctx.Num) { return { offset, value: parseInt(ctx.Num[0].image), }; } return { offset, value: 0, } } else { throw new Error('Unable to parse'); } } } export const interpreter = new StructVisitor; export function traverseResult(cstOutput) { return interpreter.visit(cstOutput); };