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 { Annotated, Bytes, Dictionary, Fold, fold, Record, stringify, Tuple, Value } from "@preserves/core";
|
||||||
import * as M from "./meta";
|
import * as M from "./meta";
|
||||||
import { CompilerOptions, ModuleContext } from "./compiler/context";
|
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 { typeForDefinition } from "./compiler/gentype";
|
||||||
// import { decoderFor } from "./compiler/decoder";
|
// import { decoderFor } from "./compiler/decoder";
|
||||||
import { converterForDefinition } from "./compiler/genconverter";
|
import { converterForDefinition } from "./compiler/genconverter";
|
||||||
import { EMPTY_TYPE, renderType } from "./compiler/type";
|
import { EMPTY_TYPE, renderType } from "./compiler/type";
|
||||||
|
import { genConstructor } from "./compiler/genctor";
|
||||||
|
|
||||||
export function compile(env: M.Environment, schema: M.Schema, options: CompilerOptions = {}): string {
|
export function compile(env: M.Environment, schema: M.Schema, options: CompilerOptions = {}): string {
|
||||||
const mod = new ModuleContext(env, schema, options);
|
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)) {
|
for (const [name, def] of M.Schema._._field0(schema).get(M.$definitions)) {
|
||||||
mod.defineType(
|
const t = typeForDefinition(mod, def);
|
||||||
seq(`export type ${stringify(name)} = `,
|
const nameStr = stringify(name);
|
||||||
renderType(typeForDefinition(mod, def)), `;`));
|
|
||||||
|
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)) {
|
for (const [name0, def] of M.Schema._._field0(schema).get(M.$definitions)) {
|
||||||
|
|
|
@ -34,10 +34,7 @@ export class ModuleContext {
|
||||||
literal(v: M.Input): Item {
|
literal(v: M.Input): Item {
|
||||||
let varname = this.literals.get(v);
|
let varname = this.literals.get(v);
|
||||||
if (varname === void 0) {
|
if (varname === void 0) {
|
||||||
const s = v.asPreservesText()
|
varname = M.jsId('$' + v.asPreservesText(), () => '__lit' + this.literals.size);
|
||||||
.replace('_', '__')
|
|
||||||
.replace('*', '_STAR_');
|
|
||||||
varname = M.isValidToken('_' + s, true) ? '$' + s : '__lit' + this.literals.size;
|
|
||||||
this.literals.set(v, varname);
|
this.literals.set(v, varname);
|
||||||
}
|
}
|
||||||
return 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 * as M from './gen/schema';
|
||||||
import { SchemaSyntaxError } from './error';
|
import { SchemaSyntaxError } from './error';
|
||||||
import type { AtomicType } from './compiler/type';
|
import type { AtomicType } from './compiler/type';
|
||||||
|
import { JS_KEYWORDS } from './compiler/jskw';
|
||||||
|
|
||||||
export * from './gen/schema';
|
export * from './gen/schema';
|
||||||
|
|
||||||
|
@ -9,12 +10,22 @@ export type Builtin = { type: AtomicType, pattern: M.Alternative };
|
||||||
|
|
||||||
export type Input = Value<never>;
|
export type Input = Value<never>;
|
||||||
|
|
||||||
export function isValidToken(s: string, allowLeadingUnderscore = false): boolean {
|
export function isValidToken(s: string): boolean {
|
||||||
if (allowLeadingUnderscore) {
|
return /^[a-zA-Z][a-zA-Z_0-9]*$/.test(s);
|
||||||
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 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('&');
|
export const ANDSYM = Symbol.for('&');
|
||||||
|
|
Loading…
Reference in New Issue