import { Value, is, Position } from '@preserves/core'; import { ModulePath, Ref, Schema, Pattern, $thisModule, $definitions } from './gen/schema'; import { BASE } from './base'; import { SchemaSyntaxError } from './error'; export * from './gen/schema'; export type Input = Value; 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 const ANDSYM = Symbol.for('&'); export const DOT = Symbol.for('.'); export const DOTDOTDOT = Symbol.for('...'); export const EQUALS = Symbol.for('='); export const ORSYM = Symbol.for('/'); export type SchemaEnvEntry = { schemaModulePath: ModulePath } & ( ({ typescriptModulePath: string | null, // null means it's $thisModule in disguise schema: Schema, }) | ({ typescriptModulePath: string, schema: null, }) ); export type Environment = Array; function modsymFor(e: SchemaEnvEntry): string { return '_i_' + e.schemaModulePath.map(s => s.description!).join('$'); } export function lookup(namePos: Position | null, name: Ref, env: Environment, kLocal: (p: Pattern) => R, kBase: (p: Pattern) => R, kOther: (mod: string, modPath: string, p: Pattern | null) => R): R { for (const e of env) { if (is(e.schemaModulePath, Ref._.module(name)) || (e.typescriptModulePath === null && Ref._.module(name) === $thisModule)) { if (e.schema === null) { // It's an artificial module, not from a schema. Assume the identifier is present. return kOther(modsymFor(e), e.typescriptModulePath, null); } else { const p = Schema._.details(e.schema).get($definitions).get(Ref._.name(name)); if (p !== void 0) { if (e.typescriptModulePath === null) { return kLocal(p); } else { return kOther(modsymFor(e), e.typescriptModulePath, p); } } } } } if (Ref._.module(name) === $thisModule) { const p = Schema._.details(BASE).get($definitions).get(Ref._.name(name)); if (p !== void 0) return kBase(p); } throw new SchemaSyntaxError(`Undefined reference: ${formatRef(name)}`, namePos); } export function formatRef(r: Ref): string { return [... r[0] === $thisModule ? [] : r[0], r[1]].map(s => s.description!).join('.'); }