UNPKG

@lcap/nasl-parser

Version:

Take Nasl text to Nasl AST with the help of generalized parsing.

382 lines (328 loc) 13.3 kB
import { dispatchToNaslExpr } from './to-nasl-expression.js'; import * as nasl from '@lcap/nasl'; import { AutoPackNaslAndCst, NAMESPACE_SEP, PackNaslAndCst, PackNaslAndCstSeq, TYPE, Visitor, foldR, foldR1, getCst, getNasl, head, identity, isEmpty, projectIdentifier, tail, zipWith } from './common-util'; import { inspect } from 'util'; /** * 现在 IDE 的可视化数据查询 * - 压根就不支持别名 */ const isSqlSingleProp = (e) => e?.cst['__type'] === 'SqlSingleProp'; const isSqlMultiProp = (e) => e?.cst['__type'] === 'SqlMultiProp'; let entityNameEnv : Map<string, string> = null; const sqlPartsToMap = (arrObjs : Array<object>) => { const m = {}; arrObjs.forEach(obj => { for (const [key, value] of Object.entries(obj)) { if (m[key]) { m[key].push(value); } else { m[key] = [value]; } } }) return m; } export const toNaslCallQueryComponentImpl = ([from, _heteroParts, finalSelect, pagin]) => { // 据说 sql 的 join where group 可以不连续,骚得很。 // 这里先按只能相邻地写来处理吧。毕竟仅仅是 demo。 const heteroParts = PackNaslAndCstSeq(_heteroParts.flat()); const partsNasl = sqlPartsToMap(heteroParts.nasl); const partsCst = sqlPartsToMap(heteroParts.cst); entityNameEnv = new Map<string, string>(); entityNameEnv.set(from.obj, (from.colle)) // joins.forEach(j => { // // console.log(j) // entityNameEnv.set(j.obj, coerceExpressionToShortName(j.collection.expr)) // }) const fm = toNaslCallQueryFrom(from, partsNasl['join']); const ncqc = new nasl.CallQueryComponent({ select: finalSelect?.nasl, from: fm, where: toConjunction(partsNasl['where']) , // toNaslCallQueryWhere(head(wheres)), // 第一个啦 groupBy: partsNasl['group'], orderBy: partsNasl['order'], }); entityNameEnv = null; let finalCst = { from, parts: heteroParts.cst, finalSelect: finalSelect.cst, pagination: pagin?.cst }; if (pagin?.nasl) { let res = new nasl.Paginate({ page: pagin.nasl.page, size: pagin.nasl.size, list: ncqc, label: '查询组件分页', }) return PackNaslAndCst(res, TYPE('CallQueryComponent', finalCst)); } else { return PackNaslAndCst(ncqc, TYPE('CallQueryComponent', finalCst)); } } // function toNaslCallQueryWhere(w) : nasl.LogicItem { // // 先看后面的代码。放在前面,是因为需要先定义后使用。 // const lam1 = (env, node) => { // if (!isMemberExpression(node)) { // return node; // } // let oName; // if (isStringLiteral(node.object)) { // oName = env.get(node?.object?.value) ?? node?.object?.value; // } else if(isIdentifier(node.object)) { // oName = env.get(node?.object?.name) ?? node?.object?.name; // } // let pName; // if (isStringLiteral(node.property)) { // pName = env.get(node?.property?.value) ?? node?.property?.value; // } else if(isIdentifier(node.property)) { // pName = env.get(node?.property?.name) ?? node?.property?.name; // } // if (isStringLiteral(node.object) || isIdentifier(node.object)) { // if (isStringLiteral(node.property) || isIdentifier(node.property)) { // return new nasl.QueryFieldExpression({ // isDotStar: false, // entityAsName: oName, // propertyName: pName, // label: '查询组件实体属性', // }) // } // } // return node; // } // const refineQFE = (env : Map<string, string>, nl : nasl.LogicItem): nasl.LogicItem => { // new Visitor().preOrderVisitAll(env, nl, lam1); // return nl; // } // let nl = dispatchToNaslExpr(w.cond.expr); // // Query 的 where 里的 BinaryExpression 里的 MemberExpression 需要改成 QueryFieldExpression // // 非常,奇葩 // nl = new Visitor().preOrderVisitAll(entityNameEnv, nl, (env, node) => { // if (!node || !isNaslBinaryExpression) { // return node; // } // const nm = node as nasl.BinaryExpression; // if (!['startwith', 'endwith', 'like', 'in'].includes(nm.operator)) { // return nm; // } // return refineQFE(env, nm); // }) // return nl; // } // leaf // SqlSingleProp | SqlMultiProp export const handleSqlSingleProp = (rawIdent, sfx?) => { const nqse = new nasl.QueryFieldExpression({ isDotStar: false, entityAsName: rawIdent, propertyName: sfx?.nasl?.[0]?.name, // IDE 只支持一级 }) return PackNaslAndCst(nqse, TYPE('SqlSingleProp', { rawIdent, suffix: sfx?.cst })) } export const toNaslQueryFieldExpr = handleSqlSingleProp // return a List export const handleSqlMultiProp = (props) => { return PackNaslAndCst(props.nasl, TYPE('SqlMultiProp', { props: props.cst })) } export const mkNamedProp = (prop, alias?) => { // : Array<nasl.QueryFieldExpression> // alias 是废的,IDE 不支持 // IDE 只支持选取第一层的属性 if (isSqlSingleProp(prop)) { // let nqfe; // if (isMemberExpression(prop.nasl)) { // nqfe = [new nasl.QueryFieldExpression({ // isDotStar: true, // entityAsName: prop.nasl.object.name, // should be alias, // propertyName: prop.nasl.property.name, // })] // } else { // nqfe = [new nasl.QueryFieldExpression({ // isDotStar: false, // entityAsName: prop, // should be alias, // propertyName: null, // })] // } // return PackNaslAndCst(nqfe, TYPE('NamedProp', { prop, alias })) return PackNaslAndCst([prop.nasl], TYPE('NamedProp', { prop: [prop.cst], alias })) } if (isSqlMultiProp(prop)) { // IDE 目前不支持嵌套 select(好吧大部分数据库查询也不支持),所以用 flatMap return PackNaslAndCst(prop.nasl, TYPE('NamedProp', { prop: prop.cst, alias })) } throw new Error(`selectAliasedProp: ${inspect(prop)}`) } export const mkSelVProp = (name, fields?) => { let res; if (fields?.nasl && !isEmpty(fields.nasl)) { res = fields.nasl.map(field => new nasl.QueryFieldExpression({ isDotStar: false, entityAsName: name, propertyName: field.name, })); } else { res = [new nasl.QueryFieldExpression({ isDotStar: true, entityAsName: name, propertyName: "", })]; } return PackNaslAndCst(res, TYPE('SelVProp', { name, fields: fields?.cst })); } const toNaslCallQueryFrom = (from, jns) => new nasl.QueryFromExpression({ entityNamespace: "app.dataSources.defaultDS.entities", // DANGEROUS entityName: from.colle, // from.obj is not used joinParts: jns?.length > 0 ? [toRightDenseJoin(jns)] : null }) const toRightDenseJoin = foldR1((a : nasl.QueryJoinExpression, acc : nasl.QueryJoinExpression) => {a['joinParts'] = [acc]; return a;}, identity); // Array<Join> -> Array<nasl.QueryJoinExpression> export const toNaslQJExpr = (obj, colle, on) => { const jnNasl = new nasl.QueryJoinExpression({ entityNamespace: "app.dataSources.defaultDS.entities", // DANGEROUS jn.collection ? coerceExpressionToNamespace(jn.collection) : // entityName: entityNameEnv.get(obj), // asName: , joinType: 'INNER', // onExpressions: on?.nasl, joinParts: [] }) return PackNaslAndCst({join: jnNasl}, TYPE('Join', {join: {obj, colle: colle?.cst, on: on?.cst }})); } // -> Array<nasl.BinaryExpression> export const collapseSqlJoinCondProp = (l, r) => { if (isSqlSingleProp(l) && isSqlSingleProp(r)) { const nbe = new nasl.BinaryExpression({ left: l.nasl, right: r.nasl, operator: '==', }) const cst = new nasl.BinaryExpression({left: l.cst, right: r.cst, operator: '=='}) return PackNaslAndCst([nbe], TYPE('JoinCond-onExpressions', { 'onExpressions': cst })); } if (isSqlMultiProp(l) && isSqlMultiProp(r)) { if (l.nasl.length !== r.nasl.length) { throw new Error(`join 的条件的结构不一致`) } // let res: Array<nasl.BinaryExpression> = []; const nasls = zipWith((l1, r1) => new nasl.BinaryExpression({ // @ts-ignore left: l1 as nasl.LogicItem, // @ts-ignore right: r1 as nasl.LogicItem, operator: '==', }), l.nasl, r.nasl); const csts = zipWith((l1, r1) => TYPE('BinaryExpression', {left: l1, right: r1, op: '=='}), l.cst.props, r.cst.props) return PackNaslAndCst(nasls, TYPE('JoinCond-onExpressions', {'onExpressions': csts})); } throw new Error(`join 的条件的结构不一致`) } export const toNaslCallQueryOrderBy = ([by, __ord]) => { const ord = __ord[0]; const makeOrd = () => { switch (ord['__type']) { case 'AscOrd': { return new nasl.StringLiteral({ value: 'ASC' }) } case 'DescOrd': { return new nasl.StringLiteral({ value: 'DESC' }) } case 'DynOrd': { const ni = new nasl.Identifier({ name: ord?.ref?.name, }) ni.qName = ord?.ref; return ni; } default: throw new Error(`toNaslCallQueryOrder: ${inspect(ord)}`) } } const nqobe = new nasl.QueryOrderByExpression({ orderElement: by.nasl, order: makeOrd(), label: '查询组件OrderBy元素', }) return PackNaslAndCst(nqobe, TYPE('OrderBy', { by: by.cst, ord })) } export const toNaslSqlGroupBy = (by) => { if (isSqlSingleProp(by)) { const nqgbe = [new nasl.QueryGroupByExpression({ groupElement: by.nasl, label: '查询组件GroupBy元素', })] return PackNaslAndCst(nqgbe, TYPE('GroupBy', { by: by.cst })) } if (isSqlMultiProp(by)) { const nasls = by.nasl.map(e => new nasl.QueryGroupByExpression({ groupElement: e, label: '查询组件GroupBy元素', })) return PackNaslAndCst({group: nasls}, TYPE('GroupBy', {group: by?.cst?.props})); } } const toConjunction = (list : Array<nasl.LogicItem>) => { if (list?.length > 0) { const lam = (l: nasl.LogicItem, r: nasl.LogicItem) => // @ts-ignore new nasl.BinaryExpression({left: l, right: r, operator: '&&'}); return foldR1(lam, identity)(list) } else { return null; } } // IDE 仅支持 e1 && e2 && ... 这种形式 // function collapseConjunction(e : Expression): Array<nasl.BinaryExpression> { // if (!isBinaryExpression(e)) { // throw new Error(`The current VisualQuery in the IDE only supports predicate as binary expressions\n // collapseConjunction : ${inspect(e)}`) // } // let normL : Array<nasl.BinaryExpression> = []; // let normR : Array<nasl.BinaryExpression> = []; // if (e.operator !== '&&') { // return [dispatchToNaslExpr(e) as nasl.BinaryExpression] // } // if (isBinaryExpression(e.left)) { // normL = collapseConjunction(e.left) // } else { // normL = [dispatchToNaslExpr(e.left) as nasl.BinaryExpression] // } // if (isBinaryExpression(e.right)) { // normR = collapseConjunction(e.right) // } else { // normR = [dispatchToNaslExpr(e.right) as nasl.BinaryExpression] // } // return normL.concat(normR) // } // {x.a, x.b.c} // export const toNaslSqlMultiProp = ([props]) => { // let se = props.nasl.flatMap(toNaslSelectedProp); // const nqse = new nasl.QuerySelectExpression({ // distinct: false, // ??? // star: se.some(e => e.isDotStar), // ??? // selectElements: se.nasl, // }) // return PackNaslAndCst(nqse, TYPE('SqlMultiProp', { props: props.cst })) // } // function toNaslCallQuerySelect(env: Map<string, string>, s) // : nasl.QuerySelectExpression { // if (isSqlSingleProp(s)) { // return // } // if (isSqlMultiProp(s)) { // } // throw new Error(`toNaslCallQuerySelect: ${inspect(s)}`) // } // select a.b, select { a: b.c } 啥的 // const toNaslSelectedProp = // (expr, sfx) => // s : SqlSingleProp | NamedProp res : Array<nasl.QueryFieldExpression> // { // if(isSqlSingleProp(s)) { // return [propToNaslQueryFieldExpr(expr, sfx)] // } // if (isNamedProp(s)) { // return mkNamedProp(expr, sfx) // } // throw new Error(`dispatchToNaslQueryFieldExpr: ${inspect(expr, sfx)}`) // }