summaryrefslogtreecommitdiffstats
path: root/llparse/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'llparse/src/compiler')
-rw-r--r--llparse/src/compiler/header-builder.ts80
-rw-r--r--llparse/src/compiler/index.ts88
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),
+ };
+ }
+}