From 3816f8015068090ec7cd2309b8a7b5f516168825 Mon Sep 17 00:00:00 2001 From: dosco <832235+dosco@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:02:32 -0700 Subject: [PATCH] feat: new classification type in dspy signature --- package-lock.json | 70 -- package.json | 1 - src/ax/dsp/datetime.ts | 6 + src/ax/dsp/extract.ts | 14 +- src/ax/dsp/generate.test.ts | 1 + src/ax/dsp/index.ts | 7 +- src/ax/dsp/parser.peggy | 46 - src/ax/dsp/parser.ts | 1867 ++++------------------------------- src/ax/dsp/prompt.ts | 15 +- src/ax/dsp/sig.test.ts | 29 +- src/ax/dsp/sig.ts | 33 +- src/ax/dsp/util.ts | 4 + src/ax/package.json | 2 - src/examples/extract.ts | 4 +- 14 files changed, 257 insertions(+), 1842 deletions(-) delete mode 100644 src/ax/dsp/parser.peggy diff --git a/package-lock.json b/package-lock.json index 8144844e..8aacffca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,6 @@ "prettier": "^3.2.5", "release-it": "^17.4.0", "standard-version": "^9.5.0", - "ts-pegjs": "^4.2.1", "tsx": "^4.7.1", "typedoc": "^0.26.3", "typedoc-plugin-frontmatter": "^1.0.0", @@ -19672,33 +19671,6 @@ "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/peggy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/peggy/-/peggy-3.0.2.tgz", - "integrity": "sha512-n7chtCbEoGYRwZZ0i/O3t1cPr6o+d9Xx4Zwy2LYfzv0vjchMBU0tO+qYYyvZloBPcgRgzYvALzGWHe609JjEpg==", - "dev": true, - "peer": true, - "dependencies": { - "commander": "^10.0.0", - "source-map-generator": "0.8.0" - }, - "bin": { - "peggy": "bin/peggy.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/peggy/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "peer": true, - "engines": { - "node": ">=14" - } - }, "node_modules/periscopic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", @@ -22964,16 +22936,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-generator": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/source-map-generator/-/source-map-generator-0.8.0.tgz", - "integrity": "sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 10" - } - }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", @@ -24424,37 +24386,6 @@ "code-block-writer": "^12.0.0" } }, - "node_modules/ts-pegjs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ts-pegjs/-/ts-pegjs-4.2.1.tgz", - "integrity": "sha512-mK/O2pu6lzWUeKpEMA/wsa0GdYblfjJI1y0s0GqH6xCTvugQDOWPJbm5rY6AHivpZICuXIriCb+a7Cflbdtc2w==", - "dev": true, - "dependencies": { - "prettier": "^2.8.8", - "ts-morph": "^18.0.0" - }, - "bin": { - "tspegjs": "dist/cli.mjs" - }, - "peerDependencies": { - "peggy": "^3.0.2" - } - }, - "node_modules/ts-pegjs/node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "node_modules/tsconfck": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.1.tgz", @@ -26422,7 +26353,6 @@ "@ava/typescript": "^5.0.0", "ava": "^6.1.3", "npm-run-all": "^4.1.5", - "ts-pegjs": "^4.2.1", "tsimp": "^2.0.11", "tsx": "^4.7.1" } diff --git a/package.json b/package.json index 45d32bab..0e524b72 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,6 @@ "prettier": "^3.2.5", "release-it": "^17.4.0", "standard-version": "^9.5.0", - "ts-pegjs": "^4.2.1", "tsx": "^4.7.1", "typedoc": "^0.26.3", "typedoc-plugin-frontmatter": "^1.0.0", diff --git a/src/ax/dsp/datetime.ts b/src/ax/dsp/datetime.ts index 50fa1640..85b4fa1c 100644 --- a/src/ax/dsp/datetime.ts +++ b/src/ax/dsp/datetime.ts @@ -88,3 +88,9 @@ function _parseLLMFriendlyDateTime(dateTimeStr: string) { // Convert to UTC return date.utc().toDate(); } + +// eslint-disable-next-line @typescript-eslint/naming-convention +export const formatDateWithTimezone = (date: Readonly) => { + const momentDate = moment(date).utc(); + return momentDate.format(`YYYY-MM-DD HH:mm:ss UTC`); +}; diff --git a/src/ax/dsp/extract.ts b/src/ax/dsp/extract.ts index 3aa30be5..7cbd13a8 100644 --- a/src/ax/dsp/extract.ts +++ b/src/ax/dsp/extract.ts @@ -101,6 +101,13 @@ const convertValueToType = (field: Readonly, val: string) => { return parseLLMFriendlyDate(field, val as string); case 'datetime': return parseLLMFriendlyDateTime(field, val as string); + case 'class': + if (field.type.classes && !field.type.classes.includes(val)) { + throw new Error( + `Invalid class '${val}', expected one of the following: ${field.type.classes.join(', ')}` + ); + } + return val as string; default: return val as string; // Unknown type } @@ -122,11 +129,8 @@ function validateAndParseFieldValue( field: Readonly, fieldValue: string | undefined ): unknown { - if ( - !fieldValue || - fieldValue === '' || - fieldValue.toLocaleLowerCase() === 'null' - ) { + const fv = fieldValue?.toLocaleLowerCase(); + if (!fieldValue || !fv || fv === '' || fv === 'null' || fv === 'undefined') { if (field.isOptional) { return; } diff --git a/src/ax/dsp/generate.test.ts b/src/ax/dsp/generate.test.ts index 602db859..ff11af28 100644 --- a/src/ax/dsp/generate.test.ts +++ b/src/ax/dsp/generate.test.ts @@ -23,6 +23,7 @@ test('extractValues with no prefix and single output', (t) => { test('extractValues with json', (t) => { const sig = new AxSignature(`question -> answer : json`); + const v1 = {}; extractValues(sig, v1, 'Answer: ```json\n{"hello": "world"}\n```'); diff --git a/src/ax/dsp/index.ts b/src/ax/dsp/index.ts index 25918123..4a0bf2ca 100644 --- a/src/ax/dsp/index.ts +++ b/src/ax/dsp/index.ts @@ -14,4 +14,9 @@ export type { AxAssertion, AxStreamingAssertion } from './asserts.js'; export type { AxPromptTemplate, AxFieldTemplateFn } from './prompt.js'; export type { AxInstanceRegistry } from './registry.js'; export type { AxResponseHandlerArgs } from './generate.js'; -export type { ParsedField, ParsedIdentifier } from './parser.js'; +export type { + ParsedSignature, + InputParsedFieldList, + OutputParsedFieldList, + ParsedIdentifier +} from './parser.js'; diff --git a/src/ax/dsp/parser.peggy b/src/ax/dsp/parser.peggy deleted file mode 100644 index 70c139c2..00000000 --- a/src/ax/dsp/parser.peggy +++ /dev/null @@ -1,46 +0,0 @@ -ParsedSignature -= optionalDesc:(_ ParsedString _)? inputs:ParsedFieldList __ "->" __ outputs:ParsedFieldList _ { - return { - desc: optionalDesc ? optionalDesc.join("").trim() : undefined, - inputs, - outputs - }; -} - -ParsedFieldList = ParsedField |.., _ "," _| - -ParsedField -= _ name:ParsedIdentifier isOptional:("?")? optionalType:(_ ":" _ ParsedType)? optionalDesc:(_ ParsedString)? { - return { - name, - desc: optionalDesc ? optionalDesc.join("").trim() : undefined, - type: optionalType ? optionalType.at(-1) : undefined, - isOptional: isOptional ? true : undefined - }; -} - -ParsedType = type:Type isArray:("[]")? { - return { name: type, isArray: isArray ? true : false } -} - -Type = "string" / "number" / "boolean" / "json" / "image" / "datetime" / "date" - - -ParsedIdentifier = chars:[a-zA-Z_0-9]* { - return chars.join("") -} - -ParsedString = - "'" content:[^']* "'" { return content.join("") } - / '"' content:[^"]* '"' { return content .join("") } - - -// optional whitespace -_ = [ \t\r\n]* { - return "" -} - -// mandatory whitespace -__ = [ \t\r\n]+ { - return "" -} \ No newline at end of file diff --git a/src/ax/dsp/parser.ts b/src/ax/dsp/parser.ts index 84f452f5..1ab821d7 100644 --- a/src/ax/dsp/parser.ts +++ b/src/ax/dsp/parser.ts @@ -1,1741 +1,224 @@ -/* eslint-disable */ - - - -const peggyParser: {parse: any, SyntaxError: any, DefaultTracer?: any} = // Generated by Peggy 3.0.2. -// -// https://peggyjs.org/ -// @ts-ignore -(function() { -// @ts-ignore - "use strict"; - -// @ts-ignore -function peg$subclass(child, parent) { -// @ts-ignore - function C() { this.constructor = child; } -// @ts-ignore - C.prototype = parent.prototype; -// @ts-ignore - child.prototype = new C(); -} - -// @ts-ignore -function peg$SyntaxError(message, expected, found, location) { -// @ts-ignore - var self = Error.call(this, message); - // istanbul ignore next Check is a necessary evil to support older environments -// @ts-ignore - if (Object.setPrototypeOf) { -// @ts-ignore - Object.setPrototypeOf(self, peg$SyntaxError.prototype); - } -// @ts-ignore - self.expected = expected; -// @ts-ignore - self.found = found; -// @ts-ignore - self.location = location; -// @ts-ignore - self.name = "SyntaxError"; -// @ts-ignore - return self; -} - -// @ts-ignore -peg$subclass(peg$SyntaxError, Error); - -// @ts-ignore -function peg$padEnd(str, targetLength, padString) { -// @ts-ignore - padString = padString || " "; -// @ts-ignore - if (str.length > targetLength) { return str; } -// @ts-ignore - targetLength -= str.length; -// @ts-ignore - padString += padString.repeat(targetLength); -// @ts-ignore - return str + padString.slice(0, targetLength); -} - -// @ts-ignore -peg$SyntaxError.prototype.format = function(sources) { -// @ts-ignore - var str = "Error: " + this.message; -// @ts-ignore - if (this.location) { -// @ts-ignore - var src = null; -// @ts-ignore - var k; -// @ts-ignore - for (k = 0; k < sources.length; k++) { -// @ts-ignore - if (sources[k].source === this.location.source) { -// @ts-ignore - src = sources[k].text.split(/\r\n|\n|\r/g); -// @ts-ignore - break; - } - } -// @ts-ignore - var s = this.location.start; -// @ts-ignore - var offset_s = (this.location.source && (typeof this.location.source.offset === "function")) -// @ts-ignore - ? this.location.source.offset(s) -// @ts-ignore - : s; -// @ts-ignore - var loc = this.location.source + ":" + offset_s.line + ":" + offset_s.column; -// @ts-ignore - if (src) { -// @ts-ignore - var e = this.location.end; -// @ts-ignore - var filler = peg$padEnd("", offset_s.line.toString().length, ' '); -// @ts-ignore - var line = src[s.line - 1]; -// @ts-ignore - var last = s.line === e.line ? e.column : line.length + 1; -// @ts-ignore - var hatLen = (last - s.column) || 1; -// @ts-ignore - str += "\n --> " + loc + "\n" -// @ts-ignore - + filler + " |\n" -// @ts-ignore - + offset_s.line + " | " + line + "\n" -// @ts-ignore - + filler + " | " + peg$padEnd("", s.column - 1, ' ') -// @ts-ignore - + peg$padEnd("", hatLen, "^"); -// @ts-ignore - } else { -// @ts-ignore - str += "\n at " + loc; - } - } -// @ts-ignore - return str; +export type ParsedSignature = { + desc: any; + inputs: InputParsedFieldList; + outputs: OutputParsedFieldList; }; - -// @ts-ignore -peg$SyntaxError.buildMessage = function(expected, found) { -// @ts-ignore - var DESCRIBE_EXPECTATION_FNS = { -// @ts-ignore - literal: function(expectation) { -// @ts-ignore - return "\"" + literalEscape(expectation.text) + "\""; - }, - -// @ts-ignore - class: function(expectation) { -// @ts-ignore - var escapedParts = expectation.parts.map(function(part) { -// @ts-ignore - return Array.isArray(part) -// @ts-ignore - ? classEscape(part[0]) + "-" + classEscape(part[1]) -// @ts-ignore - : classEscape(part); - }); - -// @ts-ignore - return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]"; - }, - -// @ts-ignore - any: function() { -// @ts-ignore - return "any character"; - }, - -// @ts-ignore - end: function() { -// @ts-ignore - return "end of input"; - }, - -// @ts-ignore - other: function(expectation) { -// @ts-ignore - return expectation.description; - } - }; - -// @ts-ignore - function hex(ch) { -// @ts-ignore - return ch.charCodeAt(0).toString(16).toUpperCase(); - } - -// @ts-ignore - function literalEscape(s) { -// @ts-ignore - return s -// @ts-ignore - .replace(/\\/g, "\\\\") -// @ts-ignore - .replace(/"/g, "\\\"") -// @ts-ignore - .replace(/\0/g, "\\0") -// @ts-ignore - .replace(/\t/g, "\\t") -// @ts-ignore - .replace(/\n/g, "\\n") -// @ts-ignore - .replace(/\r/g, "\\r") -// @ts-ignore - .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); }) -// @ts-ignore - .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); }); - } - -// @ts-ignore - function classEscape(s) { -// @ts-ignore - return s -// @ts-ignore - .replace(/\\/g, "\\\\") -// @ts-ignore - .replace(/\]/g, "\\]") -// @ts-ignore - .replace(/\^/g, "\\^") -// @ts-ignore - .replace(/-/g, "\\-") -// @ts-ignore - .replace(/\0/g, "\\0") -// @ts-ignore - .replace(/\t/g, "\\t") -// @ts-ignore - .replace(/\n/g, "\\n") -// @ts-ignore - .replace(/\r/g, "\\r") -// @ts-ignore - .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); }) -// @ts-ignore - .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); }); - } - -// @ts-ignore - function describeExpectation(expectation) { -// @ts-ignore - return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation); - } - -// @ts-ignore - function describeExpected(expected) { -// @ts-ignore - var descriptions = expected.map(describeExpectation); -// @ts-ignore - var i, j; - -// @ts-ignore - descriptions.sort(); - -// @ts-ignore - if (descriptions.length > 0) { -// @ts-ignore - for (i = 1, j = 1; i < descriptions.length; i++) { -// @ts-ignore - if (descriptions[i - 1] !== descriptions[i]) { -// @ts-ignore - descriptions[j] = descriptions[i]; -// @ts-ignore - j++; - } - } -// @ts-ignore - descriptions.length = j; - } - -// @ts-ignore - switch (descriptions.length) { -// @ts-ignore - case 1: -// @ts-ignore - return descriptions[0]; - -// @ts-ignore - case 2: -// @ts-ignore - return descriptions[0] + " or " + descriptions[1]; - -// @ts-ignore - default: -// @ts-ignore - return descriptions.slice(0, -1).join(", ") -// @ts-ignore - + ", or " -// @ts-ignore - + descriptions[descriptions.length - 1]; - } - } - -// @ts-ignore - function describeFound(found) { -// @ts-ignore - return found ? "\"" + literalEscape(found) + "\"" : "end of input"; - } - -// @ts-ignore - return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found."; +export type InputParsedFieldList = InputParsedField[]; +export type OutputParsedFieldList = OutputParsedField[]; +export type InputParsedField = { + name: ParsedIdentifier; + desc: any; + type: + | NonNullable<{ name: TypeNotClass; isArray: boolean } | null> + | undefined; + isOptional: boolean; }; - -// @ts-ignore -function peg$parse(input, options) { -// @ts-ignore - options = options !== undefined ? options : {}; - -// @ts-ignore - var peg$FAILED = {}; -// @ts-ignore - var peg$source = options.grammarSource; - -// @ts-ignore - var peg$startRuleFunctions = { ParsedSignature: peg$parseParsedSignature }; -// @ts-ignore - var peg$startRuleFunction = peg$parseParsedSignature; - -// @ts-ignore - var peg$c0 = "->"; - var peg$c1 = ","; - var peg$c2 = "?"; - var peg$c3 = ":"; - var peg$c4 = "[]"; - var peg$c5 = "string"; - var peg$c6 = "number"; - var peg$c7 = "boolean"; - var peg$c8 = "json"; - var peg$c9 = "image"; - var peg$c10 = "datetime"; - var peg$c11 = "date"; - var peg$c12 = "'"; - var peg$c13 = "\""; - - var peg$r0 = /^[a-zA-Z_0-9]/; - var peg$r1 = /^[^']/; - var peg$r2 = /^[^"]/; - var peg$r3 = /^[ \t\r\n]/; - - var peg$e0 = peg$literalExpectation("->", false); - var peg$e1 = peg$literalExpectation(",", false); - var peg$e2 = peg$literalExpectation("?", false); - var peg$e3 = peg$literalExpectation(":", false); - var peg$e4 = peg$literalExpectation("[]", false); - var peg$e5 = peg$literalExpectation("string", false); - var peg$e6 = peg$literalExpectation("number", false); - var peg$e7 = peg$literalExpectation("boolean", false); - var peg$e8 = peg$literalExpectation("json", false); - var peg$e9 = peg$literalExpectation("image", false); - var peg$e10 = peg$literalExpectation("datetime", false); - var peg$e11 = peg$literalExpectation("date", false); - var peg$e12 = peg$classExpectation([["a", "z"], ["A", "Z"], "_", ["0", "9"]], false, false); - var peg$e13 = peg$literalExpectation("'", false); - var peg$e14 = peg$classExpectation(["'"], true, false); - var peg$e15 = peg$literalExpectation("\"", false); - var peg$e16 = peg$classExpectation(["\""], true, false); - var peg$e17 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false); -// @ts-ignore - - var peg$f0 = function(optionalDesc, inputs, outputs) { -// @ts-ignore - return { -// @ts-ignore - desc: optionalDesc ? optionalDesc.join("").trim() : undefined, -// @ts-ignore - inputs, -// @ts-ignore - outputs - }; -};// @ts-ignore - - var peg$f1 = function(name, isOptional, optionalType, optionalDesc) { -// @ts-ignore - return { -// @ts-ignore - name, -// @ts-ignore - desc: optionalDesc ? optionalDesc.join("").trim() : undefined, -// @ts-ignore - type: optionalType ? optionalType.at(-1) : undefined, -// @ts-ignore - isOptional: isOptional ? true : undefined - }; -};// @ts-ignore - - var peg$f2 = function(type, isArray) { -// @ts-ignore - return { name: type, isArray: isArray ? true : false } -};// @ts-ignore - - var peg$f3 = function(chars) { -// @ts-ignore - return chars.join("") -};// @ts-ignore - - var peg$f4 = function(content) {// @ts-ignore - return content.join("") };// @ts-ignore - - var peg$f5 = function(content) {// @ts-ignore - return content .join("") };// @ts-ignore - - var peg$f6 = function() { -// @ts-ignore - return "" -};// @ts-ignore - - var peg$f7 = function() { -// @ts-ignore - return "" +export type OutputParsedField = + | ClassField + | NonClassField +export type ClassField = { + name: ParsedIdentifier; + type: { name: "class"; isArray: boolean, classes: string[] }; + isOptional: boolean; }; -// @ts-ignore - var peg$currPos = 0; -// @ts-ignore - var peg$savedPos = 0; -// @ts-ignore - var peg$posDetailsCache = [{ line: 1, column: 1 }]; -// @ts-ignore - var peg$maxFailPos = 0; -// @ts-ignore - var peg$maxFailExpected = []; -// @ts-ignore - var peg$silentFails = 0; - -// @ts-ignore - var peg$resultsCache = {}; - -// @ts-ignore - var peg$result; - -// @ts-ignore - if ("startRule" in options) { -// @ts-ignore - if (!(options.startRule in peg$startRuleFunctions)) { -// @ts-ignore - throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); - } +export type NonClassField = { + name: ParsedIdentifier; + desc: any; + type: NonNullable<{ name: any; isArray: boolean } | null> | undefined; + isOptional: boolean; +}; +export type TypeNotClass = + | "string" + | "number" + | "boolean" + | "json" + | "image" + | "datetime" + | "date"; +export type Type = TypeNotClass | "class"; +export type ParsedIdentifier = string; +export type ParsedString = string; -// @ts-ignore - peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; - } -// @ts-ignore - function text() { -// @ts-ignore - return input.substring(peg$savedPos, peg$currPos); - } +class SignatureParser { + private input: string; + private position: number; -// @ts-ignore - function offset() { -// @ts-ignore - return peg$savedPos; + constructor(input: string) { + this.input = input; + this.position = 0; } -// @ts-ignore - function range() { -// @ts-ignore + parse(): ParsedSignature { + this.skipWhitespace(); + const optionalDesc = this.parseParsedString(); + this.skipWhitespace(); + const inputs = this.parseInputParsedFieldList(); + this.skipWhitespace(); + this.expect('->'); + this.skipWhitespace(); + const outputs = this.parseOutputParsedFieldList(); + return { -// @ts-ignore - source: peg$source, -// @ts-ignore - start: peg$savedPos, -// @ts-ignore - end: peg$currPos + desc: optionalDesc?.trim(), + inputs, + outputs, }; } -// @ts-ignore - function location() { -// @ts-ignore - return peg$computeLocation(peg$savedPos, peg$currPos); - } - -// @ts-ignore - function expected(description, location) { -// @ts-ignore - location = location !== undefined -// @ts-ignore - ? location -// @ts-ignore - : peg$computeLocation(peg$savedPos, peg$currPos); - -// @ts-ignore - throw peg$buildStructuredError( -// @ts-ignore - [peg$otherExpectation(description)], -// @ts-ignore - input.substring(peg$savedPos, peg$currPos), -// @ts-ignore - location - ); - } - -// @ts-ignore - function error(message, location) { -// @ts-ignore - location = location !== undefined -// @ts-ignore - ? location -// @ts-ignore - : peg$computeLocation(peg$savedPos, peg$currPos); - -// @ts-ignore - throw peg$buildSimpleError(message, location); - } - -// @ts-ignore - function peg$literalExpectation(text, ignoreCase) { -// @ts-ignore - return { type: "literal", text: text, ignoreCase: ignoreCase }; - } - -// @ts-ignore - function peg$classExpectation(parts, inverted, ignoreCase) { -// @ts-ignore - return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase }; - } - -// @ts-ignore - function peg$anyExpectation() { -// @ts-ignore - return { type: "any" }; - } - -// @ts-ignore - function peg$endExpectation() { -// @ts-ignore - return { type: "end" }; - } - -// @ts-ignore - function peg$otherExpectation(description) { -// @ts-ignore - return { type: "other", description: description }; - } - -// @ts-ignore - function peg$computePosDetails(pos) { -// @ts-ignore - var details = peg$posDetailsCache[pos]; -// @ts-ignore - var p; - -// @ts-ignore - if (details) { -// @ts-ignore - return details; -// @ts-ignore - } else { -// @ts-ignore - p = pos - 1; -// @ts-ignore - while (!peg$posDetailsCache[p]) { -// @ts-ignore - p--; - } - -// @ts-ignore - details = peg$posDetailsCache[p]; -// @ts-ignore - details = { -// @ts-ignore - line: details.line, -// @ts-ignore - column: details.column - }; - -// @ts-ignore - while (p < pos) { -// @ts-ignore - if (input.charCodeAt(p) === 10) { -// @ts-ignore - details.line++; -// @ts-ignore - details.column = 1; -// @ts-ignore - } else { -// @ts-ignore - details.column++; - } - -// @ts-ignore - p++; - } - -// @ts-ignore - peg$posDetailsCache[pos] = details; - -// @ts-ignore - return details; + private parseInputParsedFieldList(): InputParsedField[] { + const fields: InputParsedField[] = []; + fields.push(this.parseInputParsedField()); + + while (this.match(',')) { + this.skipWhitespace(); + fields.push(this.parseInputParsedField()); } + + return fields; } -// @ts-ignore - function peg$computeLocation(startPos, endPos, offset) { -// @ts-ignore - var startPosDetails = peg$computePosDetails(startPos); -// @ts-ignore - var endPosDetails = peg$computePosDetails(endPos); - -// @ts-ignore - var res = { -// @ts-ignore - source: peg$source, -// @ts-ignore - start: { -// @ts-ignore - offset: startPos, -// @ts-ignore - line: startPosDetails.line, -// @ts-ignore - column: startPosDetails.column - }, -// @ts-ignore - end: { -// @ts-ignore - offset: endPos, -// @ts-ignore - line: endPosDetails.line, -// @ts-ignore - column: endPosDetails.column - } - }; -// @ts-ignore - if (offset && peg$source && (typeof peg$source.offset === "function")) { -// @ts-ignore - res.start = peg$source.offset(res.start); -// @ts-ignore - res.end = peg$source.offset(res.end); + private parseOutputParsedFieldList(): OutputParsedField[] { + const fields: OutputParsedField[] = []; + fields.push(this.parseOutputParsedField()); + + while (this.match(',')) { + this.skipWhitespace(); + fields.push(this.parseOutputParsedField()); } -// @ts-ignore - return res; + + return fields; } -// @ts-ignore - function peg$fail(expected) { -// @ts-ignore - if (peg$currPos < peg$maxFailPos) { return; } + private parseInputParsedField(): InputParsedField { + this.skipWhitespace(); + const name = this.parseParsedIdentifier(); + const isOptional = this.match('?'); + let type: { name: TypeNotClass, isArray: boolean } | undefined; + let desc: string | undefined; -// @ts-ignore - if (peg$currPos > peg$maxFailPos) { -// @ts-ignore - peg$maxFailPos = peg$currPos; -// @ts-ignore - peg$maxFailExpected = []; + if (this.match(':')) { + this.skipWhitespace(); + const typeName = this.parseTypeNotClass(); + const isArray = this.match('[]'); + type = { name: typeName, isArray }; } -// @ts-ignore - peg$maxFailExpected.push(expected); - } - -// @ts-ignore - function peg$buildSimpleError(message, location) { -// @ts-ignore - return new peg$SyntaxError(message, null, null, location); - } + this.skipWhitespace(); + desc = this.parseParsedString(); -// @ts-ignore - function peg$buildStructuredError(expected, found, location) { -// @ts-ignore - return new peg$SyntaxError( -// @ts-ignore - peg$SyntaxError.buildMessage(expected, found), -// @ts-ignore - expected, -// @ts-ignore - found, -// @ts-ignore - location - ); + return { + name, + desc: desc?.trim(), + type, + isOptional, + }; } -// @ts-ignore - function // @ts-ignore -peg$parseParsedSignature() { -// @ts-ignore - var s0, s1, s2, s3, s4, s5, s6, s7; - -// @ts-ignore - var key = peg$currPos * 9 + 0; -// @ts-ignore - var cached = peg$resultsCache[key]; - -// @ts-ignore - if (cached) { -// @ts-ignore - peg$currPos = cached.nextPos; - -// @ts-ignore - return cached.result; - } - -// @ts-ignore - s0 = peg$currPos; -// @ts-ignore - s1 = peg$currPos; -// @ts-ignore - s2 = peg$parse_(); -// @ts-ignore - s3 = peg$parseParsedString(); -// @ts-ignore - if (s3 !== peg$FAILED) { -// @ts-ignore - s4 = peg$parse_(); -// @ts-ignore - s2 = [s2, s3, s4]; -// @ts-ignore - s1 = s2; -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s1; -// @ts-ignore - s1 = peg$FAILED; - } -// @ts-ignore - if (s1 === peg$FAILED) { -// @ts-ignore - s1 = null; - } -// @ts-ignore - s2 = peg$parseParsedFieldList(); -// @ts-ignore - s3 = peg$parse__(); -// @ts-ignore - if (s3 !== peg$FAILED) { -// @ts-ignore - if (input.substr(peg$currPos, 2) === peg$c0) { -// @ts-ignore - s4 = peg$c0; -// @ts-ignore - peg$currPos += 2; -// @ts-ignore - } else { -// @ts-ignore - s4 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e0); } - } -// @ts-ignore - if (s4 !== peg$FAILED) { -// @ts-ignore - s5 = peg$parse__(); -// @ts-ignore - if (s5 !== peg$FAILED) { -// @ts-ignore - s6 = peg$parseParsedFieldList(); -// @ts-ignore - s7 = peg$parse_(); -// @ts-ignore - peg$savedPos = s0; -// @ts-ignore - s0 = peg$f0(s1, s2, s6); -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s0; -// @ts-ignore - s0 = peg$FAILED; + private parseOutputParsedField(): OutputParsedField { + this.skipWhitespace(); + const name = this.parseParsedIdentifier(); + const isOptional = this.match('?'); + this.skipWhitespace(); + + if (this.match(':')) { + this.skipWhitespace(); + if (this.match('class')) { + const isArray = this.match('[]'); + this.skipWhitespace(); + const desc = this.parseParsedString(); + if (!desc) { + throw new Error("Expected description containing class names after type 'class'"); } -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s0; -// @ts-ignore - s0 = peg$FAILED; - } -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s0; -// @ts-ignore - s0 = peg$FAILED; - } - -// @ts-ignore - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - -// @ts-ignore - return s0; - } - -// @ts-ignore - function // @ts-ignore -peg$parseParsedFieldList() { -// @ts-ignore - var s0, s1, s2, s3, s4, s5; - -// @ts-ignore - var key = peg$currPos * 9 + 1; -// @ts-ignore - var cached = peg$resultsCache[key]; - -// @ts-ignore - if (cached) { -// @ts-ignore - peg$currPos = cached.nextPos; - -// @ts-ignore - return cached.result; - } - -// @ts-ignore - s0 = []; -// @ts-ignore - s1 = peg$parseParsedField(); -// @ts-ignore - while (s1 !== peg$FAILED) { -// @ts-ignore - s0.push(s1); -// @ts-ignore - s1 = peg$currPos; -// @ts-ignore - s2 = peg$currPos; -// @ts-ignore - s3 = peg$parse_(); -// @ts-ignore - if (input.charCodeAt(peg$currPos) === 44) { -// @ts-ignore - s4 = peg$c1; -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s4 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e1); } - } -// @ts-ignore - if (s4 !== peg$FAILED) { -// @ts-ignore - s5 = peg$parse_(); -// @ts-ignore - s3 = [s3, s4, s5]; -// @ts-ignore - s2 = s3; -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s2; -// @ts-ignore - s2 = peg$FAILED; - } -// @ts-ignore - if (s2 !== peg$FAILED) { -// @ts-ignore - s2 = peg$parseParsedField(); -// @ts-ignore - s1 = s2; -// @ts-ignore - } else { -// @ts-ignore - s1 = s2; - } - } - -// @ts-ignore - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - -// @ts-ignore - return s0; - } - -// @ts-ignore - function // @ts-ignore -peg$parseParsedField() { -// @ts-ignore - var s0, s1, s2, s3, s4, s5, s6, s7, s8; - -// @ts-ignore - var key = peg$currPos * 9 + 2; -// @ts-ignore - var cached = peg$resultsCache[key]; - -// @ts-ignore - if (cached) { -// @ts-ignore - peg$currPos = cached.nextPos; - -// @ts-ignore - return cached.result; - } - -// @ts-ignore - s0 = peg$currPos; -// @ts-ignore - s1 = peg$parse_(); -// @ts-ignore - s2 = peg$parseParsedIdentifier(); -// @ts-ignore - if (input.charCodeAt(peg$currPos) === 63) { -// @ts-ignore - s3 = peg$c2; -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s3 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e2); } - } -// @ts-ignore - if (s3 === peg$FAILED) { -// @ts-ignore - s3 = null; - } -// @ts-ignore - s4 = peg$currPos; -// @ts-ignore - s5 = peg$parse_(); -// @ts-ignore - if (input.charCodeAt(peg$currPos) === 58) { -// @ts-ignore - s6 = peg$c3; -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s6 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e3); } - } -// @ts-ignore - if (s6 !== peg$FAILED) { -// @ts-ignore - s7 = peg$parse_(); -// @ts-ignore - s8 = peg$parseParsedType(); -// @ts-ignore - if (s8 !== peg$FAILED) { -// @ts-ignore - s5 = [s5, s6, s7, s8]; -// @ts-ignore - s4 = s5; -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s4; -// @ts-ignore - s4 = peg$FAILED; - } -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s4; -// @ts-ignore - s4 = peg$FAILED; - } -// @ts-ignore - if (s4 === peg$FAILED) { -// @ts-ignore - s4 = null; - } -// @ts-ignore - s5 = peg$currPos; -// @ts-ignore - s6 = peg$parse_(); -// @ts-ignore - s7 = peg$parseParsedString(); -// @ts-ignore - if (s7 !== peg$FAILED) { -// @ts-ignore - s6 = [s6, s7]; -// @ts-ignore - s5 = s6; -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s5; -// @ts-ignore - s5 = peg$FAILED; - } -// @ts-ignore - if (s5 === peg$FAILED) { -// @ts-ignore - s5 = null; - } -// @ts-ignore - peg$savedPos = s0; -// @ts-ignore - s0 = peg$f1(s2, s3, s4, s5); - -// @ts-ignore - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - -// @ts-ignore - return s0; - } - -// @ts-ignore - function // @ts-ignore -peg$parseParsedType() { -// @ts-ignore - var s0, s1, s2; - -// @ts-ignore - var key = peg$currPos * 9 + 3; -// @ts-ignore - var cached = peg$resultsCache[key]; - -// @ts-ignore - if (cached) { -// @ts-ignore - peg$currPos = cached.nextPos; - -// @ts-ignore - return cached.result; - } - -// @ts-ignore - s0 = peg$currPos; -// @ts-ignore - s1 = peg$parseType(); -// @ts-ignore - if (s1 !== peg$FAILED) { -// @ts-ignore - if (input.substr(peg$currPos, 2) === peg$c4) { -// @ts-ignore - s2 = peg$c4; -// @ts-ignore - peg$currPos += 2; -// @ts-ignore + const classNames = desc.split(',').map(s => s.trim()); + return { + name, + type: { name: 'class', isArray, classes: classNames }, + isOptional, + }; } else { -// @ts-ignore - s2 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e4); } - } -// @ts-ignore - if (s2 === peg$FAILED) { -// @ts-ignore - s2 = null; - } -// @ts-ignore - peg$savedPos = s0; -// @ts-ignore - s0 = peg$f2(s1, s2); -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s0; -// @ts-ignore - s0 = peg$FAILED; + const typeName = this.parseTypeNotClass(); + const isArray = this.match('[]'); + this.skipWhitespace(); + const desc = this.parseParsedString(); + return { + name, + desc: desc?.trim(), + type: { name: typeName, isArray }, + isOptional, + }; + } + } else { + this.skipWhitespace(); + const desc = this.parseParsedString(); + return { + name, + desc: desc?.trim(), + type: undefined, + isOptional, + }; } - -// @ts-ignore - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - -// @ts-ignore - return s0; } -// @ts-ignore - function // @ts-ignore -peg$parseType() { -// @ts-ignore - var s0; - -// @ts-ignore - var key = peg$currPos * 9 + 4; -// @ts-ignore - var cached = peg$resultsCache[key]; - -// @ts-ignore - if (cached) { -// @ts-ignore - peg$currPos = cached.nextPos; - -// @ts-ignore - return cached.result; - } - -// @ts-ignore - if (input.substr(peg$currPos, 6) === peg$c5) { -// @ts-ignore - s0 = peg$c5; -// @ts-ignore - peg$currPos += 6; -// @ts-ignore - } else { -// @ts-ignore - s0 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e5); } - } -// @ts-ignore - if (s0 === peg$FAILED) { -// @ts-ignore - if (input.substr(peg$currPos, 6) === peg$c6) { -// @ts-ignore - s0 = peg$c6; -// @ts-ignore - peg$currPos += 6; -// @ts-ignore - } else { -// @ts-ignore - s0 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e6); } - } -// @ts-ignore - if (s0 === peg$FAILED) { -// @ts-ignore - if (input.substr(peg$currPos, 7) === peg$c7) { -// @ts-ignore - s0 = peg$c7; -// @ts-ignore - peg$currPos += 7; -// @ts-ignore - } else { -// @ts-ignore - s0 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e7); } - } -// @ts-ignore - if (s0 === peg$FAILED) { -// @ts-ignore - if (input.substr(peg$currPos, 4) === peg$c8) { -// @ts-ignore - s0 = peg$c8; -// @ts-ignore - peg$currPos += 4; -// @ts-ignore - } else { -// @ts-ignore - s0 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e8); } - } -// @ts-ignore - if (s0 === peg$FAILED) { -// @ts-ignore - if (input.substr(peg$currPos, 5) === peg$c9) { -// @ts-ignore - s0 = peg$c9; -// @ts-ignore - peg$currPos += 5; -// @ts-ignore - } else { -// @ts-ignore - s0 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e9); } - } -// @ts-ignore - if (s0 === peg$FAILED) { -// @ts-ignore - if (input.substr(peg$currPos, 8) === peg$c10) { -// @ts-ignore - s0 = peg$c10; -// @ts-ignore - peg$currPos += 8; -// @ts-ignore - } else { -// @ts-ignore - s0 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e10); } - } -// @ts-ignore - if (s0 === peg$FAILED) { -// @ts-ignore - if (input.substr(peg$currPos, 4) === peg$c11) { -// @ts-ignore - s0 = peg$c11; -// @ts-ignore - peg$currPos += 4; -// @ts-ignore - } else { -// @ts-ignore - s0 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e11); } - } - } - } - } - } + private parseTypeNotClass(): TypeNotClass { + const types: TypeNotClass[] = ["string", "number", "boolean", "json", "image", "datetime", "date"]; + for (const type of types) { + if (this.match(type)) { + return type; } } - -// @ts-ignore - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - -// @ts-ignore - return s0; + throw new Error(`Expected one of ${types.join(', ')}`); } -// @ts-ignore - function // @ts-ignore -peg$parseParsedIdentifier() { -// @ts-ignore - var s0, s1, s2; - -// @ts-ignore - var key = peg$currPos * 9 + 5; -// @ts-ignore - var cached = peg$resultsCache[key]; - -// @ts-ignore - if (cached) { -// @ts-ignore - peg$currPos = cached.nextPos; - -// @ts-ignore - return cached.result; - } - -// @ts-ignore - s0 = peg$currPos; -// @ts-ignore - s1 = []; -// @ts-ignore - if (peg$r0.test(input.charAt(peg$currPos))) { -// @ts-ignore - s2 = input.charAt(peg$currPos); -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s2 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e12); } + private parseParsedIdentifier(): ParsedIdentifier { + const match = /^[a-zA-Z_][a-zA-Z_0-9]*/.exec(this.input.slice(this.position)); + if (match) { + this.position += match[0].length; + return match[0]; } -// @ts-ignore - while (s2 !== peg$FAILED) { -// @ts-ignore - s1.push(s2); -// @ts-ignore - if (peg$r0.test(input.charAt(peg$currPos))) { -// @ts-ignore - s2 = input.charAt(peg$currPos); -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s2 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e12); } - } - } -// @ts-ignore - peg$savedPos = s0; -// @ts-ignore - s1 = peg$f3(s1); -// @ts-ignore - s0 = s1; - -// @ts-ignore - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - -// @ts-ignore - return s0; + throw new Error('Expected identifier'); } -// @ts-ignore - function // @ts-ignore -peg$parseParsedString() { -// @ts-ignore - var s0, s1, s2, s3; - -// @ts-ignore - var key = peg$currPos * 9 + 6; -// @ts-ignore - var cached = peg$resultsCache[key]; - -// @ts-ignore - if (cached) { -// @ts-ignore - peg$currPos = cached.nextPos; - -// @ts-ignore - return cached.result; - } - -// @ts-ignore - s0 = peg$currPos; -// @ts-ignore - if (input.charCodeAt(peg$currPos) === 39) { -// @ts-ignore - s1 = peg$c12; -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s1 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e13); } - } -// @ts-ignore - if (s1 !== peg$FAILED) { -// @ts-ignore - s2 = []; -// @ts-ignore - if (peg$r1.test(input.charAt(peg$currPos))) { -// @ts-ignore - s3 = input.charAt(peg$currPos); -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s3 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e14); } - } -// @ts-ignore - while (s3 !== peg$FAILED) { -// @ts-ignore - s2.push(s3); -// @ts-ignore - if (peg$r1.test(input.charAt(peg$currPos))) { -// @ts-ignore - s3 = input.charAt(peg$currPos); -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s3 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e14); } - } - } -// @ts-ignore - if (input.charCodeAt(peg$currPos) === 39) { -// @ts-ignore - s3 = peg$c12; -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s3 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e13); } - } -// @ts-ignore - if (s3 !== peg$FAILED) { -// @ts-ignore - peg$savedPos = s0; -// @ts-ignore - s0 = peg$f4(s2); -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s0; -// @ts-ignore - s0 = peg$FAILED; - } -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s0; -// @ts-ignore - s0 = peg$FAILED; - } -// @ts-ignore - if (s0 === peg$FAILED) { -// @ts-ignore - s0 = peg$currPos; -// @ts-ignore - if (input.charCodeAt(peg$currPos) === 34) { -// @ts-ignore - s1 = peg$c13; -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s1 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e15); } - } -// @ts-ignore - if (s1 !== peg$FAILED) { -// @ts-ignore - s2 = []; -// @ts-ignore - if (peg$r2.test(input.charAt(peg$currPos))) { -// @ts-ignore - s3 = input.charAt(peg$currPos); -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s3 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e16); } - } -// @ts-ignore - while (s3 !== peg$FAILED) { -// @ts-ignore - s2.push(s3); -// @ts-ignore - if (peg$r2.test(input.charAt(peg$currPos))) { -// @ts-ignore - s3 = input.charAt(peg$currPos); -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s3 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e16); } - } - } -// @ts-ignore - if (input.charCodeAt(peg$currPos) === 34) { -// @ts-ignore - s3 = peg$c13; -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s3 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e15); } - } -// @ts-ignore - if (s3 !== peg$FAILED) { -// @ts-ignore - peg$savedPos = s0; -// @ts-ignore - s0 = peg$f5(s2); -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s0; -// @ts-ignore - s0 = peg$FAILED; - } -// @ts-ignore - } else { -// @ts-ignore - peg$currPos = s0; -// @ts-ignore - s0 = peg$FAILED; - } + private parseParsedString(): string | undefined { + if (this.match("'")) { + const endQuote = this.input.indexOf("'", this.position); + if (endQuote === -1) throw new Error("Unterminated string"); + const content = this.input.slice(this.position, endQuote); + this.position = endQuote + 1; + return content; + } else if (this.match('"')) { + const endQuote = this.input.indexOf('"', this.position); + if (endQuote === -1) throw new Error("Unterminated string"); + const content = this.input.slice(this.position, endQuote); + this.position = endQuote + 1; + return content; } - -// @ts-ignore - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - -// @ts-ignore - return s0; + return undefined; } -// @ts-ignore - function // @ts-ignore -peg$parse_() { -// @ts-ignore - var s0, s1, s2; - -// @ts-ignore - var key = peg$currPos * 9 + 7; -// @ts-ignore - var cached = peg$resultsCache[key]; - -// @ts-ignore - if (cached) { -// @ts-ignore - peg$currPos = cached.nextPos; - -// @ts-ignore - return cached.result; - } - -// @ts-ignore - s0 = peg$currPos; -// @ts-ignore - s1 = []; -// @ts-ignore - if (peg$r3.test(input.charAt(peg$currPos))) { -// @ts-ignore - s2 = input.charAt(peg$currPos); -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s2 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e17); } + private skipWhitespace() { + const match = /^[ \t\r\n]+/.exec(this.input.slice(this.position)); + if (match) { + this.position += match[0].length; } -// @ts-ignore - while (s2 !== peg$FAILED) { -// @ts-ignore - s1.push(s2); -// @ts-ignore - if (peg$r3.test(input.charAt(peg$currPos))) { -// @ts-ignore - s2 = input.charAt(peg$currPos); -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s2 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e17); } - } - } -// @ts-ignore - peg$savedPos = s0; -// @ts-ignore - s1 = peg$f6(); -// @ts-ignore - s0 = s1; - -// @ts-ignore - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - -// @ts-ignore - return s0; } -// @ts-ignore - function // @ts-ignore -peg$parse__() { -// @ts-ignore - var s0, s1, s2; - -// @ts-ignore - var key = peg$currPos * 9 + 8; -// @ts-ignore - var cached = peg$resultsCache[key]; - -// @ts-ignore - if (cached) { -// @ts-ignore - peg$currPos = cached.nextPos; - -// @ts-ignore - return cached.result; - } - -// @ts-ignore - s0 = peg$currPos; -// @ts-ignore - s1 = []; -// @ts-ignore - if (peg$r3.test(input.charAt(peg$currPos))) { -// @ts-ignore - s2 = input.charAt(peg$currPos); -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s2 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e17); } + private match(str: string): boolean { + if (this.input.startsWith(str, this.position)) { + this.position += str.length; + return true; } -// @ts-ignore - if (s2 !== peg$FAILED) { -// @ts-ignore - while (s2 !== peg$FAILED) { -// @ts-ignore - s1.push(s2); -// @ts-ignore - if (peg$r3.test(input.charAt(peg$currPos))) { -// @ts-ignore - s2 = input.charAt(peg$currPos); -// @ts-ignore - peg$currPos++; -// @ts-ignore - } else { -// @ts-ignore - s2 = peg$FAILED; -// @ts-ignore - if (peg$silentFails === 0) { peg$fail(peg$e17); } - } - } -// @ts-ignore - } else { -// @ts-ignore - s1 = peg$FAILED; - } -// @ts-ignore - if (s1 !== peg$FAILED) { -// @ts-ignore - peg$savedPos = s0; -// @ts-ignore - s1 = peg$f7(); - } -// @ts-ignore - s0 = s1; - -// @ts-ignore - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - -// @ts-ignore - return s0; + return false; } -// @ts-ignore - peg$result = peg$startRuleFunction(); - -// @ts-ignore - if (peg$result !== peg$FAILED && peg$currPos === input.length) { -// @ts-ignore - return peg$result; -// @ts-ignore - } else { -// @ts-ignore - if (peg$result !== peg$FAILED && peg$currPos < input.length) { -// @ts-ignore - peg$fail(peg$endExpectation()); + private expect(str: string) { + if (!this.match(str)) { + throw new Error(`Expected "${str}"`); } - -// @ts-ignore - throw peg$buildStructuredError( -// @ts-ignore - peg$maxFailExpected, -// @ts-ignore - peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null, -// @ts-ignore - peg$maxFailPos < input.length -// @ts-ignore - ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) -// @ts-ignore - : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) - ); - } -} - -// @ts-ignore - return { - SyntaxError: peg$SyntaxError, - parse: peg$parse - }; -})() - -export interface FilePosition { - offset: number; - line: number; - column: number; -} - -export interface FileRange { - start: FilePosition; - end: FilePosition; - source: string; -} - -export interface LiteralExpectation { - type: "literal"; - text: string; - ignoreCase: boolean; -} - -export interface ClassParts extends Array {} - -export interface ClassExpectation { - type: "class"; - parts: ClassParts; - inverted: boolean; - ignoreCase: boolean; -} - -export interface AnyExpectation { - type: "any"; -} - -export interface EndExpectation { - type: "end"; -} - -export interface OtherExpectation { - type: "other"; - description: string; -} - -export type Expectation = LiteralExpectation | ClassExpectation | AnyExpectation | EndExpectation | OtherExpectation; - -declare class _PeggySyntaxError extends Error { - public static buildMessage(expected: Expectation[], found: string | null): string; - public message: string; - public expected: Expectation[]; - public found: string | null; - public location: FileRange; - public name: string; - constructor(message: string, expected: Expectation[], found: string | null, location: FileRange); - format(sources: { - source?: any; - text: string; - }[]): string; -} - -export interface TraceEvent { - type: string; - rule: string; - result?: any; - location: FileRange; } - -declare class _DefaultTracer { - private indentLevel: number; - public trace(event: TraceEvent): void; } -peggyParser.SyntaxError.prototype.name = "PeggySyntaxError"; - -export interface ParseOptions { - filename?: string; - startRule?: "ParsedSignature"; - tracer?: any; - [key: string]: any; -} -export type ParseFunction = ( - input: string, - options?: Options - ) => Options extends { startRule: infer StartRule } ? - StartRule extends "ParsedSignature" ? ParsedSignature : ParsedSignature - : ParsedSignature; -export const parse: ParseFunction = peggyParser.parse; - -export const PeggySyntaxError = peggyParser.SyntaxError as typeof _PeggySyntaxError; - -export type PeggySyntaxError = _PeggySyntaxError; - -// These types were autogenerated by ts-pegjs -export type ParsedSignature = { - desc: string | undefined; - inputs: ParsedFieldList; - outputs: ParsedFieldList; -}; -export type ParsedFieldList = ParsedField[]; -export type ParsedField = { - name: ParsedIdentifier; - desc: string | undefined; - type: any; - isOptional: true | undefined; -}; -export type ParsedType = { name: Type; isArray: boolean }; -export type Type = - | "string" - | "number" - | "boolean" - | "json" - | "image" - | "datetime" - | "date"; -export type ParsedIdentifier = string; -export type ParsedString = string; -export type _ = ""; -export type __ = ""; +export function parseSignature(input: string): ParsedSignature { + const parser = new SignatureParser(input); + return parser.parse(); +} \ No newline at end of file diff --git a/src/ax/dsp/prompt.ts b/src/ax/dsp/prompt.ts index 6cb70559..976abf53 100644 --- a/src/ax/dsp/prompt.ts +++ b/src/ax/dsp/prompt.ts @@ -1,5 +1,6 @@ import type { AxChatRequest } from '../ai/types.js'; +import { formatDateWithTimezone } from './datetime.js'; import { type AxFieldValue } from './program.js'; import type { AxField, AxIField, AxSignature } from './sig.js'; import { validateValue } from './util.js'; @@ -324,6 +325,13 @@ const processValue = ( field: Readonly, value: Readonly ): AxFieldValue => { + if (field.type?.name === 'date' && value instanceof Date) { + const v = value.toISOString(); + return v.slice(0, v.indexOf('T')); + } + if (field.type?.name === 'datetime' && value instanceof Date) { + return formatDateWithTimezone(value); + } if (field.type?.name === 'image' && typeof value === 'object') { return value; } @@ -356,14 +364,17 @@ const toVarDesc = (type?: Readonly) => { description = 'a boolean'; break; case 'date': - description = 'date in "YYYY-MM-DD" format'; + description = 'a date ("YYYY-MM-DD" format)'; break; case 'datetime': - description = 'date time in "YYYY-MM-DD HH:mm Timezone" format'; + description = 'a date time ("YYYY-MM-DD HH:mm Timezone" format)'; break; case 'json': description = 'a JSON object'; break; + case 'class': + description = `a classification class (allowed classes: ${type.classes?.join(', ')})`; + break; default: description = 'an unknown type'; break; diff --git a/src/ax/dsp/sig.test.ts b/src/ax/dsp/sig.test.ts index 07dc4fea..5aeb6835 100644 --- a/src/ax/dsp/sig.test.ts +++ b/src/ax/dsp/sig.test.ts @@ -1,11 +1,12 @@ import test from 'ava'; -import { parse } from './parser.js'; +import { parseSignature } from './parser.js'; test('signature parsing', (t) => { - const sig = parse( - `"hello world" context?:string "some context", query:string 'some query' -> answers:string[]` + const sig = parseSignature( + `"hello world" context?:string "some context", query:string 'some query' -> answers:string[], messageType:class "reminder, follow-up"` ); + t.is(sig.desc, 'hello world'); t.deepEqual(sig.inputs[0], { @@ -19,19 +20,27 @@ test('signature parsing', (t) => { desc: 'some query', name: 'query', type: { name: 'string', isArray: false }, - isOptional: undefined + isOptional: false }); t.deepEqual(sig.outputs[0], { desc: undefined, name: 'answers', type: { name: 'string', isArray: true }, - isOptional: undefined + isOptional: false }); -}); -test('signature parsing: invalid signature', (t) => { - t.throws(() => - parse(`context?:string, query:boom -> test:image, answers:string[]`) - ); + t.deepEqual(sig.outputs[1], { + isOptional: false, + name: 'messageType', + type: { name: 'class', isArray: false, classes: ['reminder', 'follow-up'] } + }); }); + +// test('signature parsing: invalid signature', (t) => { +// t.throws(() => +// parseSignature( +// `context?:string, query:boom -> test:image, answers:string[]` +// ) +// ); +// }); diff --git a/src/ax/dsp/sig.ts b/src/ax/dsp/sig.ts index 26273dff..e65edc23 100644 --- a/src/ax/dsp/sig.ts +++ b/src/ax/dsp/sig.ts @@ -2,7 +2,12 @@ import { createHash } from 'crypto'; import type { AxFunctionJSONSchema } from '../ai/types.js'; -import { parse, type ParsedField } from './parser.js'; +import { + parseSignature, + type InputParsedField, + type OutputParsedField, + type ParsedSignature +} from './parser.js'; export interface AxField { name: string; @@ -16,8 +21,10 @@ export interface AxField { | 'json' | 'image' | 'date' - | 'datetime'; // extend this as needed + | 'datetime' + | 'class'; isArray: boolean; + classes?: string[]; }; isOptional?: boolean; } @@ -34,11 +41,13 @@ export class AxSignature { constructor(signature: Readonly) { if (typeof signature === 'string') { - let sig; + let sig: ParsedSignature; try { - sig = parse(signature); + sig = parseSignature(signature); } catch (e) { - throw new Error('invalid signature string: ' + signature); + throw new Error( + `Invalid Signature: ${(e as Error).message} (${signature})` + ); } this.description = sig.desc; this.inputFields = sig.inputs.map((v) => this.parseParsedField(v)); @@ -59,7 +68,9 @@ export class AxSignature { } } - private parseParsedField = (field: Readonly): AxIField => { + private parseParsedField = ( + field: Readonly + ): AxIField => { if (!field.name || field.name.length === 0) { throw new Error('Field name is required.'); } @@ -68,7 +79,7 @@ export class AxSignature { return { name: field.name, title, - description: field.desc, + description: 'desc' in field ? field.desc : undefined, isOptional: field.isOptional, type: field.type ?? { name: 'string', isArray: false } }; @@ -239,7 +250,7 @@ function validateField(field: Readonly): void { if (!isValidCase(field.name)) { throw new Error( - 'Field name must be in camel case or snake case: ' + field.name + `Invalid field name '${field.name}', it must be camel case or snake case: ` ); } @@ -256,12 +267,12 @@ function validateField(field: Readonly): void { 'datetime', 'date', 'time', - 'type' + 'type', + 'class' ].includes(field.name) ) { throw new Error( - 'Invalid field name, please make it more descriptive (eg. noteText): ' + - field.name + `Invalid field name '${field.name}', please make it more descriptive (eg. companyDescription)` ); } } diff --git a/src/ax/dsp/util.ts b/src/ax/dsp/util.ts index 8f129424..b67efec6 100644 --- a/src/ax/dsp/util.ts +++ b/src/ax/dsp/util.ts @@ -44,6 +44,10 @@ export const validateValue = ( return typeof val === 'number'; case 'boolean': return typeof val === 'boolean'; + case 'date': + return val instanceof Date || typeof val === 'string'; + case 'datetime': + return val instanceof Date || typeof val === 'string'; default: return false; // Unknown or unsupported type } diff --git a/src/ax/package.json b/src/ax/package.json index 9ca8bf60..916f5b6b 100644 --- a/src/ax/package.json +++ b/src/ax/package.json @@ -20,7 +20,6 @@ "scripts": { "build": "run-s build:*", "build:clean": "node ../../scripts/clean.js build", - "build:parser": "npx peggy --plugin ts-pegjs --extra-options-file ./dsp/parser.json --cache --output ./dsp/parser.ts ./dsp/parser.peggy", "build:module": "tsc -p tsconfig.build.module.json", "build:cjs": "tsc -p tsconfig.build.cjs.json", "watch:build": "tsc -p tsconfig.build.module.json -w", @@ -38,7 +37,6 @@ "@ava/typescript": "^5.0.0", "ava": "^6.1.3", "npm-run-all": "^4.1.5", - "ts-pegjs": "^4.2.1", "tsimp": "^2.0.11", "tsx": "^4.7.1" }, diff --git a/src/examples/extract.ts b/src/examples/extract.ts index 93532b77..2aaeb207 100644 --- a/src/examples/extract.ts +++ b/src/examples/extract.ts @@ -2,7 +2,7 @@ import { AxAI, AxAIOpenAIModel, AxGenerate } from '@ax-llm/ax'; const chatMessage = `Hello Mike, How are you set for a call tomorrow or Friday? I have a few things to discuss with you. Also the ticket number is 300. Let me know what time works best for you. Thanks!`; -const currentDate = new Date().toUTCString(); +const currentDate = new Date(); // Example with OpenAI using custom labels in place of model names const ai = new AxAI({ @@ -17,7 +17,7 @@ ai.setOptions({ debug: true }); const gen = new AxGenerate( ai, - `chatMessage, currentDate -> subject, foundMeeting:boolean, ticketNumber?:number, customerNumber?:number, datesMentioned:datetime[]` + `chatMessage, currentDate:datetime -> subject, foundMeeting:boolean, ticketNumber?:number, customerNumber?:number, datesMentioned:datetime[], messageType:class "reminder, follow-up, meeting, other"` ); const res = await gen.forward(