UNPKG

agentlang

Version:

The easiest way to build the most reliable AI agents - enterprise-grade teams of AI agents that collaborate with each other and humans

433 lines 14.6 kB
import { createAgentlangServices } from '../language/agentlang-module.js'; import { EmptyFileSystem, URI } from 'langium'; import { isExpr, isGroup, isLiteral, isNegExpr, isNotExpr, isPrimExpr, isWorkflowDefinition, } from './generated/ast.js'; import { firstAliasSpec, firstCatchSpec, QuerySuffix } from '../runtime/util.js'; import { CrudPattern, DeletePattern, ExpressionPattern, ForEachPattern, FullTextSearchPattern, FunctionCallPattern, GroupExpressionPattern, IfPattern, LiteralPattern, NegExpressionPattern, NotExpressionPattern, ReturnPattern, } from './syntax.js'; let nextDocumentId = 1; export function parseHelper(services) { const metaData = services.LanguageMetaData; const documentBuilder = services.shared.workspace.DocumentBuilder; return async (input, options) => { var _a, _b; const uri = URI.parse((_a = options === null || options === void 0 ? void 0 : options.documentUri) !== null && _a !== void 0 ? _a : `file:///${nextDocumentId++}${(_b = metaData.fileExtensions[0]) !== null && _b !== void 0 ? _b : ''}`); const document = services.shared.workspace.LangiumDocumentFactory.fromString(input, uri, options === null || options === void 0 ? void 0 : options.parserOptions); services.shared.workspace.LangiumDocuments.addDocument(document); await documentBuilder.build([document], options); return document; }; } const services = createAgentlangServices(EmptyFileSystem); export const parse = parseHelper(services.Agentlang); export async function parseModule(moduleDef) { const document = await parse(moduleDef, { validation: true }); maybeRaiseParserErrors(document); return document.parseResult.value; } export async function parseStatement(stmt) { let result; const mod = await parseModule(`module Temp\nworkflow TempEvent { ${stmt} }`); if (isWorkflowDefinition(mod.defs[0])) { result = mod.defs[0].statements[0]; } else { throw new Error('Failed to extract workflow-statement'); } if (result) { return result; } else { throw new Error('There was an error parsing the statement'); } } export async function parseStatements(stmts) { const wf = await parseWorkflow(`workflow W {${stmts.join(';\n')}}`); return wf.statements; } export async function parseWorkflow(workflowDef) { const mod = await parseModule(`module Temp ${workflowDef}`); if (isWorkflowDefinition(mod.defs[0])) { return mod.defs[0]; } else { throw new Error(`Failed to generate workflow from ${workflowDef}`); } } const ErrorIndicator = '<-- ERROR'; export function maybeGetValidationErrors(document, lines) { var _a; if (lines === undefined) { lines = document.textDocument.getText().split('\n'); } const validationErrors = ((_a = document.diagnostics) !== null && _a !== void 0 ? _a : []).filter(e => e.severity === 1); const sls = new Set(); const scs = new Set(); if (validationErrors.length > 0) { for (const validationError of validationErrors) { if (!sls.has(validationError.range.start.line) && !scs.has(validationError.range.start.character)) { const t = document.textDocument.getText(validationError.range); const s = `(${validationError.range.start.line + 1}:${validationError.range.start.character + 1}) unexpected token(s) '${t}'`; const ln = lines[validationError.range.start.line]; if (ln.indexOf(ErrorIndicator) > 0) { lines[validationError.range.start.line] = `${ln}, ${s}`; } else { lines[validationError.range.start.line] = `${ln} ${ErrorIndicator} ${s}`; } sls.add(validationError.range.start.line); scs.add(validationError.range.start.character); } } return trimErrorLines(lines); } else { return undefined; } } function trimErrorLines(lines) { let startidx = 0; for (let i = 0; i < lines.length; ++i) { if (lines[i].indexOf(ErrorIndicator) > 0) { startidx = i; break; } } let endidx = startidx; for (let i = startidx + 1; i < lines.length; ++i) { if (lines[i].indexOf(ErrorIndicator) > 0) { endidx = i; break; } } if (startidx > 0) { --startidx; } if (endidx != lines.length) { ++endidx; } return lines.slice(startidx, endidx); } function trimErrorMessage(s) { const start = s.indexOf('Expecting:'); if (start >= 0) { const end = s.indexOf('but found:'); if (end > 0) { return `Expecting a valid token sequence, ${s.substring(end)}`; } } return s; } export function maybeRaiseParserErrors(document) { const code = document.textDocument.getText(); const lines = code.split('\n'); let hasErrors = false; const errLines = new Set(); if (document.parseResult.lexerErrors.length > 0) { document.parseResult.lexerErrors.forEach((err) => { if (!errLines.has(err.line)) { const errMsg = trimErrorMessage(err.message); const s = `${ErrorIndicator} (${err.line}:${err.column}) ${errMsg}`; lines[err.line - 1] = `${lines[err.line - 1]} ${s}`; errLines.add(err.line); } }); hasErrors = true; } if (document.parseResult.parserErrors.length > 0) { document.parseResult.parserErrors.forEach((err) => { const errMsg = trimErrorMessage(err.message); if (err.token.startLine && err.token.endLine) { if (!errLines.has(err.token.startLine)) { const s = `${ErrorIndicator} (${err.token.startLine}:${err.token.startColumn}) ${errMsg}`; lines[err.token.endLine - 1] = `${lines[err.token.endLine - 1]} ${s}`; lines.join('\n'); errLines.add(err.token.startLine); } } else { lines.push(`ERROR: ${errMsg}`); } }); hasErrors = true; } const errs = maybeGetValidationErrors(document, lines); if (hasErrors || errs !== undefined) { throw new Error(lines.join('\n')); } } export async function introspect(s) { let result = []; const v = await parse(`module Temp workflow Test {${s}}`); maybeRaiseParserErrors(v); if (isWorkflowDefinition(v.parseResult.value.defs[0])) { result = introspectHelper(v.parseResult.value.defs[0].statements); } else { throw new Error(`Failed to parse statements`); } return result; } function introspectHelper(stmts) { const result = []; stmts.forEach((stmt) => { result.push(introspectStatement(stmt)); }); return result; } function introspectStatement(stmt) { const r = introspectPattern(stmt.pattern); const aliasSpec = firstAliasSpec(stmt); if (aliasSpec) { if (aliasSpec.alias) { r.setAlias(aliasSpec.alias); } else if (aliasSpec.aliases.length > 0) { r.setAliases(aliasSpec.aliases); } } const catchSpec = firstCatchSpec(stmt); if (catchSpec) { catchSpec.handlers.forEach((h) => { r.addHandler(h.except, introspectStatement(h.stmt)); }); } return r; } function introspectPattern(pat) { let r; if (pat.crudMap) { if (isQueryPattern(pat)) { r = introspectQueryPattern(pat.crudMap); } else { r = introspectCreatePattern(pat.crudMap); } if (pat.crudMap.into) { r = introspectInto(pat.crudMap.into, r); } } else if (pat.expr) { r = introspectExpression(pat.expr); } else if (pat.forEach) { r = introspectForEach(pat.forEach); } else if (pat.if) { r = introspectIf(pat.if); } else if (pat.delete) { r = introspectDelete(pat.delete); } else if (pat.return) { r = introspectReturn(pat.return); } else if (pat.fullTextSearch) { r = introspectFullTextSearch(pat.fullTextSearch); } if (r) return r; else { throw new Error(`Failed to introspect pattern: ${pat}`); } } function introspectInto(intoSpec, p) { intoSpec.entries.forEach((se) => { p.addInto(se.alias, se.attribute); }); return p; } function isQueryPattern(pat) { if (pat.crudMap) { const crudMap = pat.crudMap; const r = crudMap.name.endsWith(QuerySuffix); if (!r && crudMap.body) { return (crudMap.body.attributes.length > 0 && crudMap.body.attributes.every((v) => { return v.name.endsWith(QuerySuffix); })); } else { return r; } } return false; } function introspectGroup(expr) { return new GroupExpressionPattern(introspectExpression(expr.ge)); } function introspectNegExpr(expr) { return new NegExpressionPattern(introspectExpression(expr.ne)); } function introspectNotExpr(expr) { return new NotExpressionPattern(introspectExpression(expr.ne)); } function introspectPrimExpr(expr) { if (isLiteral(expr)) { return introspectLiteral(expr); } else if (isGroup(expr)) { return introspectGroup(expr); } else if (isNegExpr(expr)) { return introspectNegExpr(expr); } else if (isNotExpr(expr)) { return introspectNotExpr(expr); } else { throw new Error(`Not a PrimExpr - ${expr}`); } } function introspectExpression(expr) { if (isPrimExpr(expr)) { return introspectPrimExpr(expr); } if (expr.$cstNode) { return new ExpressionPattern(expr.$cstNode.text); } throw new Error('Failed to introspect expression - ' + expr); } function introspectQueryPattern(crudMap) { var _a; if (crudMap) { const cp = new CrudPattern(crudMap.name); (_a = crudMap.body) === null || _a === void 0 ? void 0 : _a.attributes.forEach((sa) => { cp.addAttribute(sa.name, introspectExpression(sa.value), sa.op); }); crudMap.relationships.forEach((rp) => { cp.addRelationship(rp.name, introspectPattern(rp.pattern)); }); cp.isCreate = false; cp.isQueryUpdate = false; cp.isQuery = true; return cp; } throw new Error(`Failed to introspect query-pattern: ${crudMap}`); } function introspectCreatePattern(crudMap) { var _a; if (crudMap) { const cp = new CrudPattern(crudMap.name); cp.isCreate = false; cp.isQuery = false; let qup = false; (_a = crudMap.body) === null || _a === void 0 ? void 0 : _a.attributes.forEach((sa) => { if (!qup && sa.name.endsWith(QuerySuffix)) { qup = true; } cp.addAttribute(sa.name, introspectExpression(sa.value), sa.op); }); crudMap.relationships.forEach((rp) => { cp.addRelationship(rp.name, introspectPattern(rp.pattern)); }); cp.isQueryUpdate = qup; if (!qup) { cp.isCreate = true; cp.isQuery = false; } else { cp.isCreate = false; cp.isQuery = false; } return cp; } throw new Error(`Failed to introspect create-pattern: ${crudMap}`); } function asFnCallPattern(fnCall) { return new FunctionCallPattern(fnCall.name, fnCall.args.map((v) => { if (isExpr(v)) { return introspectExpression(v); } else { return introspectLiteral(v); } })); } function introspectLiteral(lit) { if (lit.id) { return LiteralPattern.Id(lit.id); } else if (lit.num) { return LiteralPattern.Number(lit.num); } else if (lit.ref) { return LiteralPattern.Reference(lit.ref); } else if (lit.str !== undefined) { return LiteralPattern.String(lit.str); } else if (lit.bool) { return LiteralPattern.Boolean(lit.bool == 'true' ? true : false); } else if (lit.fnCall) { return asFnCallPattern(lit.fnCall); } else if (lit.asyncFnCall) { return asFnCallPattern(lit.asyncFnCall.fnCall).asAsync(); } else if (lit.array) { return LiteralPattern.Array(lit.array.vals.map((stmt) => { return introspectStatement(stmt); })); } else if (lit.map) { return introspectMapLiteral(lit.map); } else { throw new Error(`Invalid literal - ${lit}`); } } function introspectMapLiteral(mapLit) { const m = new Map(); mapLit.entries.forEach((me) => { m.set(me.key, introspectExpression(me.value)); }); return LiteralPattern.Map(m); } function introspectForEach(forEach) { const fp = new ForEachPattern(forEach.var, introspectPattern(forEach.src)); forEach.statements.forEach((stmt) => { fp.addPattern(introspectStatement(stmt)); }); return fp; } export function introspectIf(ifpat) { const ifp = new IfPattern(introspectExpression(ifpat.cond)); ifpat.statements.forEach((stmt) => { ifp.addPattern(introspectStatement(stmt)); }); if (ifpat.else) { ifp.setElse(ifpat.else.statements.map((stmt) => { return introspectStatement(stmt); })); } return ifp; } function introspectDelete(deletePat) { return new DeletePattern(introspectPattern(deletePat.pattern)); } function introspectReturn(returnPat) { return new ReturnPattern(introspectPattern(returnPat.pattern)); } function introspectFullTextSearch(fullTextSearch) { let options = undefined; if (fullTextSearch.options) { options = introspectMapLiteral(fullTextSearch.options); } return new FullTextSearchPattern(fullTextSearch.name, introspectLiteral(fullTextSearch.query), options); } export async function introspectCase(caseStr) { const s = `if ${caseStr.trim().substring(4).trimStart()}`; const pat = await introspect(s); const ifPat = pat[0]; return { condition: ifPat.condition, body: ifPat.body[0] }; } export function canParse(s) { const ts = s.trim(); if (ts) { const contents = ts.substring(1, ts.length - 1).trim(); return contents.length > 0; } return false; } //# sourceMappingURL=parser.js.map