Skip to main content
Module

x/pgsql_ast_parser/ast-visitor.ts

Yet another simple Postgres SQL parser
Go to Latest
File
import * as a from './syntax/ast.ts';import { AstDefaultMapper, IAstPartialMapper, astMapper, IAstMapper } from './ast-mapper.ts';import { ReplaceReturnType } from './utils.ts';


export type IAstPartialVisitor = { [key in keyof IAstPartialMapper]: ReplaceReturnType<IAstPartialMapper[key], any> }export type IAstFullVisitor = { [key in keyof IAstPartialVisitor]-?: IAstPartialVisitor[key];}
export type IAstVisitor = IAstFullVisitor & { super(): IAstVisitor;}

class Visitor { mapper?: IAstMapper; visitor?: IAstPartialVisitor;
super() { return new SkipVisitor(this); }}
// =============== auto implement the mapperconst mapperProto = AstDefaultMapper.prototype as any;for (const k of Object.getOwnPropertyNames(mapperProto)) { const orig = mapperProto[k] as Function; if (k === 'constructor' || k === 'super' || typeof orig !== 'function') { continue; } Object.defineProperty(Visitor.prototype, k, { configurable: false, get() { return function (this: Visitor, ...args: any[]) { const impl = (this.visitor as any)[k] as Function; if (!impl) { // just ignore & forward call to mapper return orig.apply(this, args); } // return first argument // ...which means "I dont wana change anything" // in the ast-modifier language. impl.apply(this.visitor, args); return args[0]; } } })}

// ====== auto implement the skip mechanismclass SkipVisitor { constructor(readonly parent: Visitor) { }}
for (const k of Object.getOwnPropertyNames(mapperProto)) { const orig = mapperProto[k] as Function; if (k === 'constructor' || k === 'super' || typeof orig !== 'function') { continue; } Object.defineProperty(SkipVisitor.prototype, k, { configurable: false, get() { return function (this: SkipVisitor, ...args: []) { return orig.apply(this.parent, args); } } });}
/** * Builds an AST visitor based on the default implementation, merged with the one you provide. * * Example of visitor which counts references to a column 'foo': * * ```ts * let cnt = 0; * const visitor = astVisitor(v => ({ * ref: r => { * if (r.name === 'foo') { * cnt++; * } * v.super().ref(r); * } * })); * * visitor.statement(myStatementToCount); * console.log(`${cnt} references to foo !`); * ``` */export function astVisitor<T extends IAstPartialVisitor = IAstPartialVisitor>(visitorBuilder: (defaultImplem: IAstVisitor) => T): IAstVisitor { return astMapper(m => { const ret = new Visitor(); ret.mapper = m; ret.visitor = visitorBuilder(ret as any); return ret as any; })}