@lcap/nasl-parser
Version:
Take Nasl text to Nasl AST with the help of generalized parsing.
382 lines (328 loc) • 13.3 kB
text/typescript
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)}`)
// }