diff options
Diffstat (limited to 'llparse/src/compiler')
-rw-r--r-- | llparse/src/compiler/header-builder.ts | 80 | ||||
-rw-r--r-- | llparse/src/compiler/index.ts | 88 |
2 files changed, 168 insertions, 0 deletions
diff --git a/llparse/src/compiler/header-builder.ts b/llparse/src/compiler/header-builder.ts new file mode 100644 index 0000000..9f5bee7 --- /dev/null +++ b/llparse/src/compiler/header-builder.ts @@ -0,0 +1,80 @@ +import * as frontend from 'llparse-frontend'; +import source = frontend.source; + +export interface IHeaderBuilderOptions { + readonly prefix: string; + readonly headerGuard?: string; + readonly properties: ReadonlyArray<source.Property>; + readonly spans: ReadonlyArray<frontend.SpanField>; +} + +export class HeaderBuilder { + public build(options: IHeaderBuilderOptions): string { + let res = ''; + const PREFIX = options.prefix.toUpperCase().replace(/[^a-z]/gi, '_'); + const DEFINE = options.headerGuard === undefined ? + `INCLUDE_${PREFIX}_H_` : options.headerGuard; + + res += `#ifndef ${DEFINE}\n`; + res += `#define ${DEFINE}\n`; + res += '#ifdef __cplusplus\n'; + res += 'extern "C" {\n'; + res += '#endif\n'; + res += '\n'; + + res += '#include <stdint.h>\n'; + res += '\n'; + + // Structure + res += `typedef struct ${options.prefix}_s ${options.prefix}_t;\n`; + res += `struct ${options.prefix}_s {\n`; + res += ' int32_t _index;\n'; + + for (const [ index, field ] of options.spans.entries()) { + res += ` void* _span_pos${index};\n`; + if (field.callbacks.length > 1) { + res += ` void* _span_cb${index};\n`; + } + } + + res += ' int32_t error;\n'; + res += ' const char* reason;\n'; + res += ' const char* error_pos;\n'; + res += ' void* data;\n'; + res += ' void* _current;\n'; + + for (const prop of options.properties) { + let ty: string; + if (prop.ty === 'i8') { + ty = 'uint8_t'; + } else if (prop.ty === 'i16') { + ty = 'uint16_t'; + } else if (prop.ty === 'i32') { + ty = 'uint32_t'; + } else if (prop.ty === 'i64') { + ty = 'uint64_t'; + } else if (prop.ty === 'ptr') { + ty = 'void*'; + } else { + throw new Error( + `Unknown state property type: "${prop.ty}"`); + } + res += ` ${ty} ${prop.name};\n`; + } + res += '};\n'; + + res += '\n'; + + res += `int ${options.prefix}_init(${options.prefix}_t* s);\n`; + res += `int ${options.prefix}_execute(${options.prefix}_t* s, ` + + 'const char* p, const char* endp);\n'; + + res += '\n'; + res += '#ifdef __cplusplus\n'; + res += '} /* extern "C" *\/\n'; + res += '#endif\n'; + res += `#endif /* ${DEFINE} *\/\n`; + + return res; + } +} diff --git a/llparse/src/compiler/index.ts b/llparse/src/compiler/index.ts new file mode 100644 index 0000000..89c258a --- /dev/null +++ b/llparse/src/compiler/index.ts @@ -0,0 +1,88 @@ +import * as debugAPI from 'debug'; +import * as frontend from 'llparse-frontend'; + +import source = frontend.source; + +import * as cImpl from '../implementation/c'; +import { HeaderBuilder } from './header-builder'; + +const debug = debugAPI('llparse:compiler'); + +export interface ICompilerOptions { + /** + * Debug method name + * + * The method must have following signature: + * + * ```c + * void debug(llparse_t* state, const char* p, const char* endp, + * const char* msg); + * ``` + * + * Where `llparse_t` is a parser state type. + */ + readonly debug?: string; + + /** + * What guard define to use in `#ifndef` in C headers. + * + * Default value: `prefix` argument + */ + readonly headerGuard?: string; + + /** Optional frontend configuration */ + readonly frontend?: frontend.IFrontendLazyOptions; + + /** Optional C-backend configuration */ + readonly c?: cImpl.ICPublicOptions; +} + +export interface ICompilerResult { + /** + * Textual C code + */ + readonly c: string; + + /** + * Textual C header file + */ + readonly header: string; +} + +export class Compiler { + constructor(public readonly prefix: string, + public readonly options: ICompilerOptions) { + } + + public compile(root: source.node.Node, + properties: ReadonlyArray<source.Property>): ICompilerResult { + debug('Combining implementations'); + const container = new frontend.Container(); + + const c = new cImpl.CCompiler(container, Object.assign({ + debug: this.options.debug, + }, this.options.c)); + + debug('Running frontend pass'); + const f = new frontend.Frontend(this.prefix, + container.build(), + this.options.frontend); + const info = f.compile(root, properties); + + debug('Building header'); + const hb = new HeaderBuilder(); + + const header = hb.build({ + headerGuard: this.options.headerGuard, + prefix: this.prefix, + properties, + spans: info.spans, + }); + + debug('Building C'); + return { + header, + c: c.compile(info), + }; + } +} |