forked from syndicate-lang/preserves
genctor.ts
This commit is contained in:
parent
889d38bbb8
commit
0304c2631b
|
@ -1,11 +1,12 @@
|
|||
import { Annotated, Bytes, Dictionary, Fold, fold, Record, stringify, Tuple, Value } from "@preserves/core";
|
||||
import * as M from "./meta";
|
||||
import { CompilerOptions, ModuleContext } from "./compiler/context";
|
||||
import { brackets, Formatter, Item, parens, seq } from "./compiler/block";
|
||||
import { block, brackets, Formatter, Item, parens, seq } from "./compiler/block";
|
||||
import { typeForDefinition } from "./compiler/gentype";
|
||||
// import { decoderFor } from "./compiler/decoder";
|
||||
import { converterForDefinition } from "./compiler/genconverter";
|
||||
import { EMPTY_TYPE, renderType } from "./compiler/type";
|
||||
import { genConstructor } from "./compiler/genctor";
|
||||
|
||||
export function compile(env: M.Environment, schema: M.Schema, options: CompilerOptions = {}): string {
|
||||
const mod = new ModuleContext(env, schema, options);
|
||||
|
@ -30,9 +31,21 @@ export function compile(env: M.Environment, schema: M.Schema, options: CompilerO
|
|||
`;`));
|
||||
|
||||
for (const [name, def] of M.Schema._._field0(schema).get(M.$definitions)) {
|
||||
mod.defineType(
|
||||
seq(`export type ${stringify(name)} = `,
|
||||
renderType(typeForDefinition(mod, def)), `;`));
|
||||
const t = typeForDefinition(mod, def);
|
||||
const nameStr = stringify(name);
|
||||
|
||||
mod.defineType(seq(`export type ${nameStr} = `, renderType(t), `;`));
|
||||
|
||||
if (t.kind === 'union') {
|
||||
mod.defineFunction(_ctx =>
|
||||
seq(`export namespace ${nameStr} `, block(
|
||||
... Array.from(t.variants).map(([vn, vt]) =>
|
||||
genConstructor(vn, vn, vt, nameStr))
|
||||
)));
|
||||
} else {
|
||||
mod.defineFunction(_ctx =>
|
||||
genConstructor(nameStr, void 0, t, nameStr));
|
||||
}
|
||||
}
|
||||
|
||||
for (const [name0, def] of M.Schema._._field0(schema).get(M.$definitions)) {
|
||||
|
|
|
@ -34,10 +34,7 @@ export class ModuleContext {
|
|||
literal(v: M.Input): Item {
|
||||
let varname = this.literals.get(v);
|
||||
if (varname === void 0) {
|
||||
const s = v.asPreservesText()
|
||||
.replace('_', '__')
|
||||
.replace('*', '_STAR_');
|
||||
varname = M.isValidToken('_' + s, true) ? '$' + s : '__lit' + this.literals.size;
|
||||
varname = M.jsId('$' + v.asPreservesText(), () => '__lit' + this.literals.size);
|
||||
this.literals.set(v, varname);
|
||||
}
|
||||
return varname;
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import * as M from '../meta';
|
||||
import { block, braces, Item, keyvalue, parens, seq } from "./block";
|
||||
import { FieldType, renderType, SimpleType } from "./type";
|
||||
|
||||
export function genConstructor(
|
||||
name: string,
|
||||
variant: string | undefined,
|
||||
arg: SimpleType,
|
||||
resultType: Item): Item
|
||||
{
|
||||
const formals: Array<[string, FieldType]> = [];
|
||||
let simpleValue = false;
|
||||
|
||||
function examine(t: FieldType, name: string): void {
|
||||
if (t.kind !== 'unit') {
|
||||
formals.push([name, t]);
|
||||
}
|
||||
}
|
||||
|
||||
if (arg.kind === 'record') {
|
||||
arg.fields.forEach(examine);
|
||||
} else {
|
||||
examine(arg, 'value');
|
||||
simpleValue = variant === void 0;
|
||||
}
|
||||
|
||||
const initializers: Item[] = (variant !== void 0)
|
||||
? [keyvalue('_variant', JSON.stringify(variant))]
|
||||
: [];
|
||||
formals.forEach(([n, _t]) => initializers.push(n));
|
||||
|
||||
return seq(`export function ${M.jsId(name)}`,
|
||||
parens(... formals.map(([n, t]) => seq(n, ': ', renderType(t)))),
|
||||
': ', resultType, ' ', block(
|
||||
seq(`return `,
|
||||
((arg.kind === 'unit' && initializers.length === 0)
|
||||
? 'null'
|
||||
: (simpleValue
|
||||
? 'value'
|
||||
: braces(... initializers))))));
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
export const JS_KEYWORDS = new Set([
|
||||
'abstract',
|
||||
'await',
|
||||
'boolean',
|
||||
'break',
|
||||
'byte',
|
||||
'case',
|
||||
'catch',
|
||||
'char',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'default',
|
||||
'delete',
|
||||
'do',
|
||||
'double',
|
||||
'else',
|
||||
'enum',
|
||||
'export',
|
||||
'extends',
|
||||
'false',
|
||||
'final',
|
||||
'finally',
|
||||
'float',
|
||||
'for',
|
||||
'function',
|
||||
'goto',
|
||||
'if',
|
||||
'implements',
|
||||
'import',
|
||||
'in',
|
||||
'instanceof',
|
||||
'int',
|
||||
'interface',
|
||||
'let',
|
||||
'long',
|
||||
'native',
|
||||
'new',
|
||||
'null',
|
||||
'package',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'return',
|
||||
'short',
|
||||
'static',
|
||||
'super',
|
||||
'switch',
|
||||
'synchronized',
|
||||
'this',
|
||||
'throw',
|
||||
'throws',
|
||||
'transient',
|
||||
'true',
|
||||
'try',
|
||||
'typeof',
|
||||
'var',
|
||||
'void',
|
||||
'volatile',
|
||||
'while',
|
||||
'with',
|
||||
'yield',
|
||||
]);
|
|
@ -2,6 +2,7 @@ import { Value, is, Position } from '@preserves/core';
|
|||
import * as M from './gen/schema';
|
||||
import { SchemaSyntaxError } from './error';
|
||||
import type { AtomicType } from './compiler/type';
|
||||
import { JS_KEYWORDS } from './compiler/jskw';
|
||||
|
||||
export * from './gen/schema';
|
||||
|
||||
|
@ -9,12 +10,22 @@ export type Builtin = { type: AtomicType, pattern: M.Alternative };
|
|||
|
||||
export type Input = Value<never>;
|
||||
|
||||
export function isValidToken(s: string, allowLeadingUnderscore = false): boolean {
|
||||
if (allowLeadingUnderscore) {
|
||||
return /^[a-zA-Z_][a-zA-Z_0-9]*$/.test(s);
|
||||
} else {
|
||||
return /^[a-zA-Z][a-zA-Z_0-9]*$/.test(s);
|
||||
}
|
||||
export function isValidToken(s: string): boolean {
|
||||
return /^[a-zA-Z][a-zA-Z_0-9]*$/.test(s);
|
||||
}
|
||||
|
||||
export function isValidJsId(s: string): boolean {
|
||||
return /^[$_a-zA-Z][$_a-zA-Z0-9]*$/.test(s) && !JS_KEYWORDS.has(s);
|
||||
}
|
||||
|
||||
export function jsId(v: string, kf?: () => string): string {
|
||||
const s = v
|
||||
.replace('_', '__')
|
||||
.replace('*', '_STAR_');
|
||||
if (isValidJsId(s)) return s;
|
||||
if (isValidJsId('$' + s)) return '$' + s;
|
||||
if (kf !== void 0) return kf();
|
||||
throw new Error(`Internal error: idForLiteral needs to be completed (${v})`);
|
||||
}
|
||||
|
||||
export const ANDSYM = Symbol.for('&');
|
||||
|
|
Loading…
Reference in New Issue