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
JavaScript
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