From ae21605fb79eb06d6d8f4b484d21c0ff33c94a37 Mon Sep 17 00:00:00 2001 From: John Dvorak Date: Mon, 3 Aug 2026 08:48:39 -0700 Subject: [PATCH] evidence-dsl: extract Evidence DSL v2 compiler from @arbiter/core The Evidence DSL (ADR-000) is a thin declarative layer that compiles to engine rule types. It has zero runtime coupling to the core engine (DSLCompiler takes an arbiter as a duck-typed argument; the only shared code was the ip-utils helpers, now local). Extracting it into its own package keeps the core artifact free of the DSL surface. - @arbiter/evidence-dsl depends on @arbiter/core (config formats are the compilation target) - deep-path exports for the compiler, parser, generator, validation, and built-in functions (the surface the core's DSL tests consume) - tests moved alongside; generate-parser script + peggy devDep local - CI: test on push, publish on v* tags --- .gitea/workflows/ci.yaml | 56 + .gitignore | 2 + .npmrc | 4 + package-lock.json | 124 + package.json | 31 + scripts/generate-parser.js | 51 + src/DSLCompiler.js | 356 ++ src/generator/RuleGenerator.js | 1039 +++++ src/grammar/dsl.peggy | 420 ++ src/grammar/expression.peggy | 167 + src/index.js | 22 + src/interpreter/BuiltInFunctions.js | 182 + src/interpreter/ExpressionInterpreter.js | 323 ++ src/interpreter/PredicateResolver.js | 167 + src/nodes/AggregationNode.js | 162 + src/nodes/BaseNode.js | 157 + src/nodes/BehaviorNode.js | 151 + src/nodes/DefeasibleLogicNode.js | 150 + src/nodes/DefinitionNode.js | 117 + src/nodes/DirectEvidenceNode.js | 78 + src/nodes/EvidenceBodyNode.js | 82 + src/nodes/EvidenceNode.js | 117 + src/nodes/ExpressionNode.js | 260 ++ src/nodes/FactNode.js | 166 + src/nodes/FieldNode.js | 141 + src/nodes/FusionNode.js | 170 + src/nodes/MeasureBodyNode.js | 130 + src/nodes/MeasureNode.js | 117 + src/nodes/ParameterNode.js | 79 + src/nodes/PatternMatchNode.js | 142 + src/nodes/PredicateNode.js | 106 + src/nodes/ProgramNode.js | 158 + src/nodes/SourceNode.js | 87 + src/nodes/WithClauseNode.js | 154 + src/nodes/index.js | 24 + src/parser/DSLParser.js | 4998 +++++++++++++++++++++ src/parser/DslParser.js | 4998 +++++++++++++++++++++ src/parser/ExpressionParser.js | 1614 +++++++ src/parser/GeneratedParser.js | 5186 ++++++++++++++++++++++ src/parser/PeggyDSLParser.js | 114 + src/parser/PeggyWrapper.js | 45 + src/utils/ip-utils-fast.js | 165 + src/utils/ip-utils.js | 123 + src/validation/DSLPrelude.js | 37 + src/validation/DSLValidation.js | 1002 +++++ tests/DSLCompiler.test.js | 265 ++ tests/DefinitionTests.js | 283 ++ tests/EvidenceTests.js | 374 ++ tests/ExpressionTests.js | 233 + tests/FactTests.js | 232 + tests/IntegrationTests.js | 534 +++ tests/MeasureTests.js | 306 ++ tests/PeggyParser.test.js | 101 + tests/README.md | 249 ++ 54 files changed, 26551 insertions(+) create mode 100644 .gitea/workflows/ci.yaml create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 package-lock.json create mode 100644 package.json create mode 100755 scripts/generate-parser.js create mode 100644 src/DSLCompiler.js create mode 100644 src/generator/RuleGenerator.js create mode 100644 src/grammar/dsl.peggy create mode 100644 src/grammar/expression.peggy create mode 100644 src/index.js create mode 100644 src/interpreter/BuiltInFunctions.js create mode 100644 src/interpreter/ExpressionInterpreter.js create mode 100644 src/interpreter/PredicateResolver.js create mode 100644 src/nodes/AggregationNode.js create mode 100644 src/nodes/BaseNode.js create mode 100644 src/nodes/BehaviorNode.js create mode 100644 src/nodes/DefeasibleLogicNode.js create mode 100644 src/nodes/DefinitionNode.js create mode 100644 src/nodes/DirectEvidenceNode.js create mode 100644 src/nodes/EvidenceBodyNode.js create mode 100644 src/nodes/EvidenceNode.js create mode 100644 src/nodes/ExpressionNode.js create mode 100644 src/nodes/FactNode.js create mode 100644 src/nodes/FieldNode.js create mode 100644 src/nodes/FusionNode.js create mode 100644 src/nodes/MeasureBodyNode.js create mode 100644 src/nodes/MeasureNode.js create mode 100644 src/nodes/ParameterNode.js create mode 100644 src/nodes/PatternMatchNode.js create mode 100644 src/nodes/PredicateNode.js create mode 100644 src/nodes/ProgramNode.js create mode 100644 src/nodes/SourceNode.js create mode 100644 src/nodes/WithClauseNode.js create mode 100644 src/nodes/index.js create mode 100644 src/parser/DSLParser.js create mode 100644 src/parser/DslParser.js create mode 100644 src/parser/ExpressionParser.js create mode 100644 src/parser/GeneratedParser.js create mode 100644 src/parser/PeggyDSLParser.js create mode 100644 src/parser/PeggyWrapper.js create mode 100644 src/utils/ip-utils-fast.js create mode 100644 src/utils/ip-utils.js create mode 100644 src/validation/DSLPrelude.js create mode 100644 src/validation/DSLValidation.js create mode 100644 tests/DSLCompiler.test.js create mode 100644 tests/DefinitionTests.js create mode 100644 tests/EvidenceTests.js create mode 100644 tests/ExpressionTests.js create mode 100644 tests/FactTests.js create mode 100644 tests/IntegrationTests.js create mode 100644 tests/MeasureTests.js create mode 100644 tests/PeggyParser.test.js create mode 100644 tests/README.md diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml new file mode 100644 index 0000000..28ba692 --- /dev/null +++ b/.gitea/workflows/ci.yaml @@ -0,0 +1,56 @@ +name: CI + +on: + push: + branches: [master, main] + tags: ['v*'] + pull_request: + branches: [master, main] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Auth for Gitea npm registry + run: | + echo "@arbiter:registry=https://hub.kl1.tenere.ai/api/packages/Arbiter/npm/" > .npmrc + echo "//hub.kl1.tenere.ai/api/packages/Arbiter/npm/:_authToken=${{ secrets.PACKAGE_TOKEN }}" >> .npmrc + echo "@tenere:registry=https://hub.kl1.tenere.ai/api/packages/Tenere/npm/" >> .npmrc + echo "//hub.kl1.tenere.ai/api/packages/Tenere/npm/:_authToken=${{ secrets.PACKAGE_TOKEN }}" >> .npmrc + + - run: npm ci + + - name: Full suite + run: npm test + + publish: + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') + needs: test + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Auth for Gitea npm registry + run: | + echo "@arbiter:registry=https://hub.kl1.tenere.ai/api/packages/Arbiter/npm/" > .npmrc + echo "//hub.kl1.tenere.ai/api/packages/Arbiter/npm/:_authToken=${{ secrets.PACKAGE_TOKEN }}" >> .npmrc + echo "@tenere:registry=https://hub.kl1.tenere.ai/api/packages/Tenere/npm/" >> .npmrc + echo "//hub.kl1.tenere.ai/api/packages/Tenere/npm/:_authToken=${{ secrets.PACKAGE_TOKEN }}" >> .npmrc + + - run: npm ci + + - run: npm publish diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..552f221 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +*.log diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..be917a1 --- /dev/null +++ b/.npmrc @@ -0,0 +1,4 @@ +@arbiter:registry=https://hub.kl1.tenere.ai/api/packages/Arbiter/npm/ +//hub.kl1.tenere.ai/api/packages/Arbiter/npm/:_authToken=${PACKAGE_TOKEN} +@tenere:registry=https://hub.kl1.tenere.ai/api/packages/Tenere/npm/ +//hub.kl1.tenere.ai/api/packages/Tenere/npm/:_authToken=${PACKAGE_TOKEN} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3edf4c1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,124 @@ +{ + "name": "@arbiter/evidence-dsl", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@arbiter/evidence-dsl", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@arbiter/core": "^1.0.1" + }, + "devDependencies": { + "peggy": "^5.0.6" + } + }, + "node_modules/@arbiter/core": { + "version": "1.0.1", + "resolved": "https://hub.kl1.tenere.ai/api/packages/Arbiter/npm/%40arbiter%2Fcore/-/1.0.1/core-1.0.1.tgz", + "integrity": "sha512-BfIv6vRKsuJR39WBkBGxl2PQ/LDoW/J1u4UeFrog0AbeTSTvRqhxG1+its2pAcUvcQXq99ZOccFlK3XgNRKt4Q==", + "license": "ISC", + "dependencies": { + "@tenere/pltc-core": "^0.6.3", + "heapify": "^1.0.2", + "uuidv7": "^1.0.2" + } + }, + "node_modules/@peggyjs/from-mem": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@peggyjs/from-mem/-/from-mem-3.1.3.tgz", + "integrity": "sha512-LLlgtfXIaeYXoOYovOI0spLM8ZXaqkAlmcRRrLzHJzLMqkU6Sw0R4KMoCoHx1PjaP815pSCBlS+BN6aD8t1Jgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "7.7.4" + }, + "engines": { + "node": ">=20.8" + } + }, + "node_modules/@tenere/graph-core": { + "version": "1.0.1", + "resolved": "https://hub.kl1.tenere.ai/api/packages/Tenere/npm/%40tenere%2Fgraph-core/-/1.0.1/graph-core-1.0.1.tgz", + "integrity": "sha512-29EzF1yBVLuaaapmrL+xNGRaWsM2Ln0G+WHVJURNzwQnDUC2ZSgaeVRMGj3aHFOj2K4mfSh9JqBP5Az2ZHU4pw==", + "license": "MIT" + }, + "node_modules/@tenere/pltc-core": { + "version": "0.6.3", + "resolved": "https://hub.kl1.tenere.ai/api/packages/Tenere/npm/%40tenere%2Fpltc-core/-/0.6.3/pltc-core-0.6.3.tgz", + "integrity": "sha512-+XsxNw35fyX8ku19FfyFsb6DgYoaKb6wkAbo1VF+moeyZ0D3AqDTofsBnd2sv5o2y8RpB3XftBxt0Hv1sBcQtg==", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "@tenere/graph-core": "^1.0.1" + } + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/heapify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/heapify/-/heapify-1.0.2.tgz", + "integrity": "sha512-h/b3y12Orh2VsISvDsF/vulkoKH38P7yr223hfWJILo3imy7dX8f9ZrBgkkfLsJG11g8GI1/y6+8POSxgR7YcQ==", + "license": "MIT" + }, + "node_modules/peggy": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/peggy/-/peggy-5.1.0.tgz", + "integrity": "sha512-IEo5aYRZ2kXH4Qby06cjtL114PZnwLoTiA41vUmg2vPZgANn+c87m5BUurhuDr5/cu758ZlpgsAfBVx+hhO5+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peggyjs/from-mem": "3.1.3", + "commander": "^14.0.3", + "source-map-generator": "2.0.6" + }, + "bin": { + "peggy": "bin/peggy.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map-generator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/source-map-generator/-/source-map-generator-2.0.6.tgz", + "integrity": "sha512-IlassDs1Ve8nV6uyQZXF9kdkJpVKnMte2JZQXu13M0A5zwc+vu6+LNHfmxsHBMDtoZE21RHiKI0/xvpecZRCNg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/uuidv7": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/uuidv7/-/uuidv7-1.2.1.tgz", + "integrity": "sha512-4kPkK3/XTQW9Hbm4CaqfICn+kY9LJtDVEOfgsRRra/+n2Ofg4NqzRFceAkxvQ/Ud/6BpHOPzj8cirqM7TzTN5Q==", + "license": "Apache-2.0", + "bin": { + "uuidv7": "cli.js" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5d33202 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "@arbiter/evidence-dsl", + "version": "1.0.0", + "description": "Evidence DSL v2 compiler: translates the natural Evidence DSL (ADR-000) into @arbiter/core relation configurations.", + "license": "ISC", + "type": "module", + "main": "src/index.js", + "exports": { + ".": "./src/index.js", + "./package.json": "./package.json", + "./DSLCompiler": "./src/DSLCompiler.js", + "./parser/DSLParser": "./src/parser/DSLParser.js", + "./parser/GeneratedParser": "./src/parser/GeneratedParser.js", + "./generator/RuleGenerator": "./src/generator/RuleGenerator.js", + "./validation/DSLValidation": "./src/validation/DSLValidation.js", + "./interpreter/BuiltInFunctions": "./src/interpreter/BuiltInFunctions.js" + }, + "files": [ + "src/" + ], + "scripts": { + "test": "node --test --test-force-exit \"tests/**/*.test.js\"", + "generate:parser": "node scripts/generate-parser.js" + }, + "dependencies": { + "@arbiter/core": "^1.0.1" + }, + "devDependencies": { + "peggy": "^5.0.6" + } +} diff --git a/scripts/generate-parser.js b/scripts/generate-parser.js new file mode 100755 index 0000000..3d7cb9c --- /dev/null +++ b/scripts/generate-parser.js @@ -0,0 +1,51 @@ +#!/usr/bin/env node + +/** + * Script to generate the DSL parser from Peggy grammar + */ + +import peggy from 'peggy'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const grammarPath = path.join(__dirname, '../src/grammar/dsl.peggy'); +const outputPath = path.join(__dirname, '../src/parser/GeneratedParser.js'); + +console.log('Generating DSL parser from Peggy grammar...'); +console.log('Grammar:', grammarPath); +console.log('Output:', outputPath); + +try { + // Read the grammar file + const grammar = fs.readFileSync(grammarPath, 'utf8'); + + // Generate the parser + const parserSource = peggy.generate(grammar, { + format: 'es', + output: 'source', + grammarSource: 'dsl.peggy' + }); + + // Write the generated parser + fs.writeFileSync(outputPath, parserSource); + + console.log('✅ Parser generated successfully!'); + console.log('Generated file:', outputPath); + +} catch (error) { + console.error('❌ Error generating parser:'); + console.error(error.message); + + if (error.format) { + console.error('\nFormatted error:'); + console.error(error.format([ + { source: 'dsl.peggy', text: fs.readFileSync(grammarPath, 'utf8') } + ])); + } + + process.exit(1); +} diff --git a/src/DSLCompiler.js b/src/DSLCompiler.js new file mode 100644 index 0000000..8a1f796 --- /dev/null +++ b/src/DSLCompiler.js @@ -0,0 +1,356 @@ +import { parse } from './parser/DSLParser.js'; +import { RuleGenerator } from './generator/RuleGenerator.js'; +import { validateDslText } from './validation/DSLValidation.js'; + +/** + * DSL Compiler - Main integration layer + * Compiles DSL text into rule configurations for the zanzibar-graph system + */ +export class DSLCompiler { + constructor(arbiter) { + this.arbiter = arbiter; + this.parser = parse; + this.generator = new RuleGenerator(arbiter); + this.compiledPrograms = new Map(); + } + + /** + * Compile DSL text into rule configurations + * @param {string} dslText - DSL text to compile + * @param {string} programName - Optional name for the program + * @returns {Object} Compilation result + */ + compile(dslText, programName = 'default') { + try { + const validation = validateDslText(dslText); + if (!validation.success) { + return { + success: false, + errors: validation.errors, + warnings: validation.warnings, + program: null, + generatedRules: new Map() + }; + } + + const program = validation.program; + + // Convert plain AST to ProgramNode structure + const programNode = { + definitions: program.body.filter(s => s.type === 'Definition'), + facts: program.body.filter(s => s.type === 'Fact'), + evidence: program.body.filter(s => s.type === 'Evidence'), + measures: program.body.filter(s => s.type === 'Measure'), + validate: () => ({ isValid: true, errors: [], warnings: [] }) + }; + + // Generate rules from AST + const generationResult = this.generator.generateRules(programNode); + + if (!generationResult.success) { + return { + success: false, + errors: generationResult.errors, + warnings: [], + program: programNode, + generatedRules: new Map() + }; + } + + // Store compiled program + this.compiledPrograms.set(programName, { + program: programNode, + generatedRules: this.generator.getGeneratedRules(), + dependencyIndex: this.generator.getDependencyIndex(), + compiledAt: new Date() + }); + + return { + success: true, + errors: validation.errors, + warnings: validation.warnings, + program: programNode, + generatedRules: this.generator.getGeneratedRules(), + dependencyIndex: this.generator.getDependencyIndex(), + generatedCount: generationResult.generatedCount + }; + } catch (error) { + return { + success: false, + errors: [`Compilation error: ${error.message}`], + warnings: [], + program: null, + generatedRules: new Map() + }; + } + } + + /** + * Compile multiple DSL programs + * @param {Object} programs - Map of program names to DSL text + * @returns {Object} Compilation result for all programs + */ + compileMultiple(programs) { + const results = {}; + let overallSuccess = true; + const allErrors = []; + const allWarnings = []; + + for (const [name, dslText] of Object.entries(programs)) { + const result = this.compile(dslText, name); + results[name] = result; + + if (!result.success) { + overallSuccess = false; + } + + allErrors.push(...result.errors.map(err => `${name}: ${err}`)); + allWarnings.push(...result.warnings.map(warn => `${name}: ${warn}`)); + } + + return { + success: overallSuccess, + errors: allErrors, + warnings: allWarnings, + results: results + }; + } + + /** + * Get compiled program by name + * @param {string} programName - Name of the program + * @returns {Object|null} Compiled program or null + */ + getCompiledProgram(programName) { + return this.compiledPrograms.get(programName) || null; + } + + /** + * Get all compiled programs + * @returns {Map} Map of all compiled programs + */ + getAllCompiledPrograms() { + return this.compiledPrograms; + } + + /** + * Remove compiled program + * @param {string} programName - Name of the program to remove + * @returns {boolean} True if removed successfully + */ + removeCompiledProgram(programName) { + return this.compiledPrograms.delete(programName); + } + + /** + * Clear all compiled programs + */ + clearCompiledPrograms() { + this.compiledPrograms.clear(); + } + + /** + * Get compilation statistics + * @returns {Object} Compilation statistics + */ + getCompilationStats() { + const stats = { + totalPrograms: this.compiledPrograms.size, + totalRules: 0, + programs: {} + }; + + this.compiledPrograms.forEach((program, name) => { + const programStats = { + name: name, + compiledAt: program.compiledAt, + ruleCount: program.generatedRules.size, + definitions: program.program.definitions.length, + facts: program.program.facts.length, + evidence: program.program.evidence.length, + measures: program.program.measures.length + }; + + stats.programs[name] = programStats; + stats.totalRules += program.generatedRules.size; + }); + + return stats; + } + + /** + * Validate DSL text without compiling + * @param {string} dslText - DSL text to validate + * @returns {Object} Validation result + */ + validate(dslText) { + const validation = validateDslText(dslText); + return { + success: validation.success, + errors: validation.errors, + warnings: validation.warnings, + program: validation.program + }; + } + + /** + * Get parser errors from last parse + * @returns {string[]} Array of parser errors + */ + getParserErrors() { + return this.parser.getErrors(); + } + + /** + * Get generator errors from last generation + * @returns {string[]} Array of generator errors + */ + getGeneratorErrors() { + return this.generator.getErrors(); + } + + /** + * Check if a relation is configured + * @param {string} relation - Relation name to check + * @returns {boolean} True if relation is configured + */ + isRelationConfigured(relation) { + return this.arbiter.relationConfigs.has(relation); + } + + /** + * Get relation configuration + * @param {string} relation - Relation name + * @returns {Object|null} Relation configuration or null + */ + getRelationConfig(relation) { + return this.arbiter.relationConfigs.get(relation) || null; + } + + /** + * Get all configured relations + * @returns {Map} Map of all relation configurations + */ + getAllRelationConfigs() { + return this.arbiter.relationConfigs; + } + + /** + * Export compiled program to JSON + * @param {string} programName - Name of the program to export + * @returns {string|null} JSON string or null if program not found + */ + exportProgram(programName) { + const program = this.getCompiledProgram(programName); + if (!program) { + return null; + } + + return JSON.stringify({ + name: programName, + compiledAt: program.compiledAt, + program: this.serializeProgram(program.program), + generatedRules: Array.from(program.generatedRules.entries()) + }, null, 2); + } + + /** + * Import compiled program from JSON + * @param {string} jsonString - JSON string to import + * @returns {boolean} True if imported successfully + */ + importProgram(jsonString) { + try { + const data = JSON.parse(jsonString); + const program = this.deserializeProgram(data.program); + + this.compiledPrograms.set(data.name, { + program: program, + generatedRules: new Map(data.generatedRules), + compiledAt: new Date(data.compiledAt) + }); + + // Apply rules to arbiter + data.generatedRules.forEach(([relation, config]) => { + this.arbiter.setRelationConfig(relation, config); + }); + + return true; + } catch (error) { + return false; + } + } + + /** + * Serialize program to plain object + * @param {ProgramNode} program - Program to serialize + * @returns {Object} Serialized program + */ + serializeProgram(program) { + // This is a simplified serialization - in a real implementation, + // you'd want to properly serialize all node types + return { + type: 'Program', + definitions: program.definitions.map(def => ({ + type: 'Definition', + name: def.name, + definitionType: def.definitionType, + fields: def.fields.map(field => ({ + type: 'Field', + name: field.name, + type: field.type, + isArray: field.isArray, + isOptional: field.isOptional + })) + })), + facts: program.facts.map(fact => ({ + type: 'Fact', + name: fact.name, + parameters: fact.parameters.map(param => ({ + type: 'Parameter', + name: param.name, + type: param.type, + isArray: param.isArray + })) + })), + evidence: program.evidence.map(ev => ({ + type: 'Evidence', + name: ev.name, + parameters: ev.parameters.map(param => ({ + type: 'Parameter', + name: param.name, + type: param.type, + isArray: param.isArray + })), + returnType: ev.returnType + })), + measures: program.measures.map(measure => ({ + type: 'Measure', + name: measure.name, + parameters: measure.parameters.map(param => ({ + type: 'Parameter', + name: param.name, + type: param.type, + isArray: param.isArray + })), + returnType: measure.returnType + })) + }; + } + + /** + * Deserialize program from plain object + * @param {Object} data - Serialized program data + * @returns {ProgramNode} Deserialized program + */ + deserializeProgram(data) { + // This is a simplified deserialization - in a real implementation, + // you'd want to properly deserialize all node types + const program = new ProgramNode(); + + // Note: This is a basic implementation. In practice, you'd need + // to properly reconstruct all the AST nodes from the serialized data + + return program; + } +} diff --git a/src/generator/RuleGenerator.js b/src/generator/RuleGenerator.js new file mode 100644 index 0000000..c64b32a --- /dev/null +++ b/src/generator/RuleGenerator.js @@ -0,0 +1,1039 @@ +import { ProgramNode, DefinitionNode, FactNode, EvidenceNode, MeasureNode, DirectEvidenceNode, PatternMatchNode, DefeasibleLogicNode, FusionNode, PredicateNode, ExpressionNode } from '../nodes/index.js'; + +/** + * Rule Generator for converting AST to setRelationConfig calls + * Generates rule configurations that interface with the existing rule system + */ +export class RuleGenerator { + constructor(arbiter) { + this.arbiter = arbiter; + this.generatedRules = new Map(); + this.errors = []; + this.dependencyIndex = new Map(); + } + + /** + * Generate rules from AST program + * @param {ProgramNode} program - AST program to generate rules from + * @returns {Object} Generation result with success status and errors + */ + generateRules(program) { + this.errors = []; + this.generatedRules.clear(); + this.dependencyIndex.clear(); + + try { + // Generate rules for each evidence definition + program.evidence.forEach(evidence => { + this.generateEvidenceRules(evidence); + }); + + // Generate rules for each measure definition + program.measures.forEach(measure => { + this.generateMeasureRules(measure); + }); + + // Generate fact relation configs (requires injection at check time) + program.facts.forEach(fact => { + this.generateFactConfig(fact); + }); + + // Apply generated rules to arbiter + this.applyRulesToArbiter(); + + return { + success: this.errors.length === 0, + errors: this.errors, + generatedCount: this.generatedRules.size + }; + } catch (error) { + this.errors.push(`Generation error: ${error.message}`); + return { + success: false, + errors: this.errors, + generatedCount: 0 + }; + } + } + + /** + * Generate rules for an evidence definition + * @param {EvidenceNode} evidence - Evidence to generate rules for + */ + generateEvidenceRules(evidence) { + const relationName = evidence.name; + const ruleConfig = this.buildRuleConfig(evidence); + + if (ruleConfig) { + this._annotateDependencies(relationName, ruleConfig); + this.generatedRules.set(relationName, ruleConfig); + } + } + + /** + * Generate rules for a measure definition + * @param {MeasureNode} measure - Measure to generate rules for + */ + generateMeasureRules(measure) { + const relationName = measure.name; + const ruleConfig = this.buildMeasureRuleConfig(measure); + + if (ruleConfig) { + this._annotateDependencies(relationName, ruleConfig); + this.generatedRules.set(relationName, ruleConfig); + } + } + + /** + * Generate relation config for a fact declaration. + * Facts require injection at check time (e.g. from user DB, session store). + * Registered as type: 'direct' with requiresInjection flag so the gate-check + * pipeline can pre-fetch facts before calling graphStore.check(). + * @param {FactNode} fact - Fact to generate config for + */ + generateFactConfig(fact) { + const name = fact.name; + const params = fact.params || []; + const paramNames = params.map(p => p.name); + const paramTypes = params.map(p => p.type); + + this.generatedRules.set(name, { + type: 'direct', + relation: name, + isFactRelation: true, + requiresInjection: true, + arity: paramTypes.length, + paramTypes: paramTypes, + paramNames: paramNames, + cacheDirective: fact.cacheDirective || fact.cache || 'lazy', + properties: Object.fromEntries( + (fact.properties || []).map(p => + typeof p === 'string' ? [p, true] : Array.isArray(p) ? p : [p, true] + ) + ) + }); + } + + _annotateDependencies(relationName, ruleConfig) { + if (!ruleConfig || typeof ruleConfig !== 'object') return; + const dependsOn = new Set(); + const dependsByLevel = { + never: new Set(), + always: new Set(), + requires: new Set(), + when: new Set(), + unless: new Set(), + ordinary: new Set() + }; + + const collect = (rule, targetSet) => { + if (!rule || typeof rule !== 'object') return; + if (rule.type === 'direct' && rule.relation) { + targetSet.add(rule.relation); + } + if (rule.type === 'tuple_to_userset') { + if (rule.tuplesetRelation) targetSet.add(rule.tuplesetRelation); + if (rule.computedRelation) targetSet.add(rule.computedRelation); + } + if (rule.type === 'parent' && rule.parentRelation) { + targetSet.add(rule.parentRelation); + } + if (rule.type === 'multi_hop' && rule.relation) { + targetSet.add(rule.relation); + } + if (rule.type === 'chain' && Array.isArray(rule.steps)) { + for (const step of rule.steps) { + if (typeof step === 'string') targetSet.add(step); + else if (step && typeof step.relation === 'string') targetSet.add(step.relation); + } + } + if (rule.type === 'relational_comparator') { + if (rule.left?.valueRelation) targetSet.add(rule.left.valueRelation); + if (rule.right?.valueRelation) targetSet.add(rule.right.valueRelation); + if (rule.left?.rule) collect(rule.left.rule, targetSet); + if (rule.right?.rule) collect(rule.right.rule, targetSet); + } + + const logicalKeys = ['union', 'intersection', 'exclusion', 'never', 'always', 'requires', 'when', 'unless']; + for (const key of logicalKeys) { + const node = rule[key]; + const ruleList = node?.rules || node?.union?.rules || node?.intersection?.rules; + if (Array.isArray(ruleList)) { + for (const child of ruleList) collect(child, targetSet); + } + if (node?.rule) collect(node.rule, targetSet); + } + }; + + const collectWithKeys = (rule, keys, targetSet) => { + if (!rule || typeof rule !== 'object') return; + for (const key of keys) { + const node = rule[key]; + const ruleList = node?.rules || node?.union?.rules || node?.intersection?.rules; + if (Array.isArray(ruleList)) { + for (const child of ruleList) collect(child, targetSet); + } + if (node?.rule) collect(node.rule, targetSet); + } + }; + + if (ruleConfig.type === 'logical') { + if (ruleConfig.never) collect(ruleConfig.never, dependsByLevel.never); + if (ruleConfig.always) collect(ruleConfig.always, dependsByLevel.always); + if (ruleConfig.requires) collect(ruleConfig.requires, dependsByLevel.requires); + if (ruleConfig.when) collect(ruleConfig.when, dependsByLevel.when); + if (ruleConfig.unless) collect(ruleConfig.unless, dependsByLevel.unless); + collectWithKeys(ruleConfig, ['union', 'intersection', 'exclusion'], dependsByLevel.ordinary); + } else { + collect(ruleConfig, dependsOn); + } + + for (const level of Object.keys(dependsByLevel)) { + for (const rel of dependsByLevel[level]) { + dependsOn.add(rel); + } + } + + const orderedDependsOn = Array.from(dependsOn); + if (orderedDependsOn.length > 0) { + ruleConfig.dependsOn = orderedDependsOn; + } + + if (Object.values(dependsByLevel).some(set => set.size > 0)) { + ruleConfig.dependsByLevel = { + never: Array.from(dependsByLevel.never), + always: Array.from(dependsByLevel.always), + requires: Array.from(dependsByLevel.requires), + when: Array.from(dependsByLevel.when), + unless: Array.from(dependsByLevel.unless), + ordinary: Array.from(dependsByLevel.ordinary) + }; + } + + if (orderedDependsOn.length > 0) { + for (const rel of orderedDependsOn) { + let entry = this.dependencyIndex.get(rel); + if (!entry) { + entry = { + all: new Set(), + byLevel: { + never: new Set(), + always: new Set(), + requires: new Set(), + when: new Set(), + unless: new Set(), + ordinary: new Set() + } + }; + this.dependencyIndex.set(rel, entry); + } + entry.all.add(relationName); + if (ruleConfig.dependsByLevel) { + for (const level of Object.keys(entry.byLevel)) { + if (ruleConfig.dependsByLevel[level]?.includes(rel)) { + entry.byLevel[level].add(relationName); + } + } + } + } + } + } + + getDependencyIndex() { + return this.dependencyIndex; + } + + /** + * Build rule configuration from evidence + * @param {EvidenceNode} evidence - Evidence to build config for + * @returns {Object|null} Rule configuration or null + */ + buildRuleConfig(evidence) { + if (!evidence.body || !evidence.body.statements) { + this.errors.push(`Evidence ${evidence.name} has no body`); + return null; + } + + const statements = evidence.body.statements; + + // Handle single statement evidence + if (statements.length === 1) { + return this.buildSingleStatementRule(statements[0]); + } + + // Handle multiple statements with logical operators + return this.buildLogicalRule(statements); + } + + /** + * Build rule configuration for a single statement + * @param {BaseNode} statement - Statement to build rule for + * @returns {Object|null} Rule configuration or null + */ + buildSingleStatementRule(statement) { + switch (statement.type) { + case 'DirectEvidence': + return this.buildDirectRule(statement); + case 'PatternMatch': + return this.buildPatternMatchRule(statement); + case 'DefeasibleLogic': + return this.buildDefeasibleRule(statement); + case 'Fusion': + return this.buildFusionRule(statement); + case 'PredicateCall': + return this.buildPredicateRule(statement); + case 'UnaryExpression': + return this.buildUnaryRule(statement); + case 'BinaryExpression': + // Top-level comparator — emit a relational_comparator rule. RF-24 closure. + return this.buildRuleFromExpressionNode(statement); + case 'Expression': + // Handle expressions that might be predicate calls + if (statement.type === 'PredicateCall') { + return this.buildPredicateRule(statement); + } + return this.buildRuleFromExpressionNode(statement); + default: + this.errors.push(`Unsupported statement type: ${statement.type}`); + return null; + } + } + + /** + * Build rule for unary expression (NOT) + * @param {Object} expression - Unary expression + * @returns {Object|null} Rule configuration or null + */ + buildUnaryRule(expression) { + if (expression.operator !== 'NOT') { + this.errors.push(`Unsupported unary operator: ${expression.operator}`); + return null; + } + + const innerRule = this.buildRuleFromExpression(expression.operand); + if (!innerRule) { + return null; + } + + // NOT x: evaluation will negate the inner rule's possibility (1 - poss) + return { + type: 'logical', + intersection: { + rules: [innerRule], + aggregator: 'min', + negate: true + } + }; + } + + /** + * Build logical rule configuration for multiple statements + * @param {BaseNode[]} statements - Statements to combine + * @returns {Object|null} Rule configuration or null + */ + buildLogicalRule(statements) { + const rules = []; + + statements.forEach(statement => { + const rule = this.buildSingleStatementRule(statement); + if (rule) { + rules.push(rule); + } + }); + + if (rules.length === 0) { + this.errors.push('No valid rules found in evidence body'); + return null; + } + + if (rules.length === 1) { + return rules[0]; + } + + // Combine rules with intersection (AND) logic — REBAC default. + // Use explicit fusion max { ... } in DSL for OR semantics. + return { + type: 'logical', + intersection: { + rules: rules, + aggregator: 'min' + } + }; + } + + /** + * Build direct rule configuration + * @param {DirectEvidenceNode} directEvidence - Direct evidence statement + * @returns {Object|null} Rule configuration or null + */ + buildDirectRule(directEvidence) { + if (!directEvidence.predicate) { + this.errors.push('Direct evidence must have a predicate'); + return null; + } + + const predicate = directEvidence.predicate; + const relation = predicate.name; + + return { + type: 'direct', + relation: relation, + reverse: false + }; + } + + /** + * Build pattern match rule configuration + * @param {PatternMatchNode} patternMatch - Pattern match statement + * @returns {Object|null} Rule configuration or null + */ + buildPatternMatchRule(patternMatch) { + if (!patternMatch.predicate) { + this.errors.push('Pattern match must have a predicate'); + return null; + } + + const predicate = patternMatch.predicate; + const relation = predicate.name; + + // Membership/hierarchy predicates map to TupleToUsersetRule / ParentRule + // regardless of body shape — those have priority over chain detection. + if (this.isMembershipPredicate(predicate)) { + return this.buildTupleToUsersetRule(patternMatch); + } + if (this.isHierarchyPredicate(predicate)) { + return this.buildParentRule(patternMatch); + } + + // Chain detection: ADR-000 ChainRule shape is "works_in(p, *d) { has_access(d, r) }". + // The outer PatternMatch has a Wildcard binding, and its body contains a single + // PredicateCall (no DefeasibleLogic wrapping, no nested PatternMatch). Treat that + // as a chain: two-hop traversal through the wildcard intermediate. RF-24 closure + // (parallel to RF-22/RF-23 — DSL→engine mapping gap surfaced by rigor coverage). + if (this._isChainPattern(patternMatch)) { + return this.buildChainRule(patternMatch); + } + + return this.buildMultiHopRule(patternMatch); + } + + /** + * Detect the ChainRule shape: a PatternMatch whose body contains exactly one + * PredicateCall and uses a Wildcard arg to bind the intermediate. The predicate + * name itself is NOT in the membership/hierarchy lists (those map to TUS/Parent). + */ + _isChainPattern(patternMatch) { + if (!patternMatch.body || !Array.isArray(patternMatch.body.statements)) return false; + const stmts = patternMatch.body.statements; + if (stmts.length !== 1) return false; + if (stmts[0].type !== 'PredicateCall') return false; + const predicate = patternMatch.predicate; + if (!predicate || !Array.isArray(predicate.args)) return false; + // Must use at least one Wildcard (*var) to bind the intermediate + return predicate.args.some(arg => arg && arg.type === 'Wildcard'); + } + + /** + * Build chain rule configuration (ADR-000 ChainRule). + * Compiles "works_in(p, *d) { has_access(d, doc) }" into + * { type: 'chain', steps: ['works_in', 'has_access'] } + * The intermediate wildcard binds the two predicates' arguments. + */ + buildChainRule(patternMatch) { + const steps = []; + steps.push(patternMatch.predicate.name); + + const inner = patternMatch.body.statements[0]; + if (inner && inner.type === 'PredicateCall' && inner.name) { + steps.push(inner.name); + } + + return { + type: 'chain', + steps, + aggregator: 'max', + collectValues: true + }; + } + + /** + * Build tuple-to-userset rule configuration + * @param {PatternMatchNode} patternMatch - Pattern match statement + * @returns {Object|null} Rule configuration or null + */ + buildTupleToUsersetRule(patternMatch) { + const predicate = patternMatch.predicate; + const relation = predicate.name; + + return { + type: 'tuple_to_userset', + tuplesetRelation: 'owner', // Default, could be inferred from context + computedRelation: relation, + reverse: false, + earlyExitThreshold: 0.95, + maxIntermediates: patternMatch.limit || 10 + }; + } + + /** + * Build parent rule configuration + * @param {PatternMatchNode} patternMatch - Pattern match statement + * @returns {Object|null} Rule configuration or null + */ + buildParentRule(patternMatch) { + const predicate = patternMatch.predicate; + const relation = predicate.name; + + return { + type: 'parent', + parentRelation: 'parent', // Default, could be inferred from context + relation: relation, + reverse: false, + aggregator: 'max' + }; + } + + /** + * Build multi-hop rule configuration + * @param {PatternMatchNode} patternMatch - Pattern match statement + * @returns {Object|null} Rule configuration or null + */ + buildMultiHopRule(patternMatch) { + const predicate = patternMatch.predicate; + const relation = predicate.name; + + return { + type: 'multi_hop', + relation: relation, + maxDepth: 3, + pathAggregation: 'max', + reverse: false, + fallbackToBasicPaths: true, + collectValues: false + }; + } + + /** + * Build defeasible rule configuration + * @param {DefeasibleLogicNode} defeasibleLogic - Defeasible logic statement + * @returns {Object|null} Rule configuration or null + */ + buildDefeasibleRule(defeasibleLogic) { + const logicType = defeasibleLogic.logicType; + + if (logicType === 'NEVER') { + return this.buildNeverRule(defeasibleLogic); + } else if (logicType === 'ALWAYS') { + return this.buildStrictRule(defeasibleLogic); + } else if (logicType === 'WHEN') { + return this.buildDefeasibleRuleWithDefeater(defeasibleLogic); + } else if (logicType === 'UNLESS') { + return this.buildDefeaterRule(defeasibleLogic); + } else if (logicType === 'REQUIRES') { + return this.buildRequirementRule(defeasibleLogic); + } + + this.errors.push(`Unsupported defeasible logic type: ${logicType}`); + return null; + } + + /** + * Build NEVER rule configuration (absolute denial) + * @param {DefeasibleLogicNode} defeasibleLogic - Defeasible logic statement + * @returns {Object|null} Rule configuration or null + */ + buildNeverRule(defeasibleLogic) { + const condition = this.buildRuleFromExpression(defeasibleLogic.condition); + + return { + type: 'logical', + never: { + union: { + rules: [condition], + aggregator: 'max' + } + } + }; + } + + /** + * Build strict rule configuration + * @param {DefeasibleLogicNode} defeasibleLogic - Defeasible logic statement + * @returns {Object|null} Rule configuration or null + */ + buildStrictRule(defeasibleLogic) { + const condition = this.buildRuleFromExpression(defeasibleLogic.condition); + + return { + type: 'logical', + always: { + direct: condition, + aggregator: 'min' + } + }; + } + + /** + * Build defeasible rule with defeater + * @param {DefeasibleLogicNode} defeasibleLogic - Defeasible logic statement + * @returns {Object|null} Rule configuration or null + */ + buildDefeasibleRuleWithDefeater(defeasibleLogic) { + const condition = this.buildRuleFromExpression(defeasibleLogic.condition); + const defeater = this.buildRuleFromExpression(defeasibleLogic.defeater); + + const rule = { + type: 'logical', + when: { + intersection: { + rules: [condition], + aggregator: 'min' + } + } + }; + + if (defeater) { + rule.unless = { + union: { + rules: [defeater], + aggregator: 'max' + } + }; + } + + return rule; + } + + /** + * Build defeater rule configuration + * @param {DefeasibleLogicNode} defeasibleLogic - Defeasible logic statement + * @returns {Object|null} Rule configuration or null + */ + buildDefeaterRule(defeasibleLogic) { + const condition = this.buildRuleFromExpression(defeasibleLogic.condition); + + return { + type: 'logical', + unless: { + union: { + rules: [condition], + aggregator: 'max' + } + } + }; + } + + /** + * Build requirement rule configuration + * @param {DefeasibleLogicNode} defeasibleLogic - Defeasible logic statement + * @returns {Object|null} Rule configuration or null + */ + buildRequirementRule(defeasibleLogic) { + const condition = this.buildRuleFromExpression(defeasibleLogic.condition); + + return { + type: 'logical', + requires: { + union: { + rules: [condition], + aggregator: 'min' + } + } + }; + } + + /** + * Build fusion rule configuration + * @param {FusionNode} fusion - Fusion statement + * @returns {Object|null} Rule configuration or null + */ + buildFusionRule(fusion) { + const rules = []; + + (fusion.expressions || fusion.evidence || []).forEach(evidence => { + const rule = this.buildRuleFromExpression(evidence); + if (rule) { + rules.push(rule); + } + }); + + if (rules.length === 0) { + this.errors.push('Fusion has no valid evidence'); + return null; + } + + if (fusion.weights && fusion.strategy !== 'custom') { + this.errors.push(`Fusion weights require custom strategy, got: ${fusion.strategy}`); + return null; + } + + if (fusion.strategy === 'custom') { + if (!fusion.weights || fusion.weights.length === 0) { + this.errors.push('Custom fusion requires weights'); + return null; + } + const total = fusion.weights.reduce((sum, w) => sum + w, 0); + if (Math.abs(total - 1.0) > 1e-6) { + this.errors.push('Custom fusion weights must sum to 1.0'); + return null; + } + } + + const union = { + rules: rules, + aggregator: fusion.strategy + }; + if (fusion.weights && fusion.weights.length > 0) { + union.owaWeights = fusion.weights; + } + + return { + type: 'logical', + union + }; + } + + /** + * Build rule from expression + * @param {BaseNode} expression - Expression to build rule from + * @returns {Object|null} Rule configuration or null + */ + buildRuleFromExpression(expression) { + if (!expression) { + return null; + } + + if (expression.type === 'Predicate') { + return this.buildDirectRuleFromPredicate(expression); + } else if (expression.type === 'Expression') { + return this.buildRuleFromExpressionNode(expression); + } else if (expression.type === 'PredicateCall') { + return this.buildPredicateRule(expression); + } else if (expression.type === 'UnaryExpression') { + return this.buildUnaryRule(expression); + } + + this.errors.push(`Unsupported expression type: ${expression.type}`); + return null; + } + + /** + * Build direct rule from predicate + * @param {PredicateNode} predicate - Predicate to build rule from + * @returns {Object|null} Rule configuration or null + */ + buildDirectRuleFromPredicate(predicate) { + const expanded = this._expandPredicate(predicate.name); + if (expanded) return expanded; + + return { + type: 'direct', + relation: predicate.name, + reverse: false + }; + } + + _expandPredicate(predicateName) { + const existingConfig = this.generatedRules.get(predicateName) || this.arbiter?.relationConfigs?.get(predicateName); + if (!existingConfig) return null; + if (!existingConfig.union && !existingConfig.intersection && !existingConfig.exclusion) return null; + + const logicalKey = existingConfig.union ? 'union' : existingConfig.intersection ? 'intersection' : 'exclusion'; + const subRules = Array.isArray(existingConfig[logicalKey]?.rules) + ? existingConfig[logicalKey].rules + : Array.isArray(existingConfig[logicalKey]) ? existingConfig[logicalKey] : []; + + if (subRules.length === 0) return null; + + const expandedRules = subRules.map(r => { + if (r && r.type === 'direct') return { type: 'direct', relation: r.relation, reverse: !!r.reverse }; + if (typeof r === 'string') return { type: 'direct', relation: r, reverse: false }; + return null; + }).filter(Boolean); + + if (expandedRules.length === 0) return null; + + return { + type: 'logical', + [logicalKey]: { + rules: expandedRules, + aggregator: existingConfig[logicalKey]?.aggregator || 'min' + }, + // Flag to tell the evaluator: this expanded sub-predicate is unary — + // use the subject as the object instead of inheriting the parent's object. + _subjectAsObject: true + }; + } + + /** + * Build rule from expression node + * @param {ExpressionNode} expression - Expression to build rule from + * @returns {Object|null} Rule configuration or null + */ + buildRuleFromExpressionNode(expression) { + if (expression.type === 'AttributeAccess') { + return this.buildAttributeRule(expression); + } else if (expression.type === 'PredicateCall') { + return this.buildPredicateRule(expression); + } else if (expression.type === 'BinaryExpression' && expression.operator === 'within') { + return this.buildWithinRule(expression); + } else if (expression.type === 'BinaryExpression' && this._isComparatorOperator(expression.operator)) { + // ADR-000 RelationalComparatorRule shape: "userRisk(u) <= riskLimit(r)". + // Route BinaryExpression with comparator operators here so the + // evaluator can run a fuzzy interval comparison instead of treating + // them as logical truth values. RF-24 closure. + return this.buildRelationalComparatorRule(expression); + } + + this.errors.push(`Unsupported expression type: ${expression.type}`); + return null; + } + + /** + * Detect comparator operators per ADR-000. These are the operators that map + * to RelationalComparatorRule (vs. the boolean operators like `&&`/`||` which + * keep going through LogicalOperators). + */ + _isComparatorOperator(operator) { + return operator === '>' || operator === '>=' || operator === '<' || + operator === '<=' || operator === '==' || operator === '!='; + } + + /** + * Build relational comparator rule configuration (ADR-000 RelationalComparatorRule). + * Compiles "personAge(p) >= docMinAge(d)" into + * { + * type: 'relational_comparator', + * left: { rule: , extractValue: true }, + * right: { rule: , extractValue: true }, + * comparator: '>=', + * marginOfSafety: 1.0, + * fallbackBehavior: 'deny', + * minRulePossibility: 0 + * } + */ + buildRelationalComparatorRule(binaryExpression) { + const comparator = binaryExpression.operator; + const left = this._buildComparatorOperand(binaryExpression.left); + const right = this._buildComparatorOperand(binaryExpression.right); + if (!left || !right) { + this.errors.push(`Comparator operands must resolve to predicate calls (operator=${comparator})`); + return null; + } + + return { + type: 'relational_comparator', + left, + right, + comparator, + marginOfSafety: 1.0, + fallbackBehavior: 'deny', + minRulePossibility: 0 + }; + } + + /** + * Wrap a BinaryExpression side into a relational_comparator operand. The + * operand's `rule` field is the original predicate call (preserving reference + * semantics so the inner rule's evaluator can resolve its values). `extractValue` + * tells the evaluator to read the relation's `value` field rather than its + * `possibility`, which is what `personAge(p)` / `docMinAge(d)` semantics require. + */ + _buildComparatorOperand(side) { + if (!side) return null; + if (side.type === 'PredicateCall') { + return { + rule: side, + extractValue: true, + evaluatorFrom: 'auto' + }; + } + if (side.type === 'AttributeAccess') { + // user.age — treat the attribute path as a "measure" reference + return { + rule: side, + extractValue: true, + evaluatorFrom: 'auto', + attributePath: side.getAttributePath ? side.getAttributePath() : null + }; + } + return null; + } + + /** + * Build attribute rule configuration + * @param {ExpressionNode} expression - Attribute expression + * @returns {Object|null} Rule configuration or null + */ + buildAttributeRule(expression) { + const attributePath = expression.getAttributePath(); + + return { + type: 'direct', + relation: attributePath, + reverse: false + }; + } + + /** + * Build function rule configuration + * @param {ExpressionNode} expression - Function expression + * @returns {Object|null} Rule configuration or null + */ + buildPredicateRule(expression) { + const predicateName = expression.name; + if (expression.challenge) { + return this.buildChallengeRule(expression, null); + } + + // Expand composite (logical) predicate references into their direct + // leaf components so the optimizer can flatten to a correct direct_list. + const expanded = this._expandPredicate(predicateName); + if (expanded) return expanded; + + return { + type: 'direct', + relation: predicateName, + reverse: false + }; + } + + buildWithinRule(expression) { + const left = expression.left; + const right = expression.right; + if (left && left.type === 'PredicateCall' && left.challenge) { + this.errors.push('within must be applied to a challenge proof field, e.g. !mfa(user).issued_at within 10m'); + return null; + } + if (left && left.type === 'AttributeAccess' && left.object?.type === 'PredicateCall' && left.object?.challenge) { + if (left.attribute !== 'issued_at' && left.attribute !== 'issuedAt') { + this.errors.push('only issued_at is supported for challenge recency checks'); + return null; + } + return this.buildChallengeRule(left.object, right); + } + this.errors.push('within operator currently only supported with challenge predicates'); + return null; + } + + buildChallengeRule(expression, duration) { + const predicateName = expression.name; + const subject = this._resolveChallengeSubject(expression.args || []); + const withinMs = duration ? this._durationToMs(duration) : null; + return { + type: 'challenge', + challenge: predicateName, + subject, + ...(withinMs !== null && { withinMs }) + }; + } + + _resolveChallengeSubject(args) { + if (!args || !args.length) return 'user'; + const first = args[0]; + if (first?.type === 'Variable') { + if (first.name === 'object') return 'object'; + if (first.name === 'session') return 'session'; + return 'user'; + } + return 'user'; + } + + _durationToMs(duration) { + if (!duration || duration.value === undefined) return null; + const raw = typeof duration.value === 'string' ? duration.value : String(duration.value); + const unit = duration.unit || raw.slice(-1); + const numeric = parseFloat(raw); + if (!Number.isFinite(numeric)) return null; + switch (unit) { + case 's': + return numeric * 1000; + case 'm': + return numeric * 60 * 1000; + case 'h': + return numeric * 60 * 60 * 1000; + case 'd': + return numeric * 24 * 60 * 60 * 1000; + case 'w': + return numeric * 7 * 24 * 60 * 60 * 1000; + default: + return null; + } + } + + /** + * Build measure rule configuration + * @param {MeasureNode} measure - Measure to build rule for + * @returns {Object|null} Rule configuration or null + */ + buildMeasureRuleConfig(measure) { + if (!measure.body) { + this.errors.push(`Measure ${measure.name} has no body`); + return null; + } + + // Measures typically generate computed rules + return { + type: 'computed', + relation: measure.name + }; + } + + /** + * Check if predicate is membership-based + * @param {PredicateNode} predicate - Predicate to check + * @returns {boolean} True if membership predicate + */ + isMembershipPredicate(predicate) { + const membershipPredicates = ['member', 'member_of', 'belongs_to', 'is_member']; + return membershipPredicates.includes(predicate.name.toLowerCase()); + } + + /** + * Check if predicate is hierarchy-based + * @param {PredicateNode} predicate - Predicate to check + * @returns {boolean} True if hierarchy predicate + */ + isHierarchyPredicate(predicate) { + const hierarchyPredicates = ['parent', 'parent_of', 'child', 'child_of', 'ancestor', 'descendant']; + return hierarchyPredicates.includes(predicate.name.toLowerCase()); + } + + /** + * Apply generated rules to arbiter + */ + applyRulesToArbiter() { + if (!this.arbiter || typeof this.arbiter.setRelationConfig !== 'function') { + // Skip applying rules if arbiter is not available or doesn't support setRelationConfig + return; + } + + this.generatedRules.forEach((config, relation) => { + try { + this.arbiter.setRelationConfig(relation, config); + } catch (error) { + this.errors.push(`Failed to set relation config for ${relation}: ${error.message}`); + } + }); + + if (typeof this.arbiter.registerDependencyIndex === 'function') { + this.arbiter.registerDependencyIndex(this.dependencyIndex); + } + } + + /** + * Get generated rules + * @returns {Map} Map of generated rules + */ + getGeneratedRules() { + return this.generatedRules; + } + + /** + * Get generation errors + * @returns {string[]} Array of error messages + */ + getErrors() { + return this.errors; + } +} diff --git a/src/grammar/dsl.peggy b/src/grammar/dsl.peggy new file mode 100644 index 0000000..8bf560a --- /dev/null +++ b/src/grammar/dsl.peggy @@ -0,0 +1,420 @@ +/* + * Peggy Parser for the Evidence DSL + * + * This grammar defines a declarative language for authorization policies. + * It parses definitions, facts, measures, and evidence rules into a structured + * Abstract Syntax Tree (AST) represented by plain JavaScript objects. + * (Version 4: Corrected infinite loop check in String literal parsing) + */ +{ + // The location() function provides line/column info for error reporting. + // The text() function returns the matched text for a rule. + + // Helper function to build a left-associative binary expression tree. + function buildLeftAssoc(head, tail) { + return tail.reduce((result, element) => { + return { + type: "BinaryExpression", + operator: element[1], + left: result, + right: element[3], + location: location() + }; + }, head); + } +} + +// -- Grammar Entry Point -- +Program + = _ statements:(Statement _)* _ { + const allStatements = statements.map(s => s[0]); + return { + type: "Program", + body: allStatements, + definitions: allStatements.filter(s => s.type === "Definition"), + facts: allStatements.filter(s => s.type === "Fact"), + evidence: allStatements.filter(s => s.type === "Evidence"), + measures: allStatements.filter(s => s.type === "Measure"), + sources: allStatements.filter(s => s.type === "Source") + }; + } + +Statement + = Definition + / Source + / Fact + / Evidence + / Measure + +// -- Top-Level Statements -- + +Definition "A type definition" + = ("definition" / "type") __ name:Identifier __ "{" _ fields:(Field _)* "}" { + return { type: "Definition", name, fields: fields.map(f => f[0]) }; + } + +Field + = name:Identifier _ ":" _ fieldType:Type _ isArray:("[]")? _ behavior:Behavior? _ cache:CacheDirective? { + return { + type: "Field", + name, + fieldType, + isArray: !!isArray, + behavior: behavior || null, + cache: cache || null + }; + } + +Fact "A statement of fact (or relation in ADR-000)" + = ("fact" / "relation") __ star:"*"? name:Identifier _ "(" _ params:ParameterList? _ ")" _ behavior:BehaviorAnnotation? _ properties:(FactProperty _)* cache:CacheDirective? _ limit:Limit? { + return { + type: "Fact", + name, + params: params || [], + behavior: behavior || null, + properties: properties.map(p => p[0]), + cache: cache || null, + limit: limit || null, + injectable: !!star + }; + } + +Source "An injectable source (proof provider)" + = "source" __ star:"*"? name:Identifier _ "(" _ params:ParameterList? _ ")" _ provides:Provides? _ within:WithinClause? { + return { + type: "Source", + name, + params: params || [], + provides: provides || null, + injectable: !!star, + within: within || null + }; + } + +WithinClause "A freshness constraint on a source" + = "within" __ duration:Duration { return duration; } + +Evidence "An evidence rule" + = "evidence" __ star:"*"? name:Identifier _ "(" _ params:ParameterList? _ ")" _ limit:Limit? _ "{" _ body:EvidenceBody _ "}" _ provides:Provides? { + return { + type: "Evidence", + name, + params: params || [], + limit: limit || null, + body, + provides: provides || null, + challenge: !!star + }; + } + +Measure "A derived measurement or value" + = "measure" __ name:Identifier _ "(" _ params:ParameterList? _ ")" _ "{" _ body:MeasureBody _ "}" _ provides:Provides? { + return { + type: "Measure", + name, + params: params || [], + body, + provides: provides || null + }; + } + +// -- Evidence & Measure Internals -- + +EvidenceBody + = statements:(EvidenceStatement _)* { + return { type: "EvidenceBody", statements: statements.map(s => s[0]) }; + } + +EvidenceStatement + = DefeasibleLogic + / Fusion + / CollectionProcessing + / PatternMatch + / Expression + +MeasureBody + = statements:(MeasureStatement _)* returnStmt:ReturnStatement? { + return { + type: "MeasureBody", + statements: statements.map(s => s[0]), + returnStatement: returnStmt || null + }; + } + +MeasureStatement + = Fusion + / Aggregation + / PatternMatch + / Expression + +ReturnStatement + = "return" __ expression:Expression { + return { type: "ReturnStatement", expression }; + } + +// -- Complex Statement Types -- + +DefeasibleLogic + = type:("NEVER" / "ALWAYS" / "REQUIRES") __ condition:Expression { + return { type: "DefeasibleLogic", logicType: type, condition }; + } + / "WHEN" __ condition:Expression __ "UNLESS" __ defeater:Expression { + return { type: "DefeasibleLogic", logicType: "WHEN", condition, defeater }; + } + / "WHEN" __ condition:Expression { + return { type: "DefeasibleLogic", logicType: "WHEN", condition }; + } + +PatternMatch + = predicate:PatternPredicate _ binding:BindingClause? _ "{" _ body:EvidenceBody _ "}" _ limit:Limit? _ withClause:WithClause? { + return { + type: "PatternMatch", + predicate, + binding: binding || null, + limit: limit || null, + body, + withClause: withClause || null + }; + } + +CollectionProcessing + = measure:Expression _ "|" _ variable:Identifier _ "|" _ fusionStrategy:("fusion" __ strategy:Identifier)? _ "{" _ body:EvidenceBody _ "}" _ limit:Limit? { + return { + type: "CollectionProcessing", + measure, + variable, + fusion: fusionStrategy ? { strategy: fusionStrategy[1] } : null, + body, + limit: limit || null + }; + } + +PatternPredicate + = name:Identifier _ "(" _ args:PatternArgumentList? _ ")" { + return { type: "Predicate", name, args: args || [] }; + } + +PatternArgumentList + = head:PatternArgument tail:(_ "," _ arg:PatternArgument)* { + return [head, ...tail.map(t => t[3])]; + } + +PatternArgument + = "*" _ name:Identifier { return { type: "Wildcard", name }; } + / Expression + +BindingClause + = "|" _ name:Identifier _ "|" { return name; } + +WithClause + = "with" __ condition:Expression { return condition; } + +Fusion + = "fusion" __ strategy:Identifier __ "{" _ expressions:ExpressionList _ "}" { + return { type: "Fusion", strategy, expressions }; + } + +Aggregation + = "aggregate" __ "{" _ expressions:ExpressionList _ "}" _ using:Using? { + return { type: "Aggregation", expressions, using: using || null }; + } + +Using + = "USING" __ method:Identifier { return method; } + +// -- Type System & Parameters -- + +Type + = Identifier + +TypeName + = name:Identifier { return { type: "TypeName", name }; } + / literal:String { return { type: "TypeName", name: literal.value }; } + +ParameterList + = head:Parameter tail:(_ "," _ param:Parameter)* { + return [head, ...tail.map(t => t[3])]; + } + +Parameter + = name:Identifier _ ":" _ paramType:Type _ isArray:("[]")? { + return { type: "Parameter", name, paramType, isArray: !!isArray }; + } + +Provides + = "PROVIDES" __ providesType:Type { return providesType; } + +BehaviorAnnotation + = "BEHAVES" __ "AS" __ behavior:("edge" / "transitive" / "hierarchical" / "symmetrical_graph") { + return { type: "BehaviorAnnotation", behavior }; + } + +FactProperty + = "transitive" { return "transitive"; } + / "symmetrical" { return "symmetrical"; } + +Limit + = "limit" __ value:Integer { return value; } + +// -- Behaviors and Caching -- + +Behavior + = "BEHAVES" __ "{" _ b:(DecayBehavior / BlurBehavior / TTLBehavior) _ "}" { return b; } + +DecayBehavior + = "decaying" __ direction:("up" / "down" / "neutral" / "stable") __ period:("hourly" / "daily" / "weekly" / "monthly") { + return { type: "Behavior", behaviorType: "decay", direction, period }; + } + +BlurBehavior + = "blurring" __ mode:("fixed" / "adaptive" / "confidence") confidence:(__ ("confidence_90" / "confidence_95" / "confidence_99"))? { + return { type: "Behavior", behaviorType: "blur", mode, confidence: confidence ? confidence[1] : null }; + } + +TTLBehavior + = "ttl" __ duration:Duration { + return { type: "Behavior", behaviorType: "ttl", duration }; + } + +CacheDirective + = "CACHE" __ directive:("eager" / "lazy") { return directive; } + +// -- Expressions (with operator precedence) -- + +Expression + = LogicalOr + +LogicalOr + = head:LogicalAnd tail:(_ "||" _ right:LogicalAnd)* { return buildLeftAssoc(head, tail); } + +LogicalAnd + = head:Comparison tail:(_ "&&" _ right:Comparison)* { return buildLeftAssoc(head, tail); } + +Comparison + = head:TemporalComparison _ "is" __ typeName:TypeName { + return { type: "BinaryExpression", operator: "is", left: head, right: typeName }; + } + / head:TemporalComparison tail:(_ operator:("==" / "!=" / ">=" / "<=" / ">" / "<") _ right:TemporalComparison)* { return buildLeftAssoc(head, tail); } + +TemporalComparison + = head:Addition _ "within" __ right:Duration { + return { type: "BinaryExpression", operator: "within", left: head, right }; + } + / Addition + +Addition + = head:Multiplication tail:(_i operator:("+" / "-") _i right:Multiplication)* { return buildLeftAssoc(head, tail); } + +Multiplication + = head:Unary tail:(_i operator:("*" / "/") _i right:Unary)* { return buildLeftAssoc(head, tail); } + +Unary + = operator:("NOT" / "!") __ operand:Unary { return { type: "UnaryExpression", operator: "NOT", operand }; } + / Postfix + +Postfix + = primary:(AttributeAccess / PrimaryTerm) binding:BindingClause? { + if (binding) { + return { type: "BindingAccess", expression: primary, binding }; + } + return primary; + } + +AttributeAccess + = head:PrimaryTerm tail:(_ "." _ attr:Identifier)+ { + return tail.reduce((obj, part) => { + return { + type: "AttributeAccess", + object: obj, + attribute: part[3], // The Identifier is the 4th element (index 3) + location: location() + }; + }, head); + } + +PrimaryTerm "The non-recursive base for an expression chain" + = ChallengePredicate + / Literal + / PredicateCall + / Variable + / "(" _ expr:Expression _ ")" { return expr; } + +ChallengePredicate + = "*" name:Identifier _ "(" _ args:ArgumentList? _ ")" { + return { type: "PredicateCall", name, args: args || [], challenge: true }; + } + +PredicateCall + = name:Identifier "(" _ args:ArgumentList? _ ")" { + return { type: "PredicateCall", name, args: args || [] }; + } + +Variable + = name:Identifier { return { type: "Variable", name }; } + +ArgumentList + = head:Expression tail:(_ "," _ expr:Expression)* { + return [head, ...tail.map(t => t[3])]; + } + +ExpressionList + = head:Expression tail:(_ "," _ expr:Expression)* { + return [head, ...tail.map(t => t[3])]; + } + +// -- Literals -- + +Literal + = String / Float / Integer / Boolean / Duration + +String "A string literal" + = '"' chars:((!("\"" / "\\")) . / "\\" .)* '"' { + return { type: "Literal", value: JSON.parse(text()) }; + } + / "'" chars:((!("'" / "\\")) . / "\\" .)* "'" { + return { type: "Literal", value: JSON.parse("\"" + chars.map(c => c[0] === '\\' ? c[1] : c[1]).join('') + "\"") }; + } + +Float "A floating-point number" + = value:([0-9]+ "." [0-9]+) { return { type: "Literal", value: parseFloat(text()) }; } + +Integer "An integer" + = value:[0-9]+ { return { type: "Literal", value: parseInt(text(), 10) }; } + +Boolean "A boolean literal" + = value:("true" / "false") { return { type: "Literal", value: value === "true" }; } + +Duration "A time duration literal" + = value:([0-9]+ ("h" / "d" / "w" / "m")) { return { type: "Literal", value: text(), unit: text().slice(-1) }; } + + +// -- Core Tokens & Whitespace -- + +Identifier + = !Keyword name:$([a-zA-Z_][a-zA-Z0-9_]*) { return name; } + +Keyword + = ("definition" / "type" / "fact" / "relation" / "evidence" / "measure" / "BEHAVES" / "AS" / "CACHE" + / "decaying" / "blurring" / "ttl" / "transitive" / "symmetrical" / "hierarchical" / "symmetrical_graph" / "edge" / "limit" + / "PROVIDES" / "fusion" / "aggregate" / "USING" / "NEVER" / "ALWAYS" / "WHEN" / "UNLESS" + / "REQUIRES" / "with" / "true" / "false" / "NOT" / "within" / "return" / "is") !([a-zA-Z0-9_]) + +// _ = optional whitespace and comments +// __ = mandatory whitespace and comments +_ + = (WhiteSpace / Comment)* + +// Inline (single-line) optional whitespace — used around arithmetic +// operators so a `*` challenge-predicate on the next line is not +// absorbed as a multiplication tail. +_i + = [ \t]* +__ + = (WhiteSpace / Comment)+ + +WhiteSpace + = [ \t\r\n] + +Comment + = "//" [^\r\n]* + / "/*" (!"*/" .)* "*/" diff --git a/src/grammar/expression.peggy b/src/grammar/expression.peggy new file mode 100644 index 0000000..816987b --- /dev/null +++ b/src/grammar/expression.peggy @@ -0,0 +1,167 @@ +// Inline Expression Grammar for Permission Checking +// +// This grammar parses inline DSL expressions used for permission checks. +// It supports predicate calls, OWA Fusion blocks (exclusive composition), +// challenge predicates (* prefix for out-of-band), and defeasible logic (UNLESS). +// AND/OR operators removed — OWA Fusion is the only composition mechanism. +// +// Usage: npx peggy -o src/ast/parser/ExpressionParser.js src/ast/grammar/expression.peggy + +{ + // Helper functions + function makeVariable(name, path) { + return { type: 'Variable', name, path: path || [] }; + } + + function makePredicate(name, args) { + return { type: 'Predicate', name, args: args || [] }; + } + + function makeChallengePredicate(name, args) { + return { type: 'Predicate', name, args: args || [], challenge: true }; + } + + function makeFusion(expressions, aggregator) { + return { type: 'Fusion', aggregator, expressions }; + } + + function makeDefeasible(primary, exception) { + return { type: 'Defeasible', primary, exception }; + } +} + +// Entry point +Expression + = _ expr:DefeasibleExpr _ { return expr; } + +// Defeasible logic: primary UNLESS exception +DefeasibleExpr + = primary:PrimaryExpr _ "UNLESS" _ exception:PredicateCall { + return makeDefeasible(primary, exception); + } + / PrimaryExpr + +// Primary expressions: Fusion blocks or predicate calls +PrimaryExpr + = FusionBlock + / ChallengePredicate + / PredicateCall + +// OWA Fusion block: FUSION { expr1 expr2 ... } +FusionBlock + = "FUSION" _ aggregator:AggregatorKeyword _ "{" _ expressions:ExpressionList _ "}" { + return makeFusion(expressions, aggregator); + } + +// Aggregator keywords (subset of ADR-000 DSL v2 aggregators) +AggregatorKeyword + = "max" / "min" / "majority" / "average" / "sum" / "sum_unbounded" + / "median" / "optimistic" / "pessimistic" / "top2" / "top3" / "priority" + +// List of expressions (whitespace-separated) +ExpressionList + = head:Expression tail:(_ Expression)* { + const exprs = [head]; + for (const t of tail) { + exprs.push(t[1]); + } + return exprs; + } + +// Challenge predicate (* prefixed): *name(arg1, arg2, ...) +ChallengePredicate + = "*" name:Identifier _ "(" _ args:ArgumentList? _ ")" { + return makeChallengePredicate(name, args || []); + } + +// Predicate call: name(arg1, arg2, ...) +PredicateCall + = name:Identifier _ "(" _ args:ArgumentList? _ ")" { + return makePredicate(name, args || []); + } + +// Comma-separated arguments +ArgumentList + = head:Argument tail:(_ "," _ Argument)* { + const args = [head]; + for (const t of tail) { + args.push(t[3]); + } + return args; + } + +// Argument types +// Order matters: try VariableBinding first (starts with :), +// then Literal (strings/numbers), then TypedReference (which looks like an identifier) +Argument + = VariableBinding + / Literal + / TypedReference + +// Variable binding: :name or :name.path.subpath +VariableBinding + = ":" name:Identifier path:("." Identifier)* { + return makeVariable(name, path.map(p => p[1])); + } + +// Typed reference: Type::path.subpath (e.g., document::params.id) +TypedReference + = refType:Identifier "::" path:Path { + return { type: 'Reference', refType: refType, path: path }; + } + +// Path for typed references +Path + = head:Identifier tail:("." Identifier)* { + const parts = [head]; + for (const t of tail) { + parts.push(t[1]); + } + return parts; + } + +// Literals +Literal + = StringLiteral + / NumberLiteral + +// String literals (single or double quoted) +StringLiteral + = '"' chars:([^"\\] / EscapeSequence)* '"' { + return { type: 'Literal', value: chars.join(''), dataType: 'string' }; + } + / "'" chars:([^'\\] / EscapeSequence)* "'" { + return { type: 'Literal', value: chars.join(''), dataType: 'string' }; + } + +// Escape sequences +EscapeSequence + = "\\" char:["'\\nrt] { + const escapes = { '"': '"', "'": "'", '\\': '\\', 'n': '\n', 'r': '\r', 't': '\t' }; + return escapes[char] || char; + } + +// Number literals +NumberLiteral + = digits:([0-9]+) { + return { type: 'Literal', value: parseInt(digits.join(''), 10), dataType: 'number' }; + } + +// Identifiers (support hyphens like doc-123, user-456) +Identifier + = first:[a-zA-Z_] rest:[a-zA-Z0-9_-]* { + return first + rest.join(''); + } + +// Whitespace and comments +_ "whitespace" + = (WS / LineComment / BlockComment)* + +WS + = [ \t\n\r]+ + +LineComment + = "//" [^\n]* + +BlockComment + = "/*" (!"*/" .)* "*/" diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..5bc0568 --- /dev/null +++ b/src/index.js @@ -0,0 +1,22 @@ +/** + * AST Module - Main export file + * Provides access to all AST functionality for DSL compilation + */ + +// Core AST components +export { DSLCompiler } from './DSLCompiler.js'; + +// Parser +export { PeggyDSLParser } from './parser/PeggyDSLParser.js'; + +// Generator +export { RuleGenerator } from './generator/RuleGenerator.js'; + +// Validation +export { validateDslText } from './validation/DSLValidation.js'; + +// All AST nodes +export * from './nodes/index.js'; + +// Re-export for convenience +export { DSLCompiler as default } from './DSLCompiler.js'; diff --git a/src/interpreter/BuiltInFunctions.js b/src/interpreter/BuiltInFunctions.js new file mode 100644 index 0000000..49ff9e6 --- /dev/null +++ b/src/interpreter/BuiltInFunctions.js @@ -0,0 +1,182 @@ +/** + * Built-in DSL Functions + * + * Native functions for use in DSL expressions + * Includes IP operations, time functions, string utilities + */ + +// Use optimized fast versions for hot paths +import { + isIpInCidrFast, + isPrivateIpFast, + ipToIntFast, + isIPv4Fast +} from '../utils/ip-utils-fast.js'; +import { + isIPv6, + isLoopbackIp, + ipEquals, + getIpVersion +} from '../utils/ip-utils.js'; + +/** + * Registry of built-in functions + */ +export const BUILT_IN_FUNCTIONS = { + // IP Address Functions - Using optimized fast versions + ip_in_cidr: { + params: ['ip', 'cidr'], + evaluate: (ip, cidr) => { + if (!ip || !cidr) return false; + return isIpInCidrFast(String(ip), String(cidr)); + } + }, + + ip_equals: { + params: ['ip1', 'ip2'], + evaluate: (ip1, ip2) => { + return ipEquals(String(ip1), String(ip2)); + } + }, + + ip_version: { + params: ['ip'], + evaluate: (ip) => { + return getIpVersion(String(ip)); + } + }, + + ip_is_private: { + params: ['ip'], + evaluate: (ip) => { + if (!ip) return false; + return isPrivateIpFast(String(ip)); + } + }, + + ip_is_loopback: { + params: ['ip'], + evaluate: (ip) => { + if (!ip) return false; + // Fast check: 127.x.x.x + return ipToIntFast(String(ip)) >>> 24 === 127; + } + }, + + ip_is_v4: { + params: ['ip'], + evaluate: (ip) => { + if (!ip) return false; + return isIPv4Fast(String(ip)); + } + }, + + ip_is_v6: { + params: ['ip'], + evaluate: (ip) => { + if (!ip) return false; + return isIPv6(String(ip)); + } + }, + + // Time Functions + hour_of_day: { + params: ['timestamp'], + evaluate: (timestamp) => { + const ts = typeof timestamp === 'number' ? timestamp : Date.now(); + return new Date(ts).getHours(); + } + }, + + day_of_week: { + params: ['timestamp'], + evaluate: (timestamp) => { + const ts = typeof timestamp === 'number' ? timestamp : Date.now(); + return new Date(ts).getDay(); // 0 = Sunday + } + }, + + // String Functions + contains: { + params: ['string', 'substring'], + evaluate: (str, substr) => { + if (!str || !substr) return false; + return String(str).includes(String(substr)); + } + }, + + starts_with: { + params: ['string', 'prefix'], + evaluate: (str, prefix) => { + if (!str || !prefix) return false; + return String(str).startsWith(String(prefix)); + } + }, + + ends_with: { + params: ['string', 'suffix'], + evaluate: (str, suffix) => { + if (!str || !suffix) return false; + return String(str).endsWith(String(suffix)); + } + }, + + // Comparison Functions + equals: { + params: ['a', 'b'], + evaluate: (a, b) => a === b + }, + + greater_than: { + params: ['a', 'b'], + evaluate: (a, b) => a > b + }, + + less_than: { + params: ['a', 'b'], + evaluate: (a, b) => a < b + }, + + in_range: { + params: ['value', 'min', 'max'], + evaluate: (value, min, max) => value >= min && value <= max + } +}; + +/** + * Check if a function name is a built-in + */ +export function isBuiltInFunction(name) { + return name in BUILT_IN_FUNCTIONS; +} + +/** + * Evaluate a built-in function + */ +export function evaluateBuiltIn(name, args) { + const func = BUILT_IN_FUNCTIONS[name]; + if (!func) { + throw new Error(`Unknown built-in function: ${name}`); + } + + if (args.length !== func.params.length) { + throw new Error( + `Function ${name} expects ${func.params.length} arguments, got ${args.length}` + ); + } + + return func.evaluate(...args); +} + +/** + * Get function signature + */ +export function getFunctionSignature(name) { + const func = BUILT_IN_FUNCTIONS[name]; + if (!func) return null; + + return { + name, + params: func.params + }; +} diff --git a/src/interpreter/ExpressionInterpreter.js b/src/interpreter/ExpressionInterpreter.js new file mode 100644 index 0000000..8581bda --- /dev/null +++ b/src/interpreter/ExpressionInterpreter.js @@ -0,0 +1,323 @@ +/** + * Expression Interpreter + * + * Interprets inline DSL expressions by evaluating them against existing + * compiled DSL rules in the graph. No temporary rules are created. + */ + +import { PredicateResolver } from './PredicateResolver.js'; + +export class ExpressionInterpreter { + constructor(context, options = {}) { + this.context = context; + this.graphStores = context.graphStores; + this.resolver = new PredicateResolver(context); + this.customBuiltIns = options.customBuiltIns || {}; + } + + /** + * Interpret an expression AST against the graph + * + * @param {Object} ast - Parsed expression AST + * @param {Object} bindings - Variable bindings + * @returns {Object} Interpretation result + */ + async interpret(ast, bindings) { + switch (ast.type) { + case 'Fusion': + return this.interpretFusion(ast, bindings); + case 'Or': + return this.interpretOr(ast, bindings); + case 'And': + return this.interpretAnd(ast, bindings); + case 'Not': + return this.interpretNot(ast, bindings); + case 'Defeasible': + return this.interpretDefeasible(ast, bindings); + case 'Predicate': + return this.interpretPredicate(ast, bindings); + default: + throw new Error(`Unknown AST node type: ${ast.type}`); + } + } + + /** + * Interpret FUSION block - ALL expressions must be true + * Uses OWA semantics: minimum possibility across all expressions + */ + async interpretFusion(ast, bindings) { + const results = await Promise.all( + ast.expressions.map(expr => this.interpret(expr, bindings)) + ); + + const allAllowed = results.every(r => r.allowed); + const minPossibility = results.length > 0 + ? Math.min(...results.map(r => r.possibility || 0)) + : 0; + + return { + allowed: allAllowed, + possibility: minPossibility, + type: 'Fusion', + details: results + }; + } + + /** + * Interpret OR - ANY expression can be true (short-circuited) + */ + async interpretOr(ast, bindings) { + for (const operand of ast.operands) { + const result = await this.interpret(operand, bindings); + if (result.allowed) { + return { + allowed: true, + possibility: result.possibility, + type: 'Or', + satisfiedBy: operand + }; + } + } + + return { + allowed: false, + possibility: 0, + type: 'Or' + }; + } + + /** + * Interpret AND - ALL expressions must be true + */ + async interpretAnd(ast, bindings) { + const results = []; + let minPossibility = 1; + + for (const operand of ast.operands) { + const result = await this.interpret(operand, bindings); + results.push(result); + minPossibility = Math.min(minPossibility, result.possibility || 0); + + if (!result.allowed) { + return { + allowed: false, + possibility: 0, + type: 'And', + failedAt: operand, + details: results + }; + } + } + + return { + allowed: true, + possibility: minPossibility, + type: 'And', + details: results + }; + } + + /** + * Interpret NOT - negate the operand result + */ + async interpretNot(ast, bindings) { + const result = await this.interpret(ast.operand, bindings); + + return { + allowed: !result.allowed, + possibility: result.allowed ? 0 : 1, + type: 'Not', + inner: result + }; + } + + /** + * Interpret Defeasible - primary UNLESS exception + * If exception is true, primary is defeated + */ + async interpretDefeasible(ast, bindings) { + // Check exception first (short-circuit if possible) + const exceptionResult = await this.interpret(ast.exception, bindings); + + if (exceptionResult.allowed) { + return { + allowed: false, + possibility: 0, + type: 'Defeasible', + reason: 'Defeated by exception', + defeatedBy: exceptionResult + }; + } + + // Exception is false, evaluate primary + const primaryResult = await this.interpret(ast.primary, bindings); + + return { + ...primaryResult, + type: 'Defeasible', + primary: primaryResult, + exception: exceptionResult + }; + } + + /** + * Interpret Predicate - call existing DSL rule via graph.check() + * OR evaluate built-in function + * + * This is where we use the ALREADY COMPILED DSL rules. + * We do NOT create temporary rules. + */ + async interpretPredicate(ast, bindings) { + // Check for built-in functions first (ip_in_cidr, etc.) + const { isBuiltInFunction, evaluateBuiltIn } = await import('./BuiltInFunctions.js'); + const resolvedArgs = ast.args.map(arg => this.resolveArgument(arg, bindings)); + + // Check custom built-ins first (e.g., PriceOps predicates) + if (this.customBuiltIns[ast.name]) { + const result = await this.customBuiltIns[ast.name](...resolvedArgs); + return { + allowed: result === true || result === 1, + possibility: result === true || result === 1 ? 1 : 0, + type: 'BuiltInFunction', + function: ast.name, + args: resolvedArgs, + result + }; + } + + if (isBuiltInFunction(ast.name)) { + const result = evaluateBuiltIn(ast.name, resolvedArgs); + + return { + allowed: result === true || result === 1, + possibility: result === true || result === 1 ? 1 : 0, + type: 'BuiltInFunction', + function: ast.name, + args: resolvedArgs, + result + }; + } + + // Resolve predicate to existing rule + const rule = this.resolver.resolve(ast.name); + if (!rule) { + throw new Error(`Unknown predicate: ${ast.name} - must be defined in compiled DSL`); + } + + // Extract subject (user) and optional object + const subject = resolvedArgs[0]; // First arg is always the subject + // For single-argument predicates, use subject as object to avoid "missing_node" errors + const object = resolvedArgs[1] || subject; // Second arg is optional object + + // Get the appropriate graph store + const graphStore = this.context.getGraphStore + ? this.context.getGraphStore(rule.scope, { + tenantId: bindings.tenant, + applicationId: bindings.applicationId + }) + : this.graphStores[rule.scope]; + if (!graphStore) { + throw new Error(`Graph store not found for scope: ${rule.scope}`); + } + + // Execute check using EXISTING compiled rule + // The graphStore.check() will use the pre-compiled DSL rule config + const checkOptions = {}; + if (bindings.partialGraph) { + checkOptions.partialGraph = bindings.partialGraph; + } + + const result = graphStore.check(subject, ast.name, object, checkOptions); + + return { + allowed: result?.allowed || result?.possibility === 1, + possibility: result?.possibility || 0, + type: 'Predicate', + predicate: ast.name, + scope: rule.scope, + subject, + object, + rawResult: result + }; + } + + /** + * Resolve an argument to its actual value + */ + resolveArgument(arg, bindings) { + switch (arg.type) { + case 'Variable': + return this.resolveVariable(arg, bindings); + case 'Reference': + return this.resolveReference(arg, bindings); + case 'Literal': + return arg.value; + default: + throw new Error(`Unknown argument type: ${arg.type}`); + } + } + + /** + * Resolve a variable binding + * :user -> bindings.user + * :params.id -> bindings.params.id + */ + resolveVariable(variable, bindings) { + let value = bindings[variable.name]; + + // Handle nested paths: :params.id + if (variable.path && variable.path.length > 0) { + for (const key of variable.path) { + if (value === undefined || value === null) { + return undefined; + } + value = value[key]; + } + } + + return value; + } + + /** + * Resolve a typed reference + * document::params.id -> bindings.params.id with type info + */ + resolveReference(ref, bindings) { + // Typed references like document::params.id + // The type (document) is metadata, the value comes from the path + let value = bindings; + + for (const key of ref.path) { + if (value === undefined || value === null) { + return undefined; + } + value = value[key]; + } + + return value; + } +} + +/** + * Utility to collect all predicates from an AST + * Used for validation before interpretation + */ +export function collectPredicates(ast, predicates = []) { + if (ast.type === 'Predicate') { + predicates.push(ast); + } + + // Recursively collect from child nodes + const childKeys = ['expressions', 'operands', 'operand', 'primary', 'exception', 'inner']; + for (const key of childKeys) { + if (ast[key]) { + if (Array.isArray(ast[key])) { + ast[key].forEach(child => collectPredicates(child, predicates)); + } else { + collectPredicates(ast[key], predicates); + } + } + } + + return predicates; +} diff --git a/src/interpreter/PredicateResolver.js b/src/interpreter/PredicateResolver.js new file mode 100644 index 0000000..c1f74d7 --- /dev/null +++ b/src/interpreter/PredicateResolver.js @@ -0,0 +1,167 @@ +/** + * Predicate Resolver + * + * Maps predicate names to existing compiled DSL rules across all graph scopes. + * Does NOT create new rules - only looks up existing ones. + */ + +export class PredicateResolver { + constructor(context) { + this.context = context; + this.graphStores = context.graphStores || {}; + this.cache = new Map(); + } + + /** + * Resolve a predicate name to its DSL rule + * + * @param {string} predicateName - Name of the predicate + * @returns {Object|null} Rule info or null if not found + */ + resolve(predicateName) { + // Check cache first + if (this.cache.has(predicateName)) { + return this.cache.get(predicateName); + } + + // Look up in all graph scopes + const rule = this.findRule(predicateName); + + if (rule) { + this.cache.set(predicateName, rule); + } + + return rule; + } + + /** + * Find a rule across all graph scopes + * Prefers logical rules over direct rules for evidence predicates + */ + findRule(predicateName) { + const scopes = [ + 'tenantExternal', + 'tenantInternal', + 'rootExternal', + 'rootInternal', + 'masterExternal', + 'masterInternal' + ]; + + let directRule = null; + let directScope = null; + + for (const scopeName of scopes) { + const graphStore = this.graphStores[scopeName]; + if (!graphStore) { + continue; + } + + // Handle both: Arbiter directly (has .check()) or wrapper with .arbiter + const arbiter = graphStore.arbiter || graphStore; + const config = arbiter.relationConfigs?.get(predicateName); + + if (config) { + // Prefer logical rules (intersection/union) over direct rules + // This ensures evidence rules work correctly across all scopes + if (config.type === 'intersection' || config.type === 'union' || config.type === 'logical') { + return { + name: predicateName, + scope: scopeName, + config: config, + arity: this.inferArity(config) + }; + } + + // Remember the first direct rule as fallback + if (!directRule && config.type === 'direct') { + directRule = config; + directScope = scopeName; + } + } + } + + // Return direct rule if no logical rule found + if (directRule) { + return { + name: predicateName, + scope: directScope, + config: directRule, + arity: this.inferArity(directRule) + }; + } + + return null; + } + + /** + * Infer the arity (parameter count) from rule config + */ + inferArity(config) { + // Most DSL evidence rules have 1 or 2 parameters: + // - 1 param: just the subject (user) + // - 2 params: subject (user) + object + + if (config.arity) { + return config.arity; + } + + // Default to checking if it's a relation that typically needs an object + // This is a heuristic - in practice, the DSL defines this explicitly + if (config.type === 'tuple_to_userset' || config.type === 'direct') { + return 2; // Likely needs subject + object + } + + return 1; // Default to 1 param + } + + /** + * Check if a predicate exists without full resolution + */ + exists(predicateName) { + return this.resolve(predicateName) !== null; + } + + /** + * Get all available predicates across all scopes + */ + getAllPredicates() { + const predicates = []; + const scopes = [ + 'tenantExternal', + 'tenantInternal', + 'rootExternal', + 'rootInternal', + 'masterExternal', + 'masterInternal' + ]; + + for (const scopeName of scopes) { + const graphStore = this.graphStores[scopeName]; + if (!graphStore) { + continue; + } + + // Handle both: Arbiter directly (has .check()) or wrapper with .arbiter + const arbiter = graphStore.arbiter || graphStore; + if (arbiter.relationConfigs) { + for (const [name, config] of arbiter.relationConfigs) { + predicates.push({ + name, + scope: scopeName, + arity: this.inferArity(config) + }); + } + } + } + + return predicates; + } + + /** + * Clear the cache (useful for testing or when rules change) + */ + clearCache() { + this.cache.clear(); + } +} diff --git a/src/nodes/AggregationNode.js b/src/nodes/AggregationNode.js new file mode 100644 index 0000000..e44a958 --- /dev/null +++ b/src/nodes/AggregationNode.js @@ -0,0 +1,162 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for aggregation expressions + * Represents: aggregate { ... } USING majority + */ +export class AggregationNode extends BaseNode { + constructor(location = null) { + super('Aggregation', location); + this.expressions = []; // Array of expressions to aggregate + this.method = null; // Aggregation method ('majority', 'max', 'min', 'sum', 'avg') + this.weights = null; // Optional weights array + } + + /** + * Add an expression to this aggregation + * @param {ExpressionNode} expression - Expression to add + */ + addExpression(expression) { + this.expressions.push(expression); + this.addChild(expression); + } + + /** + * Set the aggregation method + * @param {string} method - Aggregation method + */ + setMethod(method) { + this.method = method; + } + + /** + * Set weights for this aggregation + * @param {number[]} weights - Weights array + */ + setWeights(weights) { + this.weights = weights; + } + + /** + * Get all expressions + * @returns {ExpressionNode[]} Expressions to aggregate + */ + getExpressions() { + return this.expressions; + } + + /** + * Get the aggregation method + * @returns {string|null} Aggregation method or null + */ + getMethod() { + return this.method; + } + + /** + * Get the weights for this aggregation + * @returns {number[]|null} Weights or null + */ + getWeights() { + return this.weights; + } + + /** + * Check if this aggregation has weights + * @returns {boolean} True if has weights + */ + hasWeights() { + return this.weights !== null && this.weights.length > 0; + } + + /** + * Check if this is a majority aggregation + * @returns {boolean} True if majority + */ + isMajority() { + return this.method === 'majority'; + } + + /** + * Check if this is a max aggregation + * @returns {boolean} True if max + */ + isMax() { + return this.method === 'max'; + } + + /** + * Check if this is a min aggregation + * @returns {boolean} True if min + */ + isMin() { + return this.method === 'min'; + } + + /** + * Check if this is a sum aggregation + * @returns {boolean} True if sum + */ + isSum() { + return this.method === 'sum'; + } + + /** + * Check if this is an average aggregation + * @returns {boolean} True if average + */ + isAverage() { + return this.method === 'avg'; + } + + /** + * Get the number of expressions + * @returns {number} Number of expressions + */ + getExpressionCount() { + return this.expressions.length; + } + + /** + * Validate the aggregation + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate method + const validMethods = ['majority', 'max', 'min', 'sum', 'avg', 'count']; + if (!this.method || !validMethods.includes(this.method)) { + errors.push(`Invalid aggregation method: ${this.method}`); + } + + // Validate expressions + if (this.expressions.length === 0) { + errors.push('Aggregation must have at least one expression'); + } + + // Validate each expression + this.expressions.forEach((expr, index) => { + const exprErrors = expr.validate ? expr.validate() : []; + errors.push(...exprErrors.map(err => `Expression ${index + 1}: ${err}`)); + }); + + // Validate weights + if (this.weights !== null) { + if (!Array.isArray(this.weights)) { + errors.push('Weights must be an array'); + } else if (this.weights.length !== this.expressions.length) { + errors.push('Weights array length must match expression count'); + } else if (this.weights.some(w => typeof w !== 'number' || w < 0)) { + errors.push('All weights must be non-negative numbers'); + } + } + + return errors; + } + + toString() { + const weightsStr = this.hasWeights() ? ` weights[${this.weights.length}]` : ''; + return `Aggregation(${this.method}, ${this.expressions.length} expressions${weightsStr})`; + } +} diff --git a/src/nodes/BaseNode.js b/src/nodes/BaseNode.js new file mode 100644 index 0000000..d3be2e5 --- /dev/null +++ b/src/nodes/BaseNode.js @@ -0,0 +1,157 @@ +/** + * Base AST Node class for all DSL AST nodes + * Provides common functionality for all AST nodes + */ +export class BaseNode { + constructor(type, location = null) { + this.type = type; + this.location = location; // { start, end, line, column } + this.parent = null; + this.children = []; + } + + /** + * Add a child node to this node + * @param {BaseNode} child - Child node to add + */ + addChild(child) { + if (child) { + child.parent = this; + this.children.push(child); + } + return this; + } + + /** + * Add multiple child nodes + * @param {BaseNode[]} children - Array of child nodes + */ + addChildren(children) { + children.forEach(child => this.addChild(child)); + return this; + } + + /** + * Get all children of a specific type + * @param {string} type - Node type to filter by + * @returns {BaseNode[]} Filtered children + */ + getChildrenOfType(type) { + return this.children.filter(child => child.type === type); + } + + /** + * Find the first child of a specific type + * @param {string} type - Node type to find + * @returns {BaseNode|null} First matching child or null + */ + getChildOfType(type) { + return this.children.find(child => child.type === type) || null; + } + + /** + * Get all descendants of a specific type + * @param {string} type - Node type to find + * @returns {BaseNode[]} All matching descendants + */ + getDescendantsOfType(type) { + const results = []; + this.children.forEach(child => { + if (child.type === type) { + results.push(child); + } + results.push(...child.getDescendantsOfType(type)); + }); + return results; + } + + /** + * Accept a visitor (visitor pattern) + * @param {Object} visitor - Visitor object with visit methods + * @returns {*} Result of visitor.visit{NodeType}(this) + */ + accept(visitor) { + const methodName = `visit${this.type}`; + if (visitor[methodName]) { + return visitor[methodName](this); + } + if (visitor.visit) { + return visitor.visit(this); + } + return null; + } + + /** + * Get a string representation of this node + * @returns {string} String representation + */ + toString() { + return `${this.type}(${this.children.length} children)`; + } + + /** + * Get a detailed string representation for debugging + * @returns {string} Detailed string representation + */ + toDebugString() { + const childrenStr = this.children.map(child => + child.toDebugString ? child.toDebugString() : child.toString() + ).join(', '); + return `${this.type}(${childrenStr})`; + } + + /** + * Clone this node and all its children + * @returns {BaseNode} Cloned node + */ + clone() { + const cloned = new this.constructor(); + cloned.type = this.type; + cloned.location = this.location ? { ...this.location } : null; + cloned.children = this.children.map(child => child.clone()); + cloned.children.forEach(child => child.parent = cloned); + return cloned; + } + + /** + * Get the root node of the AST + * @returns {BaseNode} Root node + */ + getRoot() { + let current = this; + while (current.parent) { + current = current.parent; + } + return current; + } + + /** + * Get the depth of this node in the AST + * @returns {number} Depth from root + */ + getDepth() { + let depth = 0; + let current = this.parent; + while (current) { + depth++; + current = current.parent; + } + return depth; + } + + /** + * Check if this node is a descendant of another node + * @param {BaseNode} ancestor - Potential ancestor node + * @returns {boolean} True if ancestor is an ancestor of this node + */ + isDescendantOf(ancestor) { + let current = this.parent; + while (current) { + if (current === ancestor) { + return true; + } + current = current.parent; + } + return false; + } +} diff --git a/src/nodes/BehaviorNode.js b/src/nodes/BehaviorNode.js new file mode 100644 index 0000000..3e25127 --- /dev/null +++ b/src/nodes/BehaviorNode.js @@ -0,0 +1,151 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for field behaviors (decay, blur, ttl) + * Represents: BEHAVES { decaying down hourly } + */ +export class BehaviorNode extends BaseNode { + constructor(type, location = null) { + super('Behavior', location); + this.type = type; // 'decay', 'blur', 'ttl' + this.parameters = new Map(); + } + + /** + * Set a parameter for this behavior + * @param {string} name - Parameter name + * @param {*} value - Parameter value + */ + setParameter(name, value) { + this.parameters.set(name, value); + } + + /** + * Get a parameter value + * @param {string} name - Parameter name + * @returns {*} Parameter value or null + */ + getParameter(name) { + return this.parameters.get(name) || null; + } + + /** + * Check if this is a decay behavior + * @returns {boolean} True if decay behavior + */ + isDecay() { + return this.type === 'decay'; + } + + /** + * Check if this is a blur behavior + * @returns {boolean} True if blur behavior + */ + isBlur() { + return this.type === 'blur'; + } + + /** + * Check if this is a TTL behavior + * @returns {boolean} True if TTL behavior + */ + isTTL() { + return this.type === 'ttl'; + } + + /** + * For decay behaviors, get the direction + * @returns {string|null} Decay direction or null + */ + getDecayDirection() { + return this.getParameter('direction'); + } + + /** + * For decay behaviors, get the period + * @returns {string|null} Decay period or null + */ + getDecayPeriod() { + return this.getParameter('period'); + } + + /** + * For blur behaviors, get the mode + * @returns {string|null} Blur mode or null + */ + getBlurMode() { + return this.getParameter('mode'); + } + + /** + * For blur behaviors, get the confidence level + * @returns {string|null} Confidence level or null + */ + getBlurConfidence() { + return this.getParameter('confidence'); + } + + /** + * For TTL behaviors, get the duration + * @returns {string|null} TTL duration or null + */ + getTTLDuration() { + return this.getParameter('duration'); + } + + /** + * Validate the behavior + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate behavior type + if (!['decay', 'blur', 'ttl'].includes(this.type)) { + errors.push(`Invalid behavior type: ${this.type}`); + } + + // Validate decay behavior parameters + if (this.isDecay()) { + const direction = this.getDecayDirection(); + if (!direction || !['up', 'down', 'neutral', 'stable'].includes(direction)) { + errors.push(`Invalid decay direction: ${direction}`); + } + + const period = this.getDecayPeriod(); + if (!period || !['hourly', 'daily', 'weekly', 'monthly'].includes(period)) { + errors.push(`Invalid decay period: ${period}`); + } + } + + // Validate blur behavior parameters + if (this.isBlur()) { + const mode = this.getBlurMode(); + if (!mode || !['fixed', 'adaptive', 'confidence'].includes(mode)) { + errors.push(`Invalid blur mode: ${mode}`); + } + + const confidence = this.getBlurConfidence(); + if (confidence && !['confidence_90', 'confidence_95', 'confidence_99'].includes(confidence)) { + errors.push(`Invalid blur confidence: ${confidence}`); + } + } + + // Validate TTL behavior parameters + if (this.isTTL()) { + const duration = this.getTTLDuration(); + if (!duration || !/^\d+[hd]$/.test(duration)) { + errors.push(`Invalid TTL duration: ${duration}`); + } + } + + return errors; + } + + toString() { + const params = Array.from(this.parameters.entries()) + .map(([key, value]) => `${key}: ${value}`) + .join(', '); + return `Behavior(${this.type}, ${params})`; + } +} diff --git a/src/nodes/DefeasibleLogicNode.js b/src/nodes/DefeasibleLogicNode.js new file mode 100644 index 0000000..acbe47c --- /dev/null +++ b/src/nodes/DefeasibleLogicNode.js @@ -0,0 +1,150 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for defeasible logic statements + * Represents: ALWAYS, WHEN, UNLESS, REQUIRES statements + */ +export class DefeasibleLogicNode extends BaseNode { + constructor(logicType, location = null) { + super('DefeasibleLogic', location); + this.logicType = logicType; // 'ALWAYS', 'WHEN', 'UNLESS', 'REQUIRES' + this.condition = null; // ExpressionNode or EvidenceBodyNode + this.defeater = null; // ExpressionNode or EvidenceBodyNode (for WHEN/UNLESS) + } + + /** + * Set the condition for this defeasible logic + * @param {BaseNode} condition - Condition to set + */ + setCondition(condition) { + this.condition = condition; + this.addChild(condition); + } + + /** + * Set the defeater for this defeasible logic (for WHEN/UNLESS) + * @param {BaseNode} defeater - Defeater to set + */ + setDefeater(defeater) { + this.defeater = defeater; + this.addChild(defeater); + } + + /** + * Check if this is an ALWAYS statement + * @returns {boolean} True if ALWAYS + */ + isAlways() { + return this.logicType === 'ALWAYS'; + } + + /** + * Check if this is a WHEN statement + * @returns {boolean} True if WHEN + */ + isWhen() { + return this.logicType === 'WHEN'; + } + + /** + * Check if this is an UNLESS statement + * @returns {boolean} True if UNLESS + */ + isUnless() { + return this.logicType === 'UNLESS'; + } + + /** + * Check if this is a REQUIRES statement + * @returns {boolean} True if REQUIRES + */ + isRequires() { + return this.logicType === 'REQUIRES'; + } + + /** + * Check if this is a strict rule (ALWAYS) + * @returns {boolean} True if strict + */ + isStrict() { + return this.isAlways(); + } + + /** + * Check if this is a defeasible rule (WHEN) + * @returns {boolean} True if defeasible + */ + isDefeasible() { + return this.isWhen(); + } + + /** + * Check if this is a defeater (UNLESS) + * @returns {boolean} True if defeater + */ + isDefeater() { + return this.isUnless(); + } + + /** + * Check if this is a requirement (REQUIRES) + * @returns {boolean} True if requirement + */ + isRequirement() { + return this.isRequires(); + } + + /** + * Get the precedence level for this logic type + * @returns {number} Precedence level (higher = more important) + */ + getPrecedence() { + switch (this.logicType) { + case 'ALWAYS': return 3; // Highest precedence + case 'WHEN': return 2; // Medium precedence + case 'UNLESS': return 2; // Medium precedence + case 'REQUIRES': return 1; // Lowest precedence + default: return 0; + } + } + + /** + * Validate the defeasible logic + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate logic type + if (!['ALWAYS', 'WHEN', 'UNLESS', 'REQUIRES'].includes(this.logicType)) { + errors.push(`Invalid logic type: ${this.logicType}`); + } + + // Validate condition + if (!this.condition) { + errors.push(`${this.logicType} statement must have a condition`); + } else { + const condErrors = this.condition.validate ? this.condition.validate() : []; + errors.push(...condErrors); + } + + // Validate defeater for WHEN/UNLESS + if ((this.isWhen() || this.isUnless()) && !this.defeater) { + errors.push(`${this.logicType} statement must have a defeater`); + } + + // Validate defeater if present + if (this.defeater) { + const defErrors = this.defeater.validate ? this.defeater.validate() : []; + errors.push(...defErrors); + } + + return errors; + } + + toString() { + const condStr = this.condition ? this.condition.toString() : 'null'; + const defStr = this.defeater ? ` UNLESS ${this.defeater.toString()}` : ''; + return `DefeasibleLogic(${this.logicType} ${condStr}${defStr})`; + } +} diff --git a/src/nodes/DefinitionNode.js b/src/nodes/DefinitionNode.js new file mode 100644 index 0000000..c2a20df --- /dev/null +++ b/src/nodes/DefinitionNode.js @@ -0,0 +1,117 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for type definitions + * Represents: definition User { ... } + */ +export class DefinitionNode extends BaseNode { + constructor(name, definitionType = 'type', location = null) { + super('Definition', location); + this.name = name; + this.definitionType = definitionType; // 'type', 'interface', etc. + this.fields = []; + this.behaviors = new Map(); // field name -> behavior + this.cacheDirectives = new Map(); // field name -> cache directive + } + + /** + * Add a field to the definition + * @param {FieldNode} field - Field to add + */ + addField(field) { + this.fields.push(field); + this.addChild(field); + } + + /** + * Set behavior for a field + * @param {string} fieldName - Name of the field + * @param {BehaviorNode} behavior - Behavior to set + */ + setBehavior(fieldName, behavior) { + this.behaviors.set(fieldName, behavior); + } + + /** + * Set cache directive for a field + * @param {string} fieldName - Name of the field + * @param {string} directive - Cache directive ('lazy') + */ + setCacheDirective(fieldName, directive) { + if (directive === 'eager') { + if (!DefinitionNode._warnedEagerCacheDirective) { + DefinitionNode._warnedEagerCacheDirective = true; + console.warn('[DefinitionNode] CACHE eager is deprecated; treating as CACHE lazy.'); + } + this.cacheDirectives.set(fieldName, 'lazy'); + return; + } + this.cacheDirectives.set(fieldName, directive); + } + + /** + * Get behavior for a field + * @param {string} fieldName - Name of the field + * @returns {BehaviorNode|null} Behavior or null + */ + getBehavior(fieldName) { + return this.behaviors.get(fieldName) || null; + } + + /** + * Get cache directive for a field + * @param {string} fieldName - Name of the field + * @returns {string|null} Cache directive or null + */ + getCacheDirective(fieldName) { + return this.cacheDirectives.get(fieldName) || null; + } + + /** + * Find a field by name + * @param {string} fieldName - Name to search for + * @returns {FieldNode|null} Found field or null + */ + getField(fieldName) { + return this.fields.find(field => field.name === fieldName) || null; + } + + /** + * Get all fields with a specific type + * @param {string} type - Type to filter by + * @returns {FieldNode[]} Filtered fields + */ + getFieldsOfType(type) { + return this.fields.filter(field => field.type === type); + } + + /** + * Validate the definition + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Check for duplicate field names + const fieldNames = new Set(); + this.fields.forEach(field => { + if (fieldNames.has(field.name)) { + errors.push(`Duplicate field name '${field.name}' in definition '${this.name}'`); + } else { + fieldNames.add(field.name); + } + }); + + // Validate each field + this.fields.forEach(field => { + const fieldErrors = field.validate ? field.validate() : []; + errors.push(...fieldErrors); + }); + + return errors; + } + + toString() { + return `Definition(${this.name}: ${this.fields.length} fields)`; + } +} diff --git a/src/nodes/DirectEvidenceNode.js b/src/nodes/DirectEvidenceNode.js new file mode 100644 index 0000000..c08c9eb --- /dev/null +++ b/src/nodes/DirectEvidenceNode.js @@ -0,0 +1,78 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for direct evidence statements + * Represents: owns(user, doc) + */ +export class DirectEvidenceNode extends BaseNode { + constructor(location = null) { + super('DirectEvidence', location); + this.predicate = null; // PredicateNode + this.negated = false; + } + + /** + * Set the predicate for this direct evidence + * @param {PredicateNode} predicate - Predicate to set + */ + setPredicate(predicate) { + this.predicate = predicate; + this.addChild(predicate); + } + + /** + * Set whether this evidence is negated + * @param {boolean} negated - Whether evidence is negated + */ + setNegated(negated) { + this.negated = negated; + } + + /** + * Check if this evidence is negated + * @returns {boolean} True if negated + */ + isNegated() { + return this.negated; + } + + /** + * Get the predicate name + * @returns {string|null} Predicate name or null + */ + getPredicateName() { + return this.predicate ? this.predicate.name : null; + } + + /** + * Get the predicate arguments + * @returns {ExpressionNode[]} Predicate arguments + */ + getArguments() { + return this.predicate ? this.predicate.arguments : []; + } + + /** + * Validate the direct evidence + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate predicate + if (!this.predicate) { + errors.push('Direct evidence must have a predicate'); + } else { + const predErrors = this.predicate.validate ? this.predicate.validate() : []; + errors.push(...predErrors); + } + + return errors; + } + + toString() { + const negStr = this.negated ? 'NOT ' : ''; + const predStr = this.predicate ? this.predicate.toString() : 'null'; + return `DirectEvidence(${negStr}${predStr})`; + } +} diff --git a/src/nodes/EvidenceBodyNode.js b/src/nodes/EvidenceBodyNode.js new file mode 100644 index 0000000..823616a --- /dev/null +++ b/src/nodes/EvidenceBodyNode.js @@ -0,0 +1,82 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for evidence body containing statements + * Represents: { statement1; statement2; ... } + */ +export class EvidenceBodyNode extends BaseNode { + constructor(location = null) { + super('EvidenceBody', location); + this.statements = []; + } + + /** + * Add a statement to the evidence body + * @param {BaseNode} statement - Statement to add + */ + addStatement(statement) { + this.statements.push(statement); + this.addChild(statement); + } + + /** + * Get all statements of a specific type + * @param {string} type - Statement type to filter by + * @returns {BaseNode[]} Filtered statements + */ + getStatementsOfType(type) { + return this.statements.filter(stmt => stmt.type === type); + } + + /** + * Get all direct evidence statements + * @returns {DirectEvidenceNode[]} Direct evidence statements + */ + getDirectEvidence() { + return this.getStatementsOfType('DirectEvidence'); + } + + /** + * Get all pattern matching statements + * @returns {PatternMatchNode[]} Pattern matching statements + */ + getPatternMatches() { + return this.getStatementsOfType('PatternMatch'); + } + + /** + * Get all defeasible logic statements + * @returns {DefeasibleLogicNode[]} Defeasible logic statements + */ + getDefeasibleLogic() { + return this.getStatementsOfType('DefeasibleLogic'); + } + + /** + * Get all fusion statements + * @returns {FusionNode[]} Fusion statements + */ + getFusions() { + return this.getStatementsOfType('Fusion'); + } + + /** + * Validate the evidence body + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate each statement + this.statements.forEach((stmt, index) => { + const stmtErrors = stmt.validate ? stmt.validate() : []; + errors.push(...stmtErrors.map(err => `Statement ${index + 1}: ${err}`)); + }); + + return errors; + } + + toString() { + return `EvidenceBody(${this.statements.length} statements)`; + } +} diff --git a/src/nodes/EvidenceNode.js b/src/nodes/EvidenceNode.js new file mode 100644 index 0000000..1245484 --- /dev/null +++ b/src/nodes/EvidenceNode.js @@ -0,0 +1,117 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for evidence definitions + * Represents: evidence canRead(user: User, doc: Document) { ... } PROVIDES string + */ +export class EvidenceNode extends BaseNode { + constructor(name, location = null) { + super('Evidence', location); + this.name = name; + this.parameters = []; + this.returnType = null; + this.body = null; // EvidenceBodyNode + this.provides = null; // Return type specification + } + + /** + * Add a parameter to the evidence + * @param {ParameterNode} parameter - Parameter to add + */ + addParameter(parameter) { + this.parameters.push(parameter); + this.addChild(parameter); + } + + /** + * Set the body of the evidence + * @param {EvidenceBodyNode} body - Evidence body + */ + setBody(body) { + this.body = body; + this.addChild(body); + } + + /** + * Set the return type for this evidence + * @param {string} returnType - Return type + */ + setReturnType(returnType) { + this.returnType = returnType; + this.provides = returnType; + } + + /** + * Get the parameter names as an array + * @returns {string[]} Array of parameter names + */ + getParameterNames() { + return this.parameters.map(param => param.name); + } + + /** + * Get the parameter types as an array + * @returns {string[]} Array of parameter types + */ + getParameterTypes() { + return this.parameters.map(param => param.type); + } + + /** + * Find a parameter by name + * @param {string} name - Parameter name to find + * @returns {ParameterNode|null} Found parameter or null + */ + getParameter(name) { + return this.parameters.find(param => param.name === name) || null; + } + + /** + * Get the signature string for this evidence + * @returns {string} Evidence signature + */ + getSignature() { + const paramStr = this.parameters.map(param => `${param.name}: ${param.type}`).join(', '); + return `${this.name}(${paramStr})`; + } + + /** + * Check if this evidence has a return type + * @returns {boolean} True if has return type + */ + hasReturnType() { + return this.returnType !== null; + } + + /** + * Validate the evidence + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate evidence name + if (!this.name || typeof this.name !== 'string') { + errors.push(`Invalid evidence name: ${this.name}`); + } + + // Validate parameters + this.parameters.forEach((param, index) => { + const paramErrors = param.validate ? param.validate() : []; + errors.push(...paramErrors.map(err => `Parameter ${index + 1}: ${err}`)); + }); + + // Validate body + if (this.body) { + const bodyErrors = this.body.validate ? this.body.validate() : []; + errors.push(...bodyErrors); + } + + return errors; + } + + toString() { + const providesStr = this.returnType ? ` PROVIDES ${this.returnType}` : ''; + return `Evidence(${this.getSignature()}${providesStr})`; + } +} diff --git a/src/nodes/ExpressionNode.js b/src/nodes/ExpressionNode.js new file mode 100644 index 0000000..958aa4f --- /dev/null +++ b/src/nodes/ExpressionNode.js @@ -0,0 +1,260 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for expressions (variables, literals, attribute access, etc.) + */ +export class ExpressionNode extends BaseNode { + constructor(expressionType, location = null) { + super('Expression', location); + this.expressionType = expressionType; // 'variable', 'literal', 'attribute', 'function', etc. + this.value = null; + this.name = null; + this.attribute = null; + this.object = null; + this.arguments = []; + this.operator = null; + this.left = null; + this.right = null; + } + + /** + * Set the value for this expression + * @param {*} value - Value to set + */ + setValue(value) { + this.value = value; + } + + /** + * Set the name for this expression + * @param {string} name - Name to set + */ + setName(name) { + this.name = name; + } + + /** + * Set the attribute for this expression + * @param {string} attribute - Attribute to set + */ + setAttribute(attribute) { + this.attribute = attribute; + } + + /** + * Set the object for this expression + * @param {ExpressionNode} object - Object to set + */ + setObject(object) { + this.object = object; + this.addChild(object); + } + + /** + * Add an argument to this expression + * @param {ExpressionNode} argument - Argument to add + */ + addArgument(argument) { + this.arguments.push(argument); + this.addChild(argument); + } + + /** + * Set the operator for this expression + * @param {string} operator - Operator to set + */ + setOperator(operator) { + this.operator = operator; + } + + /** + * Set the left operand for this expression + * @param {ExpressionNode} left - Left operand to set + */ + setLeft(left) { + this.left = left; + this.addChild(left); + } + + /** + * Set the right operand for this expression + * @param {ExpressionNode} right - Right operand to set + */ + setRight(right) { + this.right = right; + this.addChild(right); + } + + /** + * Check if this is a variable expression + * @returns {boolean} True if variable + */ + isVariable() { + return this.expressionType === 'variable'; + } + + /** + * Check if this is a literal expression + * @returns {boolean} True if literal + */ + isLiteral() { + return this.expressionType === 'literal'; + } + + /** + * Check if this is an attribute access expression + * @returns {boolean} True if attribute access + */ + isAttributeAccess() { + return this.expressionType === 'attribute'; + } + + /** + * Check if this is a function call expression + * @returns {boolean} True if function call + */ + isFunctionCall() { + return this.expressionType === 'function'; + } + + /** + * Check if this is a binary operation expression + * @returns {boolean} True if binary operation + */ + isBinaryOperation() { + return this.expressionType === 'binary'; + } + + /** + * Check if this is a wildcard variable + * @returns {boolean} True if wildcard + */ + isWildcard() { + return this.isVariable() && this.name && this.name.startsWith('*'); + } + + /** + * Get the variable name (without wildcard prefix) + * @returns {string|null} Variable name or null + */ + getVariableName() { + if (this.isVariable() && this.name) { + return this.name.startsWith('*') ? this.name.substring(1) : this.name; + } + return null; + } + + /** + * Get the full attribute path + * @returns {string|null} Full attribute path or null + */ + getAttributePath() { + if (this.isAttributeAccess()) { + const objStr = this.object ? this.object.toString() : ''; + return `${objStr}.${this.attribute}`; + } + return null; + } + + /** + * Get the function signature + * @returns {string|null} Function signature or null + */ + getFunctionSignature() { + if (this.isFunctionCall()) { + const argStr = this.arguments.map(arg => arg.toString()).join(', '); + return `${this.name}(${argStr})`; + } + return null; + } + + /** + * Validate the expression + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate expression type + const validTypes = ['variable', 'literal', 'attribute', 'function', 'binary']; + if (!validTypes.includes(this.expressionType)) { + errors.push(`Invalid expression type: ${this.expressionType}`); + } + + // Validate variable expressions + if (this.isVariable() && !this.name) { + errors.push('Variable expression must have a name'); + } + + // Validate literal expressions + if (this.isLiteral() && this.value === null) { + errors.push('Literal expression must have a value'); + } + + // Validate attribute access expressions + if (this.isAttributeAccess()) { + if (!this.attribute) { + errors.push('Attribute access expression must have an attribute'); + } + if (this.object) { + const objErrors = this.object.validate ? this.object.validate() : []; + errors.push(...objErrors); + } + } + + // Validate function call expressions + if (this.isFunctionCall()) { + if (!this.name) { + errors.push('Function call expression must have a name'); + } + this.arguments.forEach((arg, index) => { + const argErrors = arg.validate ? arg.validate() : []; + errors.push(...argErrors.map(err => `Argument ${index + 1}: ${err}`)); + }); + } + + // Validate binary operation expressions + if (this.isBinaryOperation()) { + if (!this.operator) { + errors.push('Binary operation expression must have an operator'); + } + if (!this.left) { + errors.push('Binary operation expression must have a left operand'); + } + if (!this.right) { + errors.push('Binary operation expression must have a right operand'); + } + if (this.left) { + const leftErrors = this.left.validate ? this.left.validate() : []; + errors.push(...leftErrors); + } + if (this.right) { + const rightErrors = this.right.validate ? this.right.validate() : []; + errors.push(...rightErrors); + } + } + + return errors; + } + + toString() { + switch (this.expressionType) { + case 'variable': + return `Variable(${this.name})`; + case 'literal': + return `Literal(${this.value})`; + case 'attribute': + const objStr = this.object ? this.object.toString() : ''; + return `Attribute(${objStr}.${this.attribute})`; + case 'function': + const argStr = this.arguments.map(arg => arg.toString()).join(', '); + return `Function(${this.name}(${argStr}))`; + case 'binary': + const leftStr = this.left ? this.left.toString() : 'null'; + const rightStr = this.right ? this.right.toString() : 'null'; + return `Binary(${leftStr} ${this.operator} ${rightStr})`; + default: + return `Expression(${this.expressionType})`; + } + } +} diff --git a/src/nodes/FactNode.js b/src/nodes/FactNode.js new file mode 100644 index 0000000..e592b5b --- /dev/null +++ b/src/nodes/FactNode.js @@ -0,0 +1,166 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for fact definitions + * Represents: fact hasRole(user: User, role: string) CACHE lazy + */ +export class FactNode extends BaseNode { + constructor(name, location = null) { + super('Fact', location); + this.name = name; + this.parameters = []; + this.returnType = null; + this.properties = new Map(); // transitive, symmetrical, etc. + this.cacheDirective = null; + this.limit = null; + } + + /** + * Add a parameter to the fact + * @param {ParameterNode} parameter - Parameter to add + */ + addParameter(parameter) { + this.parameters.push(parameter); + this.addChild(parameter); + } + + /** + * Set the return type for this fact + * @param {string} returnType - Return type + */ + setReturnType(returnType) { + this.returnType = returnType; + } + + /** + * Set a property for this fact + * @param {string} name - Property name + * @param {*} value - Property value + */ + setProperty(name, value) { + this.properties.set(name, value); + } + + /** + * Get a property value + * @param {string} name - Property name + * @returns {*} Property value or null + */ + getProperty(name) { + return this.properties.get(name) || null; + } + + /** + * Set the cache directive for this fact + * @param {string} directive - Cache directive ('lazy') + */ + setCacheDirective(directive) { + if (directive === 'eager') { + if (!FactNode._warnedEagerCacheDirective) { + FactNode._warnedEagerCacheDirective = true; + console.warn('[FactNode] CACHE eager is deprecated; treating as CACHE lazy.'); + } + this.cacheDirective = 'lazy'; + return; + } + this.cacheDirective = directive; + } + + /** + * Set the limit for this fact + * @param {number} limit - Limit value + */ + setLimit(limit) { + this.limit = limit; + } + + /** + * Check if this fact is transitive + * @returns {boolean} True if transitive + */ + isTransitive() { + return this.getProperty('transitive') === true; + } + + /** + * Check if this fact is symmetrical + * @returns {boolean} True if symmetrical + */ + isSymmetrical() { + return this.getProperty('symmetrical') === true; + } + + /** + * Get the parameter names as an array + * @returns {string[]} Array of parameter names + */ + getParameterNames() { + return this.parameters.map(param => param.name); + } + + /** + * Get the parameter types as an array + * @returns {string[]} Array of parameter types + */ + getParameterTypes() { + return this.parameters.map(param => param.type); + } + + /** + * Find a parameter by name + * @param {string} name - Parameter name to find + * @returns {ParameterNode|null} Found parameter or null + */ + getParameter(name) { + return this.parameters.find(param => param.name === name) || null; + } + + /** + * Get the signature string for this fact + * @returns {string} Fact signature + */ + getSignature() { + const paramStr = this.parameters.map(param => `${param.name}: ${param.type}`).join(', '); + return `${this.name}(${paramStr})`; + } + + /** + * Validate the fact + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate fact name + if (!this.name || typeof this.name !== 'string') { + errors.push(`Invalid fact name: ${this.name}`); + } + + // Validate parameters + this.parameters.forEach((param, index) => { + const paramErrors = param.validate ? param.validate() : []; + errors.push(...paramErrors.map(err => `Parameter ${index + 1}: ${err}`)); + }); + + // Validate cache directive + if (this.cacheDirective && !['lazy'].includes(this.cacheDirective)) { + errors.push(`Invalid cache directive: ${this.cacheDirective}`); + } + + // Validate limit + if (this.limit !== null && (typeof this.limit !== 'number' || this.limit < 0)) { + errors.push(`Invalid limit: ${this.limit}`); + } + + return errors; + } + + toString() { + const props = Array.from(this.properties.entries()) + .map(([key, value]) => `${key}: ${value}`) + .join(', '); + const cacheStr = this.cacheDirective ? ` CACHE ${this.cacheDirective}` : ''; + const limitStr = this.limit ? ` LIMIT ${this.limit}` : ''; + return `Fact(${this.getSignature()}${props ? `, ${props}` : ''}${cacheStr}${limitStr})`; + } +} diff --git a/src/nodes/FieldNode.js b/src/nodes/FieldNode.js new file mode 100644 index 0000000..f45630a --- /dev/null +++ b/src/nodes/FieldNode.js @@ -0,0 +1,141 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for field definitions within type definitions + * Represents: fieldName: type BEHAVES { ... } CACHE lazy + */ +export class FieldNode extends BaseNode { + constructor(name, type, location = null) { + super('Field', location); + this.name = name; + this.type = type; + this.isArray = false; + this.behavior = null; + this.cacheDirective = null; + this.isOptional = false; + this.defaultValue = null; + } + + /** + * Set the behavior for this field + * @param {BehaviorNode} behavior - Behavior to set + */ + setBehavior(behavior) { + this.behavior = behavior; + this.addChild(behavior); + } + + /** + * Set the cache directive for this field + * @param {string} directive - Cache directive ('lazy') + */ + setCacheDirective(directive) { + if (directive === 'eager') { + if (!FieldNode._warnedEagerCacheDirective) { + FieldNode._warnedEagerCacheDirective = true; + console.warn('[FieldNode] CACHE eager is deprecated; treating as CACHE lazy.'); + } + this.cacheDirective = 'lazy'; + return; + } + this.cacheDirective = directive; + } + + /** + * Mark this field as an array type + * @param {boolean} isArray - Whether this is an array + */ + setArray(isArray) { + this.isArray = isArray; + } + + /** + * Set whether this field is optional + * @param {boolean} optional - Whether field is optional + */ + setOptional(optional) { + this.isOptional = optional; + } + + /** + * Set default value for this field + * @param {*} value - Default value + */ + setDefaultValue(value) { + this.defaultValue = value; + } + + /** + * Get the full type string including array notation + * @returns {string} Full type string + */ + getFullType() { + let typeStr = this.type; + if (this.isArray) { + typeStr += '[]'; + } + if (this.isOptional) { + typeStr += '?'; + } + return typeStr; + } + + /** + * Check if this field has decay behavior + * @returns {boolean} True if field has decay behavior + */ + hasDecayBehavior() { + return this.behavior && this.behavior.type === 'decay'; + } + + /** + * Check if this field has blur behavior + * @returns {boolean} True if field has blur behavior + */ + hasBlurBehavior() { + return this.behavior && this.behavior.type === 'blur'; + } + + /** + * Check if this field has TTL behavior + * @returns {boolean} True if field has TTL behavior + */ + hasTTLBehavior() { + return this.behavior && this.behavior.type === 'ttl'; + } + + /** + * Validate the field + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate field name + if (!this.name || typeof this.name !== 'string') { + errors.push(`Invalid field name: ${this.name}`); + } + + // Validate type + if (!this.type || typeof this.type !== 'string') { + errors.push(`Invalid field type: ${this.type}`); + } + + // Validate behavior if present + if (this.behavior) { + const behaviorErrors = this.behavior.validate ? this.behavior.validate() : []; + errors.push(...behaviorErrors); + } + + // Validate cache directive + if (this.cacheDirective && !['lazy'].includes(this.cacheDirective)) { + errors.push(`Invalid cache directive: ${this.cacheDirective}`); + } + + return errors; + } + + toString() { + return `Field(${this.name}: ${this.getFullType()})`; + } +} diff --git a/src/nodes/FusionNode.js b/src/nodes/FusionNode.js new file mode 100644 index 0000000..9d9f8cf --- /dev/null +++ b/src/nodes/FusionNode.js @@ -0,0 +1,170 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for fusion statements + * Represents: fusion max { ... } + */ +export class FusionNode extends BaseNode { + constructor(strategy, location = null) { + super('Fusion', location); + this.strategy = strategy; // 'max', 'min', 'majority', 'average', etc. + this.evidence = []; // Array of evidence statements + this.weights = null; // Optional weights array + } + + /** + * Add evidence to this fusion + * @param {BaseNode} evidence - Evidence to add + */ + addEvidence(evidence) { + this.evidence.push(evidence); + this.addChild(evidence); + } + + /** + * Set weights for this fusion + * @param {number[]} weights - Weights array + */ + setWeights(weights) { + this.weights = weights; + } + + /** + * Get the fusion strategy + * @returns {string} Fusion strategy + */ + getStrategy() { + return this.strategy; + } + + /** + * Get all evidence statements + * @returns {BaseNode[]} Evidence statements + */ + getEvidence() { + return this.evidence; + } + + /** + * Get the weights for this fusion + * @returns {number[]|null} Weights or null + */ + getWeights() { + return this.weights; + } + + /** + * Check if this fusion has weights + * @returns {boolean} True if has weights + */ + hasWeights() { + return this.weights !== null && this.weights.length > 0; + } + + /** + * Check if this is a max fusion + * @returns {boolean} True if max fusion + */ + isMax() { + return this.strategy === 'max'; + } + + /** + * Check if this is a min fusion + * @returns {boolean} True if min fusion + */ + isMin() { + return this.strategy === 'min'; + } + + /** + * Check if this is a majority fusion + * @returns {boolean} True if majority fusion + */ + isMajority() { + return this.strategy === 'majority'; + } + + /** + * Check if this is an average fusion + * @returns {boolean} True if average fusion + */ + isAverage() { + return this.strategy === 'average'; + } + + /** + * Get the number of evidence statements + * @returns {number} Number of evidence statements + */ + getEvidenceCount() { + return this.evidence.length; + } + + /** + * Validate the fusion + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate strategy + const validStrategies = [ + 'max', + 'min', + 'majority', + 'average', + 'sum', + 'sum_unbounded', + 'median', + 'optimistic', + 'pessimistic', + 'top2', + 'top3', + 'priority', + 'custom', + 'count' + ]; + if (!validStrategies.includes(this.strategy)) { + errors.push(`Invalid fusion strategy: ${this.strategy}`); + } + + // Validate evidence + if (this.evidence.length === 0) { + errors.push('Fusion must have at least one evidence statement'); + } + + // Validate each evidence statement + this.evidence.forEach((ev, index) => { + const evErrors = ev.validate ? ev.validate() : []; + errors.push(...evErrors.map(err => `Evidence ${index + 1}: ${err}`)); + }); + + // Validate weights + if (this.weights !== null) { + if (!Array.isArray(this.weights)) { + errors.push('Weights must be an array'); + } else if (this.weights.length !== this.evidence.length) { + errors.push('Weights array length must match evidence count'); + } else if (this.weights.some(w => typeof w !== 'number' || w < 0)) { + errors.push('All weights must be non-negative numbers'); + } else if (this.strategy === 'custom') { + const total = this.weights.reduce((sum, w) => sum + w, 0); + if (Math.abs(total - 1.0) > 1e-6) { + errors.push('Custom weights must sum to 1.0'); + } + } + } + + if (this.strategy === 'custom' && (!this.weights || this.weights.length === 0)) { + errors.push('Custom fusion requires weights'); + } + + return errors; + } + + toString() { + const weightsStr = this.hasWeights() ? ` weights[${this.weights.length}]` : ''; + return `Fusion(${this.strategy}, ${this.evidence.length} evidence${weightsStr})`; + } +} diff --git a/src/nodes/MeasureBodyNode.js b/src/nodes/MeasureBodyNode.js new file mode 100644 index 0000000..3d0ebc1 --- /dev/null +++ b/src/nodes/MeasureBodyNode.js @@ -0,0 +1,130 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for measure body containing expressions + * Represents: { user.role } + */ +export class MeasureBodyNode extends BaseNode { + constructor(location = null) { + super('MeasureBody', location); + this.expression = null; // ExpressionNode + this.fusion = null; // FusionNode (optional) + this.aggregation = null; // AggregationNode (optional) + } + + /** + * Set the expression for this measure body + * @param {ExpressionNode} expression - Expression to set + */ + setExpression(expression) { + this.expression = expression; + this.addChild(expression); + } + + /** + * Set the fusion for this measure body + * @param {FusionNode} fusion - Fusion to set + */ + setFusion(fusion) { + this.fusion = fusion; + this.addChild(fusion); + } + + /** + * Set the aggregation for this measure body + * @param {AggregationNode} aggregation - Aggregation to set + */ + setAggregation(aggregation) { + this.aggregation = aggregation; + this.addChild(aggregation); + } + + /** + * Get the expression + * @returns {ExpressionNode|null} Expression or null + */ + getExpression() { + return this.expression; + } + + /** + * Get the fusion + * @returns {FusionNode|null} Fusion or null + */ + getFusion() { + return this.fusion; + } + + /** + * Get the aggregation + * @returns {AggregationNode|null} Aggregation or null + */ + getAggregation() { + return this.aggregation; + } + + /** + * Check if this measure body has an expression + * @returns {boolean} True if has expression + */ + hasExpression() { + return this.expression !== null; + } + + /** + * Check if this measure body has fusion + * @returns {boolean} True if has fusion + */ + hasFusion() { + return this.fusion !== null; + } + + /** + * Check if this measure body has aggregation + * @returns {boolean} True if has aggregation + */ + hasAggregation() { + return this.aggregation !== null; + } + + /** + * Validate the measure body + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Must have at least one of expression, fusion, or aggregation + if (!this.expression && !this.fusion && !this.aggregation) { + errors.push('Measure body must have an expression, fusion, or aggregation'); + } + + // Validate expression if present + if (this.expression) { + const exprErrors = this.expression.validate ? this.expression.validate() : []; + errors.push(...exprErrors); + } + + // Validate fusion if present + if (this.fusion) { + const fusionErrors = this.fusion.validate ? this.fusion.validate() : []; + errors.push(...fusionErrors); + } + + // Validate aggregation if present + if (this.aggregation) { + const aggErrors = this.aggregation.validate ? this.aggregation.validate() : []; + errors.push(...aggErrors); + } + + return errors; + } + + toString() { + const parts = []; + if (this.expression) parts.push(this.expression.toString()); + if (this.fusion) parts.push(this.fusion.toString()); + if (this.aggregation) parts.push(this.aggregation.toString()); + return `MeasureBody(${parts.join(', ')})`; + } +} diff --git a/src/nodes/MeasureNode.js b/src/nodes/MeasureNode.js new file mode 100644 index 0000000..5cfac29 --- /dev/null +++ b/src/nodes/MeasureNode.js @@ -0,0 +1,117 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for measure definitions + * Represents: measure userRole(user: User) { ... } PROVIDES string + */ +export class MeasureNode extends BaseNode { + constructor(name, location = null) { + super('Measure', location); + this.name = name; + this.parameters = []; + this.returnType = null; + this.body = null; // MeasureBodyNode + this.provides = null; // Return type specification + } + + /** + * Add a parameter to the measure + * @param {ParameterNode} parameter - Parameter to add + */ + addParameter(parameter) { + this.parameters.push(parameter); + this.addChild(parameter); + } + + /** + * Set the body of the measure + * @param {MeasureBodyNode} body - Measure body + */ + setBody(body) { + this.body = body; + this.addChild(body); + } + + /** + * Set the return type for this measure + * @param {string} returnType - Return type + */ + setReturnType(returnType) { + this.returnType = returnType; + this.provides = returnType; + } + + /** + * Get the parameter names as an array + * @returns {string[]} Array of parameter names + */ + getParameterNames() { + return this.parameters.map(param => param.name); + } + + /** + * Get the parameter types as an array + * @returns {string[]} Array of parameter types + */ + getParameterTypes() { + return this.parameters.map(param => param.type); + } + + /** + * Find a parameter by name + * @param {string} name - Parameter name to find + * @returns {ParameterNode|null} Found parameter or null + */ + getParameter(name) { + return this.parameters.find(param => param.name === name) || null; + } + + /** + * Get the signature string for this measure + * @returns {string} Measure signature + */ + getSignature() { + const paramStr = this.parameters.map(param => `${param.name}: ${param.type}`).join(', '); + return `${this.name}(${paramStr})`; + } + + /** + * Check if this measure has a return type + * @returns {boolean} True if has return type + */ + hasReturnType() { + return this.returnType !== null; + } + + /** + * Validate the measure + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate measure name + if (!this.name || typeof this.name !== 'string') { + errors.push(`Invalid measure name: ${this.name}`); + } + + // Validate parameters + this.parameters.forEach((param, index) => { + const paramErrors = param.validate ? param.validate() : []; + errors.push(...paramErrors.map(err => `Parameter ${index + 1}: ${err}`)); + }); + + // Validate body + if (this.body) { + const bodyErrors = this.body.validate ? this.body.validate() : []; + errors.push(...bodyErrors); + } + + return errors; + } + + toString() { + const providesStr = this.returnType ? ` PROVIDES ${this.returnType}` : ''; + return `Measure(${this.getSignature()}${providesStr})`; + } +} diff --git a/src/nodes/ParameterNode.js b/src/nodes/ParameterNode.js new file mode 100644 index 0000000..0906c29 --- /dev/null +++ b/src/nodes/ParameterNode.js @@ -0,0 +1,79 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for function/evidence parameters + * Represents: user: User, role: string + */ +export class ParameterNode extends BaseNode { + constructor(name, type, location = null) { + super('Parameter', location); + this.name = name; + this.type = type; + this.isOptional = false; + this.defaultValue = null; + this.isArray = false; + } + + /** + * Set whether this parameter is optional + * @param {boolean} optional - Whether parameter is optional + */ + setOptional(optional) { + this.isOptional = optional; + } + + /** + * Set default value for this parameter + * @param {*} value - Default value + */ + setDefaultValue(value) { + this.defaultValue = value; + } + + /** + * Set whether this parameter is an array + * @param {boolean} isArray - Whether parameter is an array + */ + setArray(isArray) { + this.isArray = isArray; + } + + /** + * Get the full type string including array notation + * @returns {string} Full type string + */ + getFullType() { + let typeStr = this.type; + if (this.isArray) { + typeStr += '[]'; + } + if (this.isOptional) { + typeStr += '?'; + } + return typeStr; + } + + /** + * Validate the parameter + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate parameter name + if (!this.name || typeof this.name !== 'string') { + errors.push(`Invalid parameter name: ${this.name}`); + } + + // Validate type + if (!this.type || typeof this.type !== 'string') { + errors.push(`Invalid parameter type: ${this.type}`); + } + + return errors; + } + + toString() { + return `Parameter(${this.name}: ${this.getFullType()})`; + } +} diff --git a/src/nodes/PatternMatchNode.js b/src/nodes/PatternMatchNode.js new file mode 100644 index 0000000..dcdde2e --- /dev/null +++ b/src/nodes/PatternMatchNode.js @@ -0,0 +1,142 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for pattern matching statements + * Represents: isMember(user, *group) { ... } limit 5 + */ +export class PatternMatchNode extends BaseNode { + constructor(location = null) { + super('PatternMatch', location); + this.predicate = null; // PredicateNode + this.body = null; // EvidenceBodyNode + this.limit = null; + this.withClause = null; // WithClauseNode + this.negated = false; + } + + /** + * Set the predicate for this pattern match + * @param {PredicateNode} predicate - Predicate to set + */ + setPredicate(predicate) { + this.predicate = predicate; + this.addChild(predicate); + } + + /** + * Set the body of the pattern match + * @param {EvidenceBodyNode} body - Evidence body + */ + setBody(body) { + this.body = body; + this.addChild(body); + } + + /** + * Set the limit for this pattern match + * @param {number} limit - Limit value + */ + setLimit(limit) { + this.limit = limit; + } + + /** + * Set the with clause for this pattern match + * @param {WithClauseNode} withClause - With clause + */ + setWithClause(withClause) { + this.withClause = withClause; + this.addChild(withClause); + } + + /** + * Set whether this pattern match is negated + * @param {boolean} negated - Whether pattern match is negated + */ + setNegated(negated) { + this.negated = negated; + } + + /** + * Check if this pattern match is negated + * @returns {boolean} True if negated + */ + isNegated() { + return this.negated; + } + + /** + * Get the predicate name + * @returns {string|null} Predicate name or null + */ + getPredicateName() { + return this.predicate ? this.predicate.name : null; + } + + /** + * Get the predicate arguments + * @returns {ExpressionNode[]} Predicate arguments + */ + getArguments() { + return this.predicate ? this.predicate.arguments : []; + } + + /** + * Check if this pattern match has a limit + * @returns {boolean} True if has limit + */ + hasLimit() { + return this.limit !== null; + } + + /** + * Check if this pattern match has a with clause + * @returns {boolean} True if has with clause + */ + hasWithClause() { + return this.withClause !== null; + } + + /** + * Validate the pattern match + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate predicate + if (!this.predicate) { + errors.push('Pattern match must have a predicate'); + } else { + const predErrors = this.predicate.validate ? this.predicate.validate() : []; + errors.push(...predErrors); + } + + // Validate body + if (this.body) { + const bodyErrors = this.body.validate ? this.body.validate() : []; + errors.push(...bodyErrors); + } + + // Validate limit + if (this.limit !== null && (typeof this.limit !== 'number' || this.limit < 0)) { + errors.push(`Invalid limit: ${this.limit}`); + } + + // Validate with clause + if (this.withClause) { + const withErrors = this.withClause.validate ? this.withClause.validate() : []; + errors.push(...withErrors); + } + + return errors; + } + + toString() { + const negStr = this.negated ? 'NOT ' : ''; + const predStr = this.predicate ? this.predicate.toString() : 'null'; + const limitStr = this.limit ? ` limit ${this.limit}` : ''; + const withStr = this.withClause ? ` ${this.withClause.toString()}` : ''; + return `PatternMatch(${negStr}${predStr}${limitStr}${withStr})`; + } +} diff --git a/src/nodes/PredicateNode.js b/src/nodes/PredicateNode.js new file mode 100644 index 0000000..6021041 --- /dev/null +++ b/src/nodes/PredicateNode.js @@ -0,0 +1,106 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for predicate calls + * Represents: hasRole(user, role), owns(user, doc) + */ +export class PredicateNode extends BaseNode { + constructor(name, location = null) { + super('Predicate', location); + this.name = name; + this.arguments = []; + } + + /** + * Add an argument to this predicate + * @param {ExpressionNode} argument - Argument to add + */ + addArgument(argument) { + this.arguments.push(argument); + this.addChild(argument); + } + + /** + * Get the predicate name + * @returns {string} Predicate name + */ + getName() { + return this.name; + } + + /** + * Get all arguments + * @returns {ExpressionNode[]} Predicate arguments + */ + getArguments() { + return this.arguments; + } + + /** + * Get the number of arguments + * @returns {number} Number of arguments + */ + getArgumentCount() { + return this.arguments.length; + } + + /** + * Get an argument by index + * @param {number} index - Argument index + * @returns {ExpressionNode|null} Argument or null + */ + getArgument(index) { + return this.arguments[index] || null; + } + + /** + * Check if this predicate has a specific number of arguments + * @param {number} count - Expected argument count + * @returns {boolean} True if has expected count + */ + hasArgumentCount(count) { + return this.arguments.length === count; + } + + /** + * Check if this predicate has any arguments + * @returns {boolean} True if has arguments + */ + hasArguments() { + return this.arguments.length > 0; + } + + /** + * Get the signature string for this predicate + * @returns {string} Predicate signature + */ + getSignature() { + const argStr = this.arguments.map(arg => arg.toString()).join(', '); + return `${this.name}(${argStr})`; + } + + /** + * Validate the predicate + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate predicate name + if (!this.name || typeof this.name !== 'string') { + errors.push(`Invalid predicate name: ${this.name}`); + } + + // Validate arguments + this.arguments.forEach((arg, index) => { + const argErrors = arg.validate ? arg.validate() : []; + errors.push(...argErrors.map(err => `Argument ${index + 1}: ${err}`)); + }); + + return errors; + } + + toString() { + return `Predicate(${this.getSignature()})`; + } +} diff --git a/src/nodes/ProgramNode.js b/src/nodes/ProgramNode.js new file mode 100644 index 0000000..50b276e --- /dev/null +++ b/src/nodes/ProgramNode.js @@ -0,0 +1,158 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * Root node of the AST representing the entire DSL program + */ +export class ProgramNode extends BaseNode { + constructor(location = null) { + super('Program', location); + this.definitions = []; + this.facts = []; + this.evidence = []; + this.measures = []; + } + + /** + * Add a definition to the program + * @param {DefinitionNode} definition - Definition to add + */ + addDefinition(definition) { + this.definitions.push(definition); + this.addChild(definition); + } + + /** + * Add a fact to the program + * @param {FactNode} fact - Fact to add + */ + addFact(fact) { + this.facts.push(fact); + this.addChild(fact); + } + + /** + * Add evidence to the program + * @param {EvidenceNode} evidence - Evidence to add + */ + addEvidence(evidence) { + this.evidence.push(evidence); + this.addChild(evidence); + } + + /** + * Add a measure to the program + * @param {MeasureNode} measure - Measure to add + */ + addMeasure(measure) { + this.measures.push(measure); + this.addChild(measure); + } + + /** + * Get all definitions of a specific type + * @param {string} type - Definition type to filter by + * @returns {DefinitionNode[]} Filtered definitions + */ + getDefinitionsOfType(type) { + return this.definitions.filter(def => def.definitionType === type); + } + + /** + * Find a definition by name + * @param {string} name - Name to search for + * @returns {DefinitionNode|null} Found definition or null + */ + getDefinitionByName(name) { + return this.definitions.find(def => def.name === name) || null; + } + + /** + * Find evidence by name + * @param {string} name - Name to search for + * @returns {EvidenceNode|null} Found evidence or null + */ + getEvidenceByName(name) { + return this.evidence.find(ev => ev.name === name) || null; + } + + /** + * Find a fact by name + * @param {string} name - Name to search for + * @returns {FactNode|null} Found fact or null + */ + getFactByName(name) { + return this.facts.find(fact => fact.name === name) || null; + } + + /** + * Find a measure by name + * @param {string} name - Name to search for + * @returns {MeasureNode|null} Found measure or null + */ + getMeasureByName(name) { + return this.measures.find(measure => measure.name === name) || null; + } + + /** + * Get all symbols (definitions, facts, evidence, measures) by name + * @param {string} name - Name to search for + * @returns {BaseNode[]} All matching symbols + */ + getSymbolsByName(name) { + return [ + ...this.definitions.filter(def => def.name === name), + ...this.facts.filter(fact => fact.name === name), + ...this.evidence.filter(ev => ev.name === name), + ...this.measures.filter(measure => measure.name === name) + ]; + } + + /** + * Validate the program structure + * @returns {Object} Validation result with errors and warnings + */ + validate() { + const errors = []; + const warnings = []; + + // Check for duplicate names + const allNames = new Map(); + [...this.definitions, ...this.facts, ...this.evidence, ...this.measures].forEach(symbol => { + if (allNames.has(symbol.name)) { + errors.push(`Duplicate symbol name: ${symbol.name}`); + } else { + allNames.set(symbol.name, symbol); + } + }); + + // Validate each definition + this.definitions.forEach(def => { + const defErrors = def.validate ? def.validate() : []; + errors.push(...defErrors); + }); + + // Validate each fact + this.facts.forEach(fact => { + const factErrors = fact.validate ? fact.validate() : []; + errors.push(...factErrors); + }); + + // Validate each evidence + this.evidence.forEach(ev => { + const evErrors = ev.validate ? ev.validate() : []; + errors.push(...evErrors); + }); + + // Validate each measure + this.measures.forEach(measure => { + const measureErrors = measure.validate ? measure.validate() : []; + errors.push(...measureErrors); + }); + + return { errors, warnings, isValid: errors.length === 0 }; + } + + toString() { + return `Program(${this.definitions.length} definitions, ${this.facts.length} facts, ${this.evidence.length} evidence, ${this.measures.length} measures)`; + } +} diff --git a/src/nodes/SourceNode.js b/src/nodes/SourceNode.js new file mode 100644 index 0000000..e85b863 --- /dev/null +++ b/src/nodes/SourceNode.js @@ -0,0 +1,87 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for source definitions + * Represents: source *mfa(user: User) PROVIDES Proof within 10m + * + * Sources are injectable object/proof references that must be + * provided in the partial graph before authorization evaluation. + * They always carry a PROVIDES type and an optional freshness window. + */ +export class SourceNode extends BaseNode { + constructor(name, location = null) { + super('Source', location); + this.name = name; + this.injectable = false; + this.parameters = []; + this.returnType = null; + this.provides = null; + this.within = null; + this.cacheDirective = null; + } + + addParameter(parameter) { + this.parameters.push(parameter); + this.addChild(parameter); + } + + setReturnType(returnType) { + this.returnType = returnType; + this.provides = returnType; + } + + setWithin(within) { + this.within = within; + } + + setCacheDirective(directive) { + this.cacheDirective = directive; + } + + setInjectable(value) { + this.injectable = !!value; + } + + getParameterNames() { + return this.parameters.map(param => param.name); + } + + getParameterTypes() { + return this.parameters.map(param => param.type); + } + + getParameter(name) { + return this.parameters.find(param => param.name === name) || null; + } + + getSignature() { + const paramStr = this.parameters.map(param => `${param.name}: ${param.type}`).join(', '); + return `${this.name}(${paramStr})`; + } + + hasReturnType() { + return this.returnType !== null; + } + + validate() { + const errors = []; + + if (!this.name || typeof this.name !== 'string') { + errors.push(`Invalid source name: ${this.name}`); + } + + this.parameters.forEach((param, index) => { + const paramErrors = param.validate ? param.validate() : []; + errors.push(...paramErrors.map(err => `Parameter ${index + 1}: ${err}`)); + }); + + return errors; + } + + toString() { + const injectableStr = this.injectable ? '*' : ''; + const providesStr = this.returnType ? ` PROVIDES ${this.returnType}` : ''; + const withinStr = this.within ? ` within ${this.within.value}` : ''; + return `Source(${injectableStr}${this.getSignature()}${providesStr}${withinStr})`; + } +} diff --git a/src/nodes/WithClauseNode.js b/src/nodes/WithClauseNode.js new file mode 100644 index 0000000..281d9ee --- /dev/null +++ b/src/nodes/WithClauseNode.js @@ -0,0 +1,154 @@ +import { BaseNode } from './BaseNode.js'; + +/** + * AST node for with clauses in pattern matching + * Represents: with similarity > 0.7 + */ +export class WithClauseNode extends BaseNode { + constructor(location = null) { + super('WithClause', location); + this.condition = null; // ExpressionNode + this.operator = null; // '>', '>=', '<', '<=', '==', '!=' + this.value = null; // Literal value + } + + /** + * Set the condition for this with clause + * @param {ExpressionNode} condition - Condition to set + */ + setCondition(condition) { + this.condition = condition; + this.addChild(condition); + } + + /** + * Set the operator for this with clause + * @param {string} operator - Operator to set + */ + setOperator(operator) { + this.operator = operator; + } + + /** + * Set the value for this with clause + * @param {*} value - Value to set + */ + setValue(value) { + this.value = value; + } + + /** + * Get the condition expression + * @returns {ExpressionNode|null} Condition expression or null + */ + getCondition() { + return this.condition; + } + + /** + * Get the operator + * @returns {string|null} Operator or null + */ + getOperator() { + return this.operator; + } + + /** + * Get the value + * @returns {*} Value or null + */ + getValue() { + return this.value; + } + + /** + * Check if this is a greater than comparison + * @returns {boolean} True if greater than + */ + isGreaterThan() { + return this.operator === '>'; + } + + /** + * Check if this is a greater than or equal comparison + * @returns {boolean} True if greater than or equal + */ + isGreaterThanOrEqual() { + return this.operator === '>='; + } + + /** + * Check if this is a less than comparison + * @returns {boolean} True if less than + */ + isLessThan() { + return this.operator === '<'; + } + + /** + * Check if this is a less than or equal comparison + * @returns {boolean} True if less than or equal + */ + isLessThanOrEqual() { + return this.operator === '<='; + } + + /** + * Check if this is an equality comparison + * @returns {boolean} True if equality + */ + isEqual() { + return this.operator === '=='; + } + + /** + * Check if this is a not equal comparison + * @returns {boolean} True if not equal + */ + isNotEqual() { + return this.operator === '!='; + } + + /** + * Get the comparison string + * @returns {string} Comparison string + */ + getComparisonString() { + const condStr = this.condition ? this.condition.toString() : 'null'; + const valStr = this.value !== null ? this.value.toString() : 'null'; + return `${condStr} ${this.operator} ${valStr}`; + } + + /** + * Validate the with clause + * @returns {string[]} Array of error messages + */ + validate() { + const errors = []; + + // Validate condition + if (!this.condition) { + errors.push('With clause must have a condition'); + } else { + const condErrors = this.condition.validate ? this.condition.validate() : []; + errors.push(...condErrors); + } + + // Validate operator + const validOperators = ['>', '>=', '<', '<=', '==', '!=']; + if (!this.operator || !validOperators.includes(this.operator)) { + errors.push(`Invalid operator: ${this.operator}`); + } + + // Validate value + if (this.value === null) { + errors.push('With clause must have a value'); + } + + return errors; + } + + toString() { + return `WithClause(${this.getComparisonString()})`; + } +} diff --git a/src/nodes/index.js b/src/nodes/index.js new file mode 100644 index 0000000..77baaf8 --- /dev/null +++ b/src/nodes/index.js @@ -0,0 +1,24 @@ +/** + * AST Node exports + * Central export file for all AST node classes + */ + +export { BaseNode } from './BaseNode.js'; +export { ProgramNode } from './ProgramNode.js'; +export { DefinitionNode } from './DefinitionNode.js'; +export { FieldNode } from './FieldNode.js'; +export { BehaviorNode } from './BehaviorNode.js'; +export { FactNode } from './FactNode.js'; +export { ParameterNode } from './ParameterNode.js'; +export { EvidenceNode } from './EvidenceNode.js'; +export { EvidenceBodyNode } from './EvidenceBodyNode.js'; +export { DirectEvidenceNode } from './DirectEvidenceNode.js'; +export { PatternMatchNode } from './PatternMatchNode.js'; +export { DefeasibleLogicNode } from './DefeasibleLogicNode.js'; +export { FusionNode } from './FusionNode.js'; +export { PredicateNode } from './PredicateNode.js'; +export { ExpressionNode } from './ExpressionNode.js'; +export { WithClauseNode } from './WithClauseNode.js'; +export { MeasureNode } from './MeasureNode.js'; +export { MeasureBodyNode } from './MeasureBodyNode.js'; +export { AggregationNode } from './AggregationNode.js'; diff --git a/src/parser/DSLParser.js b/src/parser/DSLParser.js new file mode 100644 index 0000000..7477947 --- /dev/null +++ b/src/parser/DSLParser.js @@ -0,0 +1,4998 @@ +// @generated by Peggy 5.0.6. +// +// https://peggyjs.org/ + + +class peg$SyntaxError extends SyntaxError { + constructor(message, expected, found, location) { + super(message); + this.expected = expected; + this.found = found; + this.location = location; + this.name = "SyntaxError"; + } + + format(sources) { + let str = "Error: " + this.message; + if (this.location) { + let src = null; + const st = sources.find(s => s.source === this.location.source); + if (st) { + src = st.text.split(/\r\n|\n|\r/g); + } + const s = this.location.start; + const offset_s = (this.location.source && (typeof this.location.source.offset === "function")) + ? this.location.source.offset(s) + : s; + const loc = this.location.source + ":" + offset_s.line + ":" + offset_s.column; + if (src) { + const e = this.location.end; + const filler = "".padEnd(offset_s.line.toString().length, " "); + const line = src[s.line - 1]; + const last = s.line === e.line ? e.column : line.length + 1; + const hatLen = (last - s.column) || 1; + str += "\n --> " + loc + "\n" + + filler + " |\n" + + offset_s.line + " | " + line + "\n" + + filler + " | " + "".padEnd(s.column - 1, " ") + + "".padEnd(hatLen, "^"); + } else { + str += "\n at " + loc; + } + } + return str; + } + + static buildMessage(expected, found) { + function hex(ch) { + return ch.codePointAt(0).toString(16).toUpperCase(); + } + + const nonPrintable = Object.prototype.hasOwnProperty.call(RegExp.prototype, "unicode") + ? new RegExp("[\\p{C}\\p{Mn}\\p{Mc}]", "gu") + : null; + function unicodeEscape(s) { + if (nonPrintable) { + return s.replace(nonPrintable, ch => "\\u{" + hex(ch) + "}"); + } + return s; + } + + function literalEscape(s) { + return unicodeEscape(s + .replace(/\\/g, "\\\\") + .replace(/"/g, "\\\"") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch)) + .replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch))); + } + + function classEscape(s) { + return unicodeEscape(s + .replace(/\\/g, "\\\\") + .replace(/\]/g, "\\]") + .replace(/\^/g, "\\^") + .replace(/-/g, "\\-") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch)) + .replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch))); + } + + const DESCRIBE_EXPECTATION_FNS = { + literal(expectation) { + return "\"" + literalEscape(expectation.text) + "\""; + }, + + class(expectation) { + const escapedParts = expectation.parts.map( + part => (Array.isArray(part) + ? classEscape(part[0]) + "-" + classEscape(part[1]) + : classEscape(part)) + ); + + return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]" + (expectation.unicode ? "u" : ""); + }, + + any() { + return "any character"; + }, + + end() { + return "end of input"; + }, + + other(expectation) { + return expectation.description; + }, + }; + + function describeExpectation(expectation) { + return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation); + } + + function describeExpected(expected) { + const descriptions = expected.map(describeExpectation); + descriptions.sort(); + + if (descriptions.length > 0) { + let j = 1; + for (let i = 1; i < descriptions.length; i++) { + if (descriptions[i - 1] !== descriptions[i]) { + descriptions[j] = descriptions[i]; + j++; + } + } + descriptions.length = j; + } + + switch (descriptions.length) { + case 1: + return descriptions[0]; + + case 2: + return descriptions[0] + " or " + descriptions[1]; + + default: + return descriptions.slice(0, -1).join(", ") + + ", or " + + descriptions[descriptions.length - 1]; + } + } + + function describeFound(found) { + return found ? "\"" + literalEscape(found) + "\"" : "end of input"; + } + + return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found."; + } +} + +function peg$parse(input, options) { + options = options !== undefined ? options : {}; + + const peg$FAILED = {}; + const peg$source = options.grammarSource; + + const peg$startRuleFunctions = { + Program: peg$parseProgram, + }; + let peg$startRuleFunction = peg$parseProgram; + + const peg$c0 = "definition"; + const peg$c1 = "type"; + const peg$c2 = "{"; + const peg$c3 = "}"; + const peg$c4 = ":"; + const peg$c5 = "[]"; + const peg$c6 = "fact"; + const peg$c7 = "relation"; + const peg$c8 = "("; + const peg$c9 = ")"; + const peg$c10 = "evidence"; + const peg$c11 = "!"; + const peg$c12 = "measure"; + const peg$c13 = "return"; + const peg$c14 = "NEVER"; + const peg$c15 = "ALWAYS"; + const peg$c16 = "REQUIRES"; + const peg$c17 = "WHEN"; + const peg$c18 = "UNLESS"; + const peg$c19 = "|"; + const peg$c20 = "fusion"; + const peg$c21 = ","; + const peg$c22 = "*"; + const peg$c23 = "with"; + const peg$c24 = "aggregate"; + const peg$c25 = "USING"; + const peg$c26 = "PROVIDES"; + const peg$c27 = "BEHAVES"; + const peg$c28 = "AS"; + const peg$c29 = "edge"; + const peg$c30 = "transitive"; + const peg$c31 = "hierarchical"; + const peg$c32 = "symmetrical_graph"; + const peg$c33 = "symmetrical"; + const peg$c34 = "limit"; + const peg$c35 = "decaying"; + const peg$c36 = "up"; + const peg$c37 = "down"; + const peg$c38 = "neutral"; + const peg$c39 = "stable"; + const peg$c40 = "hourly"; + const peg$c41 = "daily"; + const peg$c42 = "weekly"; + const peg$c43 = "monthly"; + const peg$c44 = "blurring"; + const peg$c45 = "fixed"; + const peg$c46 = "adaptive"; + const peg$c47 = "confidence"; + const peg$c48 = "confidence_90"; + const peg$c49 = "confidence_95"; + const peg$c50 = "confidence_99"; + const peg$c51 = "ttl"; + const peg$c52 = "CACHE"; + const peg$c53 = "eager"; + const peg$c54 = "lazy"; + const peg$c55 = "||"; + const peg$c56 = "&&"; + const peg$c57 = "is"; + const peg$c58 = "=="; + const peg$c59 = "!="; + const peg$c60 = ">="; + const peg$c61 = "<="; + const peg$c62 = "within"; + const peg$c63 = "NOT"; + const peg$c64 = "."; + const peg$c65 = "\""; + const peg$c66 = "\\"; + const peg$c67 = "'"; + const peg$c68 = "true"; + const peg$c69 = "false"; + const peg$c70 = "//"; + const peg$c71 = "/*"; + const peg$c72 = "*/"; + + const peg$r0 = /^[<>]/; + const peg$r1 = /^[+\-]/; + const peg$r2 = /^[*\/]/; + const peg$r3 = /^["\\]/; + const peg$r4 = /^['\\]/; + const peg$r5 = /^[0-9]/; + const peg$r6 = /^[dhmw]/; + const peg$r7 = /^[a-zA-Z_]/; + const peg$r8 = /^[a-zA-Z0-9_]/; + const peg$r9 = /^[ \t\r\n]/; + const peg$r10 = /^[^\r\n]/; + + const peg$e0 = peg$otherExpectation("A type definition"); + const peg$e1 = peg$literalExpectation("definition", false); + const peg$e2 = peg$literalExpectation("type", false); + const peg$e3 = peg$literalExpectation("{", false); + const peg$e4 = peg$literalExpectation("}", false); + const peg$e5 = peg$literalExpectation(":", false); + const peg$e6 = peg$literalExpectation("[]", false); + const peg$e7 = peg$otherExpectation("A statement of fact (or relation in ADR-000)"); + const peg$e8 = peg$literalExpectation("fact", false); + const peg$e9 = peg$literalExpectation("relation", false); + const peg$e10 = peg$literalExpectation("(", false); + const peg$e11 = peg$literalExpectation(")", false); + const peg$e12 = peg$otherExpectation("An evidence rule"); + const peg$e13 = peg$literalExpectation("evidence", false); + const peg$e14 = peg$literalExpectation("!", false); + const peg$e15 = peg$otherExpectation("A derived measurement or value"); + const peg$e16 = peg$literalExpectation("measure", false); + const peg$e17 = peg$literalExpectation("return", false); + const peg$e18 = peg$literalExpectation("NEVER", false); + const peg$e19 = peg$literalExpectation("ALWAYS", false); + const peg$e20 = peg$literalExpectation("REQUIRES", false); + const peg$e21 = peg$literalExpectation("WHEN", false); + const peg$e22 = peg$literalExpectation("UNLESS", false); + const peg$e23 = peg$literalExpectation("|", false); + const peg$e24 = peg$literalExpectation("fusion", false); + const peg$e25 = peg$literalExpectation(",", false); + const peg$e26 = peg$literalExpectation("*", false); + const peg$e27 = peg$literalExpectation("with", false); + const peg$e28 = peg$literalExpectation("aggregate", false); + const peg$e29 = peg$literalExpectation("USING", false); + const peg$e30 = peg$literalExpectation("PROVIDES", false); + const peg$e31 = peg$literalExpectation("BEHAVES", false); + const peg$e32 = peg$literalExpectation("AS", false); + const peg$e33 = peg$literalExpectation("edge", false); + const peg$e34 = peg$literalExpectation("transitive", false); + const peg$e35 = peg$literalExpectation("hierarchical", false); + const peg$e36 = peg$literalExpectation("symmetrical_graph", false); + const peg$e37 = peg$literalExpectation("symmetrical", false); + const peg$e38 = peg$literalExpectation("limit", false); + const peg$e39 = peg$literalExpectation("decaying", false); + const peg$e40 = peg$literalExpectation("up", false); + const peg$e41 = peg$literalExpectation("down", false); + const peg$e42 = peg$literalExpectation("neutral", false); + const peg$e43 = peg$literalExpectation("stable", false); + const peg$e44 = peg$literalExpectation("hourly", false); + const peg$e45 = peg$literalExpectation("daily", false); + const peg$e46 = peg$literalExpectation("weekly", false); + const peg$e47 = peg$literalExpectation("monthly", false); + const peg$e48 = peg$literalExpectation("blurring", false); + const peg$e49 = peg$literalExpectation("fixed", false); + const peg$e50 = peg$literalExpectation("adaptive", false); + const peg$e51 = peg$literalExpectation("confidence", false); + const peg$e52 = peg$literalExpectation("confidence_90", false); + const peg$e53 = peg$literalExpectation("confidence_95", false); + const peg$e54 = peg$literalExpectation("confidence_99", false); + const peg$e55 = peg$literalExpectation("ttl", false); + const peg$e56 = peg$literalExpectation("CACHE", false); + const peg$e57 = peg$literalExpectation("eager", false); + const peg$e58 = peg$literalExpectation("lazy", false); + const peg$e59 = peg$literalExpectation("||", false); + const peg$e60 = peg$literalExpectation("&&", false); + const peg$e61 = peg$literalExpectation("is", false); + const peg$e62 = peg$literalExpectation("==", false); + const peg$e63 = peg$literalExpectation("!=", false); + const peg$e64 = peg$literalExpectation(">=", false); + const peg$e65 = peg$literalExpectation("<=", false); + const peg$e66 = peg$classExpectation(["<", ">"], false, false, false); + const peg$e67 = peg$literalExpectation("within", false); + const peg$e68 = peg$classExpectation(["+", "-"], false, false, false); + const peg$e69 = peg$classExpectation(["*", "/"], false, false, false); + const peg$e70 = peg$literalExpectation("NOT", false); + const peg$e71 = peg$literalExpectation(".", false); + const peg$e72 = peg$otherExpectation("The non-recursive base for an expression chain"); + const peg$e73 = peg$otherExpectation("A string literal"); + const peg$e74 = peg$literalExpectation("\"", false); + const peg$e75 = peg$classExpectation(["\"", "\\"], false, false, false); + const peg$e76 = peg$anyExpectation(); + const peg$e77 = peg$literalExpectation("\\", false); + const peg$e78 = peg$literalExpectation("'", false); + const peg$e79 = peg$classExpectation(["'", "\\"], false, false, false); + const peg$e80 = peg$otherExpectation("A floating-point number"); + const peg$e81 = peg$classExpectation([["0", "9"]], false, false, false); + const peg$e82 = peg$otherExpectation("An integer"); + const peg$e83 = peg$otherExpectation("A boolean literal"); + const peg$e84 = peg$literalExpectation("true", false); + const peg$e85 = peg$literalExpectation("false", false); + const peg$e86 = peg$otherExpectation("A time duration literal"); + const peg$e87 = peg$classExpectation(["d", "h", "m", "w"], false, false, false); + const peg$e88 = peg$classExpectation([["a", "z"], ["A", "Z"], "_"], false, false, false); + const peg$e89 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"], "_"], false, false, false); + const peg$e90 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false, false); + const peg$e91 = peg$literalExpectation("//", false); + const peg$e92 = peg$classExpectation(["\r", "\n"], true, false, false); + const peg$e93 = peg$literalExpectation("/*", false); + const peg$e94 = peg$literalExpectation("*/", false); + + function peg$f0(statements) { + const allStatements = statements.map(s => s[0]); + return { + type: "Program", + body: allStatements, + definitions: allStatements.filter(s => s.type === "Definition"), + facts: allStatements.filter(s => s.type === "Fact"), + evidence: allStatements.filter(s => s.type === "Evidence"), + measures: allStatements.filter(s => s.type === "Measure") + }; + } + function peg$f1(name, fields) { + return { type: "Definition", name, fields: fields.map(f => f[0]) }; + } + function peg$f2(name, fieldType, isArray, behavior, cache) { + return { + type: "Field", + name, + fieldType, + isArray: !!isArray, + behavior: behavior || null, + cache: cache || null + }; + } + function peg$f3(name, params, behavior, properties, cache, limit) { + return { + type: "Fact", + name, + params: params || [], + behavior: behavior || null, + properties: properties.map(p => p[0]), + cache: cache || null, + limit: limit || null + }; + } + function peg$f4(bang, name, params, limit, body, provides) { + return { + type: "Evidence", + name, + params: params || [], + limit: limit || null, + body, + provides: provides || null, + challenge: !!bang + }; + } + function peg$f5(name, params, body, provides) { + return { + type: "Measure", + name, + params: params || [], + body, + provides: provides || null + }; + } + function peg$f6(statements) { + return { type: "EvidenceBody", statements: statements.map(s => s[0]) }; + } + function peg$f7(statements, returnStmt) { + return { + type: "MeasureBody", + statements: statements.map(s => s[0]), + returnStatement: returnStmt || null + }; + } + function peg$f8(expression) { + return { type: "ReturnStatement", expression }; + } + function peg$f9(type, condition) { + return { type: "DefeasibleLogic", logicType: type, condition }; + } + function peg$f10(condition, defeater) { + return { type: "DefeasibleLogic", logicType: "WHEN", condition, defeater }; + } + function peg$f11(condition) { + return { type: "DefeasibleLogic", logicType: "WHEN", condition }; + } + function peg$f12(predicate, binding, body, limit, withClause) { + return { + type: "PatternMatch", + predicate, + binding: binding || null, + limit: limit || null, + body, + withClause: withClause || null + }; + } + function peg$f13(measure, variable, fusionStrategy, body, limit) { + return { + type: "CollectionProcessing", + measure, + variable, + fusion: fusionStrategy ? { strategy: fusionStrategy[1] } : null, + body, + limit: limit || null + }; + } + function peg$f14(name, args) { + return { type: "Predicate", name, args: args || [] }; + } + function peg$f15(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f16(name) { return { type: "Wildcard", name }; } + function peg$f17(name) { return name; } + function peg$f18(condition) { return condition; } + function peg$f19(strategy, expressions) { + return { type: "Fusion", strategy, expressions }; + } + function peg$f20(expressions, using) { + return { type: "Aggregation", expressions, using: using || null }; + } + function peg$f21(method) { return method; } + function peg$f22(name) { return { type: "TypeName", name }; } + function peg$f23(literal) { return { type: "TypeName", name: literal.value }; } + function peg$f24(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f25(name, paramType, isArray) { + return { type: "Parameter", name, paramType, isArray: !!isArray }; + } + function peg$f26(providesType) { return providesType; } + function peg$f27(behavior) { + return { type: "BehaviorAnnotation", behavior }; + } + function peg$f28() { return "transitive"; } + function peg$f29() { return "symmetrical"; } + function peg$f30(value) { return value; } + function peg$f31(b) { return b; } + function peg$f32(direction, period) { + return { type: "Behavior", behaviorType: "decay", direction, period }; + } + function peg$f33(mode, confidence) { + return { type: "Behavior", behaviorType: "blur", mode, confidence: confidence ? confidence[1] : null }; + } + function peg$f34(duration) { + return { type: "Behavior", behaviorType: "ttl", duration }; + } + function peg$f35(directive) { return directive; } + function peg$f36(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f37(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f38(head, typeName) { + return { type: "BinaryExpression", operator: "is", left: head, right: typeName }; + } + function peg$f39(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f40(head, right) { + return { type: "BinaryExpression", operator: "within", left: head, right }; + } + function peg$f41(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f42(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f43(operator, operand) { return { type: "UnaryExpression", operator: "NOT", operand }; } + function peg$f44(primary, binding) { + if (binding) { + return { type: "BindingAccess", expression: primary, binding }; + } + return primary; + } + function peg$f45(head, tail) { + return tail.reduce((obj, part) => { + return { + type: "AttributeAccess", + object: obj, + attribute: part[3], // The Identifier is the 4th element (index 3) + location: location() + }; + }, head); + } + function peg$f46(expr) { return expr; } + function peg$f47(name, args) { + return { type: "PredicateCall", name, args: args || [], challenge: true }; + } + function peg$f48(name, args) { + return { type: "PredicateCall", name, args: args || [] }; + } + function peg$f49(name) { return { type: "Variable", name }; } + function peg$f50(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f51(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f52(chars) { + return { type: "Literal", value: JSON.parse(text()) }; + } + function peg$f53(chars) { + return { type: "Literal", value: JSON.parse("\"" + chars.map(c => c[0] === '\\' ? c[1] : c[1]).join('') + "\"") }; + } + function peg$f54(value) { return { type: "Literal", value: parseFloat(text()) }; } + function peg$f55(value) { return { type: "Literal", value: parseInt(text(), 10) }; } + function peg$f56(value) { return { type: "Literal", value: value === "true" }; } + function peg$f57(value) { return { type: "Literal", value: text(), unit: text().slice(-1) }; } + function peg$f58(name) { return name; } + let peg$currPos = options.peg$currPos | 0; + let peg$savedPos = peg$currPos; + const peg$posDetailsCache = [{ line: 1, column: 1 }]; + let peg$maxFailPos = peg$currPos; + let peg$maxFailExpected = options.peg$maxFailExpected || []; + let peg$silentFails = options.peg$silentFails | 0; + + let peg$result; + + if (options.startRule) { + if (!(options.startRule in peg$startRuleFunctions)) { + throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); + } + + peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; + } + + function text() { + return input.substring(peg$savedPos, peg$currPos); + } + + function offset() { + return peg$savedPos; + } + + function range() { + return { + source: peg$source, + start: peg$savedPos, + end: peg$currPos, + }; + } + + function location() { + return peg$computeLocation(peg$savedPos, peg$currPos); + } + + function expected(description, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildStructuredError( + [peg$otherExpectation(description)], + input.substring(peg$savedPos, peg$currPos), + location + ); + } + + function error(message, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildSimpleError(message, location); + } + + function peg$getUnicode(pos = peg$currPos) { + const cp = input.codePointAt(pos); + if (cp === undefined) { + return ""; + } + return String.fromCodePoint(cp); + } + + function peg$literalExpectation(text, ignoreCase) { + return { type: "literal", text, ignoreCase }; + } + + function peg$classExpectation(parts, inverted, ignoreCase, unicode) { + return { type: "class", parts, inverted, ignoreCase, unicode }; + } + + function peg$anyExpectation() { + return { type: "any" }; + } + + function peg$endExpectation() { + return { type: "end" }; + } + + function peg$otherExpectation(description) { + return { type: "other", description }; + } + + function peg$computePosDetails(pos) { + let details = peg$posDetailsCache[pos]; + let p; + + if (details) { + return details; + } else { + if (pos >= peg$posDetailsCache.length) { + p = peg$posDetailsCache.length - 1; + } else { + p = pos; + while (!peg$posDetailsCache[--p]) {} + } + + details = peg$posDetailsCache[p]; + details = { + line: details.line, + column: details.column, + }; + + while (p < pos) { + if (input.charCodeAt(p) === 10) { + details.line++; + details.column = 1; + } else { + details.column++; + } + + p++; + } + + peg$posDetailsCache[pos] = details; + + return details; + } + } + + function peg$computeLocation(startPos, endPos, offset) { + const startPosDetails = peg$computePosDetails(startPos); + const endPosDetails = peg$computePosDetails(endPos); + + const res = { + source: peg$source, + start: { + offset: startPos, + line: startPosDetails.line, + column: startPosDetails.column, + }, + end: { + offset: endPos, + line: endPosDetails.line, + column: endPosDetails.column, + }, + }; + if (offset && peg$source && (typeof peg$source.offset === "function")) { + res.start = peg$source.offset(res.start); + res.end = peg$source.offset(res.end); + } + return res; + } + + function peg$fail(expected) { + if (peg$currPos < peg$maxFailPos) { return; } + + if (peg$currPos > peg$maxFailPos) { + peg$maxFailPos = peg$currPos; + peg$maxFailExpected = []; + } + + peg$maxFailExpected.push(expected); + } + + function peg$buildSimpleError(message, location) { + return new peg$SyntaxError(message, null, null, location); + } + + function peg$buildStructuredError(expected, found, location) { + return new peg$SyntaxError( + peg$SyntaxError.buildMessage(expected, found), + expected, + found, + location + ); + } + + function peg$parseProgram() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parse_(); + s2 = []; + s3 = peg$currPos; + s4 = peg$parseStatement(); + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parseStatement(); + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + s3 = peg$parse_(); + peg$savedPos = s0; + s0 = peg$f0(s2); + + return s0; + } + + function peg$parseStatement() { + let s0; + + s0 = peg$parseDefinition(); + if (s0 === peg$FAILED) { + s0 = peg$parseFact(); + if (s0 === peg$FAILED) { + s0 = peg$parseEvidence(); + if (s0 === peg$FAILED) { + s0 = peg$parseMeasure(); + } + } + } + + return s0; + } + + function peg$parseDefinition() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 10) === peg$c0) { + s1 = peg$c0; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c1) { + s1 = peg$c1; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = []; + s8 = peg$currPos; + s9 = peg$parseField(); + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + s9 = [s9, s10]; + s8 = s9; + } else { + peg$currPos = s8; + s8 = peg$FAILED; + } + while (s8 !== peg$FAILED) { + s7.push(s8); + s8 = peg$currPos; + s9 = peg$parseField(); + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + s9 = [s9, s10]; + s8 = s9; + } else { + peg$currPos = s8; + s8 = peg$FAILED; + } + } + if (input.charCodeAt(peg$currPos) === 125) { + s8 = peg$c3; + peg$currPos++; + } else { + s8 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s8 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f1(s3, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + + return s0; + } + + function peg$parseField() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 58) { + s3 = peg$c4; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e5); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseIdentifier(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c5) { + s7 = peg$c5; + peg$currPos += 2; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } + if (s7 === peg$FAILED) { + s7 = null; + } + s8 = peg$parse_(); + s9 = peg$parseBehavior(); + if (s9 === peg$FAILED) { + s9 = null; + } + s10 = peg$parse_(); + s11 = peg$parseCacheDirective(); + if (s11 === peg$FAILED) { + s11 = null; + } + peg$savedPos = s0; + s0 = peg$f2(s1, s5, s7, s9, s11); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseFact() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c6) { + s1 = peg$c6; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e8); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c7) { + s1 = peg$c7; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e9); } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s5 = peg$c8; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseParameterList(); + if (s7 === peg$FAILED) { + s7 = null; + } + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s9 = peg$c9; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + s11 = peg$parseBehaviorAnnotation(); + if (s11 === peg$FAILED) { + s11 = null; + } + s12 = peg$parse_(); + s13 = []; + s14 = peg$currPos; + s15 = peg$parseFactProperty(); + if (s15 !== peg$FAILED) { + s16 = peg$parse_(); + s15 = [s15, s16]; + s14 = s15; + } else { + peg$currPos = s14; + s14 = peg$FAILED; + } + while (s14 !== peg$FAILED) { + s13.push(s14); + s14 = peg$currPos; + s15 = peg$parseFactProperty(); + if (s15 !== peg$FAILED) { + s16 = peg$parse_(); + s15 = [s15, s16]; + s14 = s15; + } else { + peg$currPos = s14; + s14 = peg$FAILED; + } + } + s14 = peg$parseCacheDirective(); + if (s14 === peg$FAILED) { + s14 = null; + } + s15 = peg$parse_(); + s16 = peg$parseLimit(); + if (s16 === peg$FAILED) { + s16 = null; + } + peg$savedPos = s0; + s0 = peg$f3(s3, s7, s11, s13, s14, s16); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e7); } + } + + return s0; + } + + function peg$parseEvidence() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c10) { + s1 = peg$c10; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e13); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 33) { + s3 = peg$c11; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s3 === peg$FAILED) { + s3 = null; + } + s4 = peg$parseIdentifier(); + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s6 = peg$c8; + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s6 !== peg$FAILED) { + s7 = peg$parse_(); + s8 = peg$parseParameterList(); + if (s8 === peg$FAILED) { + s8 = null; + } + s9 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s10 = peg$c9; + peg$currPos++; + } else { + s10 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s10 !== peg$FAILED) { + s11 = peg$parse_(); + s12 = peg$parseLimit(); + if (s12 === peg$FAILED) { + s12 = null; + } + s13 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s14 = peg$c2; + peg$currPos++; + } else { + s14 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s14 !== peg$FAILED) { + s15 = peg$parse_(); + s16 = peg$parseEvidenceBody(); + s17 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s18 = peg$c3; + peg$currPos++; + } else { + s18 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s18 !== peg$FAILED) { + s19 = peg$parse_(); + s20 = peg$parseProvides(); + if (s20 === peg$FAILED) { + s20 = null; + } + peg$savedPos = s0; + s0 = peg$f4(s3, s4, s8, s12, s16, s20); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + + return s0; + } + + function peg$parseMeasure() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 7) === peg$c12) { + s1 = peg$c12; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s5 = peg$c8; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseParameterList(); + if (s7 === peg$FAILED) { + s7 = null; + } + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s9 = peg$c9; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s11 = peg$c2; + peg$currPos++; + } else { + s11 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s11 !== peg$FAILED) { + s12 = peg$parse_(); + s13 = peg$parseMeasureBody(); + s14 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s15 = peg$c3; + peg$currPos++; + } else { + s15 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s15 !== peg$FAILED) { + s16 = peg$parse_(); + s17 = peg$parseProvides(); + if (s17 === peg$FAILED) { + s17 = null; + } + peg$savedPos = s0; + s0 = peg$f5(s3, s7, s13, s17); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e15); } + } + + return s0; + } + + function peg$parseEvidenceBody() { + let s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = []; + s2 = peg$currPos; + s3 = peg$parseEvidenceStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$currPos; + s3 = peg$parseEvidenceStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + } + peg$savedPos = s0; + s1 = peg$f6(s1); + s0 = s1; + + return s0; + } + + function peg$parseEvidenceStatement() { + let s0; + + s0 = peg$parseDefeasibleLogic(); + if (s0 === peg$FAILED) { + s0 = peg$parseFusion(); + if (s0 === peg$FAILED) { + s0 = peg$parseCollectionProcessing(); + if (s0 === peg$FAILED) { + s0 = peg$parsePatternMatch(); + if (s0 === peg$FAILED) { + s0 = peg$parseLogicalOr(); + } + } + } + } + + return s0; + } + + function peg$parseMeasureBody() { + let s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = []; + s2 = peg$currPos; + s3 = peg$parseMeasureStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$currPos; + s3 = peg$parseMeasureStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + } + s2 = peg$parseReturnStatement(); + if (s2 === peg$FAILED) { + s2 = null; + } + peg$savedPos = s0; + s0 = peg$f7(s1, s2); + + return s0; + } + + function peg$parseMeasureStatement() { + let s0; + + s0 = peg$parseFusion(); + if (s0 === peg$FAILED) { + s0 = peg$parseAggregation(); + if (s0 === peg$FAILED) { + s0 = peg$parsePatternMatch(); + if (s0 === peg$FAILED) { + s0 = peg$parseLogicalOr(); + } + } + } + + return s0; + } + + function peg$parseReturnStatement() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c13) { + s1 = peg$c13; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f8(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseDefeasibleLogic() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c14) { + s1 = peg$c14; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c15) { + s1 = peg$c15; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e19); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c16) { + s1 = peg$c16; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e20); } + } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f9(s1, s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c17) { + s1 = peg$c17; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c18) { + s5 = peg$c18; + peg$currPos += 6; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse__(); + if (s6 !== peg$FAILED) { + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f10(s3, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c17) { + s1 = peg$c17; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f11(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + } + + return s0; + } + + function peg$parsePatternMatch() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13; + + s0 = peg$currPos; + s1 = peg$parsePatternPredicate(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseBindingClause(); + if (s3 === peg$FAILED) { + s3 = null; + } + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseEvidenceBody(); + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s9 = peg$c3; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + s11 = peg$parseLimit(); + if (s11 === peg$FAILED) { + s11 = null; + } + s12 = peg$parse_(); + s13 = peg$parseWithClause(); + if (s13 === peg$FAILED) { + s13 = null; + } + peg$savedPos = s0; + s0 = peg$f12(s1, s3, s7, s11, s13); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseCollectionProcessing() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17; + + s0 = peg$currPos; + s1 = peg$parseLogicalOr(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 124) { + s3 = peg$c19; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseIdentifier(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 124) { + s7 = peg$c19; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + s9 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c20) { + s10 = peg$c20; + peg$currPos += 6; + } else { + s10 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + if (s10 !== peg$FAILED) { + s11 = peg$parse__(); + if (s11 !== peg$FAILED) { + s12 = peg$parseIdentifier(); + if (s12 !== peg$FAILED) { + s10 = [s10, s11, s12]; + s9 = s10; + } else { + peg$currPos = s9; + s9 = peg$FAILED; + } + } else { + peg$currPos = s9; + s9 = peg$FAILED; + } + } else { + peg$currPos = s9; + s9 = peg$FAILED; + } + if (s9 === peg$FAILED) { + s9 = null; + } + s10 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s11 = peg$c2; + peg$currPos++; + } else { + s11 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s11 !== peg$FAILED) { + s12 = peg$parse_(); + s13 = peg$parseEvidenceBody(); + if (s13 !== peg$FAILED) { + s14 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s15 = peg$c3; + peg$currPos++; + } else { + s15 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s15 !== peg$FAILED) { + s16 = peg$parse_(); + s17 = peg$parseLimit(); + if (s17 === peg$FAILED) { + s17 = null; + } + peg$savedPos = s0; + s0 = peg$f13(s1, s5, s9, s13, s17); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePatternPredicate() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s3 = peg$c8; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parsePatternArgumentList(); + if (s5 === peg$FAILED) { + s5 = null; + } + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s7 = peg$c9; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s7 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f14(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePatternArgumentList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parsePatternArgument(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parsePatternArgument(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parsePatternArgument(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f15(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePatternArgument() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 42) { + s1 = peg$c22; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e26); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f16(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseLogicalOr(); + } + + return s0; + } + + function peg$parseBindingClause() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 124) { + s1 = peg$c19; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 124) { + s5 = peg$c19; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f17(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseWithClause() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c23) { + s1 = peg$c23; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f18(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseFusion() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c20) { + s1 = peg$c20; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseExpressionList(); + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s9 = peg$c3; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s9 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f19(s3, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseAggregation() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 9) === peg$c24) { + s1 = peg$c24; + peg$currPos += 9; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e28); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s3 = peg$c2; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseExpressionList(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s7 = peg$c3; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + s9 = peg$parseUsing(); + if (s9 === peg$FAILED) { + s9 = null; + } + peg$savedPos = s0; + s0 = peg$f20(s5, s9); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseUsing() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c25) { + s1 = peg$c25; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f21(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseTypeName() { + let s0, s1; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f22(s1); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseString(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f23(s1); + } + s0 = s1; + } + + return s0; + } + + function peg$parseParameterList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseParameter(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseParameter(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseParameter(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f24(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseParameter() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 58) { + s3 = peg$c4; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e5); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseIdentifier(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c5) { + s7 = peg$c5; + peg$currPos += 2; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } + if (s7 === peg$FAILED) { + s7 = null; + } + peg$savedPos = s0; + s0 = peg$f25(s1, s5, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseProvides() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c26) { + s1 = peg$c26; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e30); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f26(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseBehaviorAnnotation() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 7) === peg$c27) { + s1 = peg$c27; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c28) { + s3 = peg$c28; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c29) { + s5 = peg$c29; + peg$currPos += 4; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c30) { + s5 = peg$c30; + peg$currPos += 10; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 12) === peg$c31) { + s5 = peg$c31; + peg$currPos += 12; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e35); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 17) === peg$c32) { + s5 = peg$c32; + peg$currPos += 17; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e36); } + } + } + } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f27(s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseFactProperty() { + let s0, s1; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 10) === peg$c30) { + s1 = peg$c30; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f28(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 11) === peg$c33) { + s1 = peg$c33; + peg$currPos += 11; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f29(); + } + s0 = s1; + } + + return s0; + } + + function peg$parseLimit() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c34) { + s1 = peg$c34; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e38); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseInteger(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f30(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseBehavior() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 7) === peg$c27) { + s1 = peg$c27; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s3 = peg$c2; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseDecayBehavior(); + if (s5 === peg$FAILED) { + s5 = peg$parseBlurBehavior(); + if (s5 === peg$FAILED) { + s5 = peg$parseTTLBehavior(); + } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s7 = peg$c3; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s7 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f31(s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseDecayBehavior() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c35) { + s1 = peg$c35; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e39); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c36) { + s3 = peg$c36; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e40); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c37) { + s3 = peg$c37; + peg$currPos += 4; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e41); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c38) { + s3 = peg$c38; + peg$currPos += 7; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e42); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c39) { + s3 = peg$c39; + peg$currPos += 6; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e43); } + } + } + } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c40) { + s5 = peg$c40; + peg$currPos += 6; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e44); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c41) { + s5 = peg$c41; + peg$currPos += 5; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e45); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c42) { + s5 = peg$c42; + peg$currPos += 6; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e46); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c43) { + s5 = peg$c43; + peg$currPos += 7; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e47); } + } + } + } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f32(s3, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseBlurBehavior() { + let s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c44) { + s1 = peg$c44; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e48); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c45) { + s3 = peg$c45; + peg$currPos += 5; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e49); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c46) { + s3 = peg$c46; + peg$currPos += 8; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e50); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c47) { + s3 = peg$c47; + peg$currPos += 10; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e51); } + } + } + } + if (s3 !== peg$FAILED) { + s4 = peg$currPos; + s5 = peg$parse__(); + if (s5 !== peg$FAILED) { + if (input.substr(peg$currPos, 13) === peg$c48) { + s6 = peg$c48; + peg$currPos += 13; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e52); } + } + if (s6 === peg$FAILED) { + if (input.substr(peg$currPos, 13) === peg$c49) { + s6 = peg$c49; + peg$currPos += 13; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e53); } + } + if (s6 === peg$FAILED) { + if (input.substr(peg$currPos, 13) === peg$c50) { + s6 = peg$c50; + peg$currPos += 13; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e54); } + } + } + } + if (s6 !== peg$FAILED) { + s5 = [s5, s6]; + s4 = s5; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 === peg$FAILED) { + s4 = null; + } + peg$savedPos = s0; + s0 = peg$f33(s3, s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseTTLBehavior() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 3) === peg$c51) { + s1 = peg$c51; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e55); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseDuration(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f34(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseCacheDirective() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c52) { + s1 = peg$c52; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e56); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c53) { + s3 = peg$c53; + peg$currPos += 5; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e57); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c54) { + s3 = peg$c54; + peg$currPos += 4; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e58); } + } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f35(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLogicalOr() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseLogicalAnd(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c55) { + s5 = peg$c55; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e59); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalAnd(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c55) { + s5 = peg$c55; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e59); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalAnd(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f36(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLogicalAnd() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseComparison(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c56) { + s5 = peg$c56; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e60); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c56) { + s5 = peg$c56; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e60); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f37(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseComparison() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseTemporalComparison(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c57) { + s3 = peg$c57; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e61); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + s5 = peg$parseTypeName(); + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f38(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseTemporalComparison(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c58) { + s5 = peg$c58; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e62); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c59) { + s5 = peg$c59; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e63); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c60) { + s5 = peg$c60; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e64); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c61) { + s5 = peg$c61; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e65); } + } + if (s5 === peg$FAILED) { + s5 = input.charAt(peg$currPos); + if (peg$r0.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e66); } + } + } + } + } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseTemporalComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c58) { + s5 = peg$c58; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e62); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c59) { + s5 = peg$c59; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e63); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c60) { + s5 = peg$c60; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e64); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c61) { + s5 = peg$c61; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e65); } + } + if (s5 === peg$FAILED) { + s5 = input.charAt(peg$currPos); + if (peg$r0.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e66); } + } + } + } + } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseTemporalComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f39(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + function peg$parseTemporalComparison() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseAddition(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.substr(peg$currPos, 6) === peg$c62) { + s3 = peg$c62; + peg$currPos += 6; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e67); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + s5 = peg$parseDuration(); + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f40(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseAddition(); + } + + return s0; + } + + function peg$parseAddition() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseMultiplication(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + s5 = input.charAt(peg$currPos); + if (peg$r1.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e68); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseMultiplication(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + s5 = input.charAt(peg$currPos); + if (peg$r1.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e68); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseMultiplication(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f41(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseMultiplication() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseUnary(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + s5 = input.charAt(peg$currPos); + if (peg$r2.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e69); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseUnary(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + s5 = input.charAt(peg$currPos); + if (peg$r2.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e69); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseUnary(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f42(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseUnary() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 3) === peg$c63) { + s1 = peg$c63; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e70); } + } + if (s1 === peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 33) { + s1 = peg$c11; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseUnary(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f43(s1, s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parsePostfix(); + } + + return s0; + } + + function peg$parsePostfix() { + let s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$parseAttributeAccess(); + if (s1 === peg$FAILED) { + s1 = peg$parsePrimaryTerm(); + } + if (s1 !== peg$FAILED) { + s2 = peg$parseBindingClause(); + if (s2 === peg$FAILED) { + s2 = null; + } + peg$savedPos = s0; + s0 = peg$f44(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseAttributeAccess() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parsePrimaryTerm(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 46) { + s5 = peg$c64; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e71); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseIdentifier(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 46) { + s5 = peg$c64; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e71); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseIdentifier(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f45(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePrimaryTerm() { + let s0, s1, s2, s3, s4, s5; + + peg$silentFails++; + s0 = peg$parseChallengePredicate(); + if (s0 === peg$FAILED) { + s0 = peg$parseLiteral(); + if (s0 === peg$FAILED) { + s0 = peg$parsePredicateCall(); + if (s0 === peg$FAILED) { + s0 = peg$parseVariable(); + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 40) { + s1 = peg$c8; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s5 = peg$c9; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f46(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + } + } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e72); } + } + + return s0; + } + + function peg$parseChallengePredicate() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 33) { + s1 = peg$c11; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseIdentifier(); + if (s2 !== peg$FAILED) { + s3 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s4 = peg$c8; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + s6 = peg$parseArgumentList(); + if (s6 === peg$FAILED) { + s6 = null; + } + s7 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s8 = peg$c9; + peg$currPos++; + } else { + s8 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s8 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f47(s2, s6); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePredicateCall() { + let s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 40) { + s2 = peg$c8; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s2 !== peg$FAILED) { + s3 = peg$parse_(); + s4 = peg$parseArgumentList(); + if (s4 === peg$FAILED) { + s4 = null; + } + s5 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s6 = peg$c9; + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s6 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f48(s1, s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseVariable() { + let s0, s1; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f49(s1); + } + s0 = s1; + + return s0; + } + + function peg$parseArgumentList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseLogicalOr(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f50(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseExpressionList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseLogicalOr(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f51(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLiteral() { + let s0; + + s0 = peg$parseString(); + if (s0 === peg$FAILED) { + s0 = peg$parseFloat(); + if (s0 === peg$FAILED) { + s0 = peg$parseInteger(); + if (s0 === peg$FAILED) { + s0 = peg$parseBoolean(); + if (s0 === peg$FAILED) { + s0 = peg$parseDuration(); + } + } + } + } + + return s0; + } + + function peg$parseString() { + let s0, s1, s2, s3, s4, s5; + + peg$silentFails++; + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 34) { + s1 = peg$c65; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e74); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r3.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e75); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c66; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e77); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r3.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e75); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c66; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e77); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } + if (input.charCodeAt(peg$currPos) === 34) { + s3 = peg$c65; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e74); } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f52(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 39) { + s1 = peg$c67; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e78); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r4.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c66; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e77); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r4.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c66; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e77); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } + if (input.charCodeAt(peg$currPos) === 39) { + s3 = peg$c67; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e78); } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f53(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e73); } + } + + return s0; + } + + function peg$parseFloat() { + let s0, s1, s2, s3, s4, s5; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$currPos; + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 46) { + s3 = peg$c64; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e71); } + } + if (s3 !== peg$FAILED) { + s4 = []; + s5 = input.charAt(peg$currPos); + if (peg$r5.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + if (s5 !== peg$FAILED) { + while (s5 !== peg$FAILED) { + s4.push(s5); + s5 = input.charAt(peg$currPos); + if (peg$r5.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + } + } else { + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + s2 = [s2, s3, s4]; + s1 = s2; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f54(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e80); } + } + + return s0; + } + + function peg$parseInteger() { + let s0, s1, s2; + + peg$silentFails++; + s0 = peg$currPos; + s1 = []; + s2 = input.charAt(peg$currPos); + if (peg$r5.test(s2)) { + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = input.charAt(peg$currPos); + if (peg$r5.test(s2)) { + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f55(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e82); } + } + + return s0; + } + + function peg$parseBoolean() { + let s0, s1; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c68) { + s1 = peg$c68; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c69) { + s1 = peg$c69; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e85); } + } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f56(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e83); } + } + + return s0; + } + + function peg$parseDuration() { + let s0, s1, s2, s3; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$currPos; + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = input.charAt(peg$currPos); + if (peg$r6.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e87); } + } + if (s3 !== peg$FAILED) { + s2 = [s2, s3]; + s1 = s2; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f57(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e86); } + } + + return s0; + } + + function peg$parseIdentifier() { + let s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + s2 = peg$parseKeyword(); + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = undefined; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + s3 = peg$currPos; + s4 = input.charAt(peg$currPos); + if (peg$r7.test(s4)) { + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e88); } + } + if (s4 !== peg$FAILED) { + s5 = []; + s6 = input.charAt(peg$currPos); + if (peg$r8.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e89); } + } + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = input.charAt(peg$currPos); + if (peg$r8.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e89); } + } + } + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + s2 = input.substring(s2, peg$currPos); + } else { + s2 = s3; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f58(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseKeyword() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 10) === peg$c0) { + s1 = peg$c0; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c1) { + s1 = peg$c1; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c6) { + s1 = peg$c6; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e8); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c7) { + s1 = peg$c7; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e9); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c10) { + s1 = peg$c10; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e13); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c12) { + s1 = peg$c12; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c27) { + s1 = peg$c27; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c28) { + s1 = peg$c28; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c52) { + s1 = peg$c52; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e56); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c35) { + s1 = peg$c35; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e39); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c44) { + s1 = peg$c44; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e48); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 3) === peg$c51) { + s1 = peg$c51; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e55); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c30) { + s1 = peg$c30; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 11) === peg$c33) { + s1 = peg$c33; + peg$currPos += 11; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 12) === peg$c31) { + s1 = peg$c31; + peg$currPos += 12; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e35); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 17) === peg$c32) { + s1 = peg$c32; + peg$currPos += 17; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e36); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c29) { + s1 = peg$c29; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c34) { + s1 = peg$c34; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e38); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c26) { + s1 = peg$c26; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e30); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c20) { + s1 = peg$c20; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 9) === peg$c24) { + s1 = peg$c24; + peg$currPos += 9; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e28); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c25) { + s1 = peg$c25; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c14) { + s1 = peg$c14; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c15) { + s1 = peg$c15; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e19); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c17) { + s1 = peg$c17; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c18) { + s1 = peg$c18; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c16) { + s1 = peg$c16; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e20); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c23) { + s1 = peg$c23; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c68) { + s1 = peg$c68; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c69) { + s1 = peg$c69; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e85); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 3) === peg$c63) { + s1 = peg$c63; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e70); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c62) { + s1 = peg$c62; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e67); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c13) { + s1 = peg$c13; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c57) { + s1 = peg$c57; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e61); } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + peg$silentFails++; + s3 = input.charAt(peg$currPos); + if (peg$r8.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e89); } + } + peg$silentFails--; + if (s3 === peg$FAILED) { + s2 = undefined; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parse_() { + let s0, s1; + + s0 = []; + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + } + + return s0; + } + + function peg$parse__() { + let s0, s1; + + s0 = []; + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + if (s1 !== peg$FAILED) { + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + } + } else { + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseWhiteSpace() { + let s0; + + s0 = input.charAt(peg$currPos); + if (peg$r9.test(s0)) { + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e90); } + } + + return s0; + } + + function peg$parseComment() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c70) { + s1 = peg$c70; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e91); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r10.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e92); } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r10.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e92); } + } + } + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c71) { + s1 = peg$c71; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e93); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c72) { + s5 = peg$c72; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e94); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c72) { + s5 = peg$c72; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e94); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + if (input.substr(peg$currPos, 2) === peg$c72) { + s3 = peg$c72; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e94); } + } + if (s3 !== peg$FAILED) { + s1 = [s1, s2, s3]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + + // The location() function provides line/column info for error reporting. + // The text() function returns the matched text for a rule. + + // Helper function to build a left-associative binary expression tree. + function buildLeftAssoc(head, tail) { + return tail.reduce((result, element) => { + return { + type: "BinaryExpression", + operator: element[1], + left: result, + right: element[3], + location: location() + }; + }, head); + } + + peg$result = peg$startRuleFunction(); + + const peg$success = (peg$result !== peg$FAILED && peg$currPos === input.length); + function peg$throw() { + if (peg$result !== peg$FAILED && peg$currPos < input.length) { + peg$fail(peg$endExpectation()); + } + + throw peg$buildStructuredError( + peg$maxFailExpected, + peg$maxFailPos < input.length ? peg$getUnicode(peg$maxFailPos) : null, + peg$maxFailPos < input.length + ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) + : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) + ); + } + if (options.peg$library) { + return /** @type {any} */ ({ + peg$result, + peg$currPos, + peg$FAILED, + peg$maxFailExpected, + peg$maxFailPos, + peg$success, + peg$throw: peg$success ? undefined : peg$throw, + }); + } + if (peg$success) { + return peg$result; + } else { + peg$throw(); + } +} + +const peg$allowedStartRules = [ + "Program" +]; + +export { + peg$allowedStartRules as StartRules, + peg$SyntaxError as SyntaxError, + peg$parse as parse +}; diff --git a/src/parser/DslParser.js b/src/parser/DslParser.js new file mode 100644 index 0000000..f7260eb --- /dev/null +++ b/src/parser/DslParser.js @@ -0,0 +1,4998 @@ +// @generated by Peggy 5.1.0. +// +// https://peggyjs.org/ + + +class peg$SyntaxError extends SyntaxError { + constructor(message, expected, found, location) { + super(message); + this.expected = expected; + this.found = found; + this.location = location; + this.name = "SyntaxError"; + } + + format(sources) { + let str = "Error: " + this.message; + if (this.location) { + let src = null; + const st = sources.find(s => s.source === this.location.source); + if (st) { + src = st.text.split(/\r\n|\n|\r/g); + } + const s = this.location.start; + const offset_s = (this.location.source && (typeof this.location.source.offset === "function")) + ? this.location.source.offset(s) + : s; + const loc = this.location.source + ":" + offset_s.line + ":" + offset_s.column; + if (src) { + const e = this.location.end; + const filler = "".padEnd(offset_s.line.toString().length, " "); + const line = src[s.line - 1]; + const last = s.line === e.line ? e.column : line.length + 1; + const hatLen = (last - s.column) || 1; + str += "\n --> " + loc + "\n" + + filler + " |\n" + + offset_s.line + " | " + line + "\n" + + filler + " | " + "".padEnd(s.column - 1, " ") + + "".padEnd(hatLen, "^"); + } else { + str += "\n at " + loc; + } + } + return str; + } + + static buildMessage(expected, found) { + function hex(ch) { + return ch.codePointAt(0).toString(16).toUpperCase(); + } + + const nonPrintable = Object.prototype.hasOwnProperty.call(RegExp.prototype, "unicode") + ? new RegExp("[\\p{C}\\p{Mn}\\p{Mc}]", "gu") + : null; + function unicodeEscape(s) { + if (nonPrintable) { + return s.replace(nonPrintable, ch => "\\u{" + hex(ch) + "}"); + } + return s; + } + + function literalEscape(s) { + return unicodeEscape(s + .replace(/\\/g, "\\\\") + .replace(/"/g, "\\\"") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch)) + .replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch))); + } + + function classEscape(s) { + return unicodeEscape(s + .replace(/\\/g, "\\\\") + .replace(/\]/g, "\\]") + .replace(/\^/g, "\\^") + .replace(/-/g, "\\-") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch)) + .replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch))); + } + + const DESCRIBE_EXPECTATION_FNS = { + literal(expectation) { + return "\"" + literalEscape(expectation.text) + "\""; + }, + + class(expectation) { + const escapedParts = expectation.parts.map( + part => (Array.isArray(part) + ? classEscape(part[0]) + "-" + classEscape(part[1]) + : classEscape(part)) + ); + + return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]" + (expectation.unicode ? "u" : ""); + }, + + any() { + return "any character"; + }, + + end() { + return "end of input"; + }, + + other(expectation) { + return expectation.description; + }, + }; + + function describeExpectation(expectation) { + return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation); + } + + function describeExpected(expected) { + const descriptions = expected.map(describeExpectation); + descriptions.sort(); + + if (descriptions.length > 0) { + let j = 1; + for (let i = 1; i < descriptions.length; i++) { + if (descriptions[i - 1] !== descriptions[i]) { + descriptions[j] = descriptions[i]; + j++; + } + } + descriptions.length = j; + } + + switch (descriptions.length) { + case 1: + return descriptions[0]; + + case 2: + return descriptions[0] + " or " + descriptions[1]; + + default: + return descriptions.slice(0, -1).join(", ") + + ", or " + + descriptions[descriptions.length - 1]; + } + } + + function describeFound(found) { + return found ? "\"" + literalEscape(found) + "\"" : "end of input"; + } + + return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found."; + } +} + +function peg$parse(input, options) { + options = options !== undefined ? options : {}; + + const peg$FAILED = {}; + const peg$source = options.grammarSource; + + const peg$startRuleFunctions = { + Program: peg$parseProgram, + }; + let peg$startRuleFunction = peg$parseProgram; + + const peg$c0 = "definition"; + const peg$c1 = "type"; + const peg$c2 = "{"; + const peg$c3 = "}"; + const peg$c4 = ":"; + const peg$c5 = "[]"; + const peg$c6 = "fact"; + const peg$c7 = "relation"; + const peg$c8 = "("; + const peg$c9 = ")"; + const peg$c10 = "evidence"; + const peg$c11 = "*"; + const peg$c12 = "measure"; + const peg$c13 = "return"; + const peg$c14 = "NEVER"; + const peg$c15 = "ALWAYS"; + const peg$c16 = "REQUIRES"; + const peg$c17 = "WHEN"; + const peg$c18 = "UNLESS"; + const peg$c19 = "|"; + const peg$c20 = "fusion"; + const peg$c21 = ","; + const peg$c22 = "with"; + const peg$c23 = "aggregate"; + const peg$c24 = "USING"; + const peg$c25 = "PROVIDES"; + const peg$c26 = "BEHAVES"; + const peg$c27 = "AS"; + const peg$c28 = "edge"; + const peg$c29 = "transitive"; + const peg$c30 = "hierarchical"; + const peg$c31 = "symmetrical_graph"; + const peg$c32 = "symmetrical"; + const peg$c33 = "limit"; + const peg$c34 = "decaying"; + const peg$c35 = "up"; + const peg$c36 = "down"; + const peg$c37 = "neutral"; + const peg$c38 = "stable"; + const peg$c39 = "hourly"; + const peg$c40 = "daily"; + const peg$c41 = "weekly"; + const peg$c42 = "monthly"; + const peg$c43 = "blurring"; + const peg$c44 = "fixed"; + const peg$c45 = "adaptive"; + const peg$c46 = "confidence"; + const peg$c47 = "confidence_90"; + const peg$c48 = "confidence_95"; + const peg$c49 = "confidence_99"; + const peg$c50 = "ttl"; + const peg$c51 = "CACHE"; + const peg$c52 = "eager"; + const peg$c53 = "lazy"; + const peg$c54 = "||"; + const peg$c55 = "&&"; + const peg$c56 = "is"; + const peg$c57 = "=="; + const peg$c58 = "!="; + const peg$c59 = ">="; + const peg$c60 = "<="; + const peg$c61 = "within"; + const peg$c62 = "NOT"; + const peg$c63 = "!"; + const peg$c64 = "."; + const peg$c65 = "\""; + const peg$c66 = "\\"; + const peg$c67 = "'"; + const peg$c68 = "true"; + const peg$c69 = "false"; + const peg$c70 = "//"; + const peg$c71 = "/*"; + const peg$c72 = "*/"; + + const peg$r0 = /^[<>]/; + const peg$r1 = /^[+\-]/; + const peg$r2 = /^[*\/]/; + const peg$r3 = /^["\\]/; + const peg$r4 = /^['\\]/; + const peg$r5 = /^[0-9]/; + const peg$r6 = /^[dhmw]/; + const peg$r7 = /^[a-zA-Z_]/; + const peg$r8 = /^[a-zA-Z0-9_]/; + const peg$r9 = /^[ \t\r\n]/; + const peg$r10 = /^[^\r\n]/; + + const peg$e0 = peg$otherExpectation("A type definition"); + const peg$e1 = peg$literalExpectation("definition", false); + const peg$e2 = peg$literalExpectation("type", false); + const peg$e3 = peg$literalExpectation("{", false); + const peg$e4 = peg$literalExpectation("}", false); + const peg$e5 = peg$literalExpectation(":", false); + const peg$e6 = peg$literalExpectation("[]", false); + const peg$e7 = peg$otherExpectation("A statement of fact (or relation in ADR-000)"); + const peg$e8 = peg$literalExpectation("fact", false); + const peg$e9 = peg$literalExpectation("relation", false); + const peg$e10 = peg$literalExpectation("(", false); + const peg$e11 = peg$literalExpectation(")", false); + const peg$e12 = peg$otherExpectation("An evidence rule"); + const peg$e13 = peg$literalExpectation("evidence", false); + const peg$e14 = peg$literalExpectation("*", false); + const peg$e15 = peg$otherExpectation("A derived measurement or value"); + const peg$e16 = peg$literalExpectation("measure", false); + const peg$e17 = peg$literalExpectation("return", false); + const peg$e18 = peg$literalExpectation("NEVER", false); + const peg$e19 = peg$literalExpectation("ALWAYS", false); + const peg$e20 = peg$literalExpectation("REQUIRES", false); + const peg$e21 = peg$literalExpectation("WHEN", false); + const peg$e22 = peg$literalExpectation("UNLESS", false); + const peg$e23 = peg$literalExpectation("|", false); + const peg$e24 = peg$literalExpectation("fusion", false); + const peg$e25 = peg$literalExpectation(",", false); + const peg$e26 = peg$literalExpectation("with", false); + const peg$e27 = peg$literalExpectation("aggregate", false); + const peg$e28 = peg$literalExpectation("USING", false); + const peg$e29 = peg$literalExpectation("PROVIDES", false); + const peg$e30 = peg$literalExpectation("BEHAVES", false); + const peg$e31 = peg$literalExpectation("AS", false); + const peg$e32 = peg$literalExpectation("edge", false); + const peg$e33 = peg$literalExpectation("transitive", false); + const peg$e34 = peg$literalExpectation("hierarchical", false); + const peg$e35 = peg$literalExpectation("symmetrical_graph", false); + const peg$e36 = peg$literalExpectation("symmetrical", false); + const peg$e37 = peg$literalExpectation("limit", false); + const peg$e38 = peg$literalExpectation("decaying", false); + const peg$e39 = peg$literalExpectation("up", false); + const peg$e40 = peg$literalExpectation("down", false); + const peg$e41 = peg$literalExpectation("neutral", false); + const peg$e42 = peg$literalExpectation("stable", false); + const peg$e43 = peg$literalExpectation("hourly", false); + const peg$e44 = peg$literalExpectation("daily", false); + const peg$e45 = peg$literalExpectation("weekly", false); + const peg$e46 = peg$literalExpectation("monthly", false); + const peg$e47 = peg$literalExpectation("blurring", false); + const peg$e48 = peg$literalExpectation("fixed", false); + const peg$e49 = peg$literalExpectation("adaptive", false); + const peg$e50 = peg$literalExpectation("confidence", false); + const peg$e51 = peg$literalExpectation("confidence_90", false); + const peg$e52 = peg$literalExpectation("confidence_95", false); + const peg$e53 = peg$literalExpectation("confidence_99", false); + const peg$e54 = peg$literalExpectation("ttl", false); + const peg$e55 = peg$literalExpectation("CACHE", false); + const peg$e56 = peg$literalExpectation("eager", false); + const peg$e57 = peg$literalExpectation("lazy", false); + const peg$e58 = peg$literalExpectation("||", false); + const peg$e59 = peg$literalExpectation("&&", false); + const peg$e60 = peg$literalExpectation("is", false); + const peg$e61 = peg$literalExpectation("==", false); + const peg$e62 = peg$literalExpectation("!=", false); + const peg$e63 = peg$literalExpectation(">=", false); + const peg$e64 = peg$literalExpectation("<=", false); + const peg$e65 = peg$classExpectation(["<", ">"], false, false, false); + const peg$e66 = peg$literalExpectation("within", false); + const peg$e67 = peg$classExpectation(["+", "-"], false, false, false); + const peg$e68 = peg$classExpectation(["*", "/"], false, false, false); + const peg$e69 = peg$literalExpectation("NOT", false); + const peg$e70 = peg$literalExpectation("!", false); + const peg$e71 = peg$literalExpectation(".", false); + const peg$e72 = peg$otherExpectation("The non-recursive base for an expression chain"); + const peg$e73 = peg$otherExpectation("A string literal"); + const peg$e74 = peg$literalExpectation("\"", false); + const peg$e75 = peg$classExpectation(["\"", "\\"], false, false, false); + const peg$e76 = peg$anyExpectation(); + const peg$e77 = peg$literalExpectation("\\", false); + const peg$e78 = peg$literalExpectation("'", false); + const peg$e79 = peg$classExpectation(["'", "\\"], false, false, false); + const peg$e80 = peg$otherExpectation("A floating-point number"); + const peg$e81 = peg$classExpectation([["0", "9"]], false, false, false); + const peg$e82 = peg$otherExpectation("An integer"); + const peg$e83 = peg$otherExpectation("A boolean literal"); + const peg$e84 = peg$literalExpectation("true", false); + const peg$e85 = peg$literalExpectation("false", false); + const peg$e86 = peg$otherExpectation("A time duration literal"); + const peg$e87 = peg$classExpectation(["d", "h", "m", "w"], false, false, false); + const peg$e88 = peg$classExpectation([["a", "z"], ["A", "Z"], "_"], false, false, false); + const peg$e89 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"], "_"], false, false, false); + const peg$e90 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false, false); + const peg$e91 = peg$literalExpectation("//", false); + const peg$e92 = peg$classExpectation(["\r", "\n"], true, false, false); + const peg$e93 = peg$literalExpectation("/*", false); + const peg$e94 = peg$literalExpectation("*/", false); + + function peg$f0(statements) { + const allStatements = statements.map(s => s[0]); + return { + type: "Program", + body: allStatements, + definitions: allStatements.filter(s => s.type === "Definition"), + facts: allStatements.filter(s => s.type === "Fact"), + evidence: allStatements.filter(s => s.type === "Evidence"), + measures: allStatements.filter(s => s.type === "Measure") + }; + } + function peg$f1(name, fields) { + return { type: "Definition", name, fields: fields.map(f => f[0]) }; + } + function peg$f2(name, fieldType, isArray, behavior, cache) { + return { + type: "Field", + name, + fieldType, + isArray: !!isArray, + behavior: behavior || null, + cache: cache || null + }; + } + function peg$f3(name, params, behavior, properties, cache, limit) { + return { + type: "Fact", + name, + params: params || [], + behavior: behavior || null, + properties: properties.map(p => p[0]), + cache: cache || null, + limit: limit || null + }; + } + function peg$f4(star, name, params, limit, body, provides) { + return { + type: "Evidence", + name, + params: params || [], + limit: limit || null, + body, + provides: provides || null, + challenge: !!star + }; + } + function peg$f5(name, params, body, provides) { + return { + type: "Measure", + name, + params: params || [], + body, + provides: provides || null + }; + } + function peg$f6(statements) { + return { type: "EvidenceBody", statements: statements.map(s => s[0]) }; + } + function peg$f7(statements, returnStmt) { + return { + type: "MeasureBody", + statements: statements.map(s => s[0]), + returnStatement: returnStmt || null + }; + } + function peg$f8(expression) { + return { type: "ReturnStatement", expression }; + } + function peg$f9(type, condition) { + return { type: "DefeasibleLogic", logicType: type, condition }; + } + function peg$f10(condition, defeater) { + return { type: "DefeasibleLogic", logicType: "WHEN", condition, defeater }; + } + function peg$f11(condition) { + return { type: "DefeasibleLogic", logicType: "WHEN", condition }; + } + function peg$f12(predicate, binding, body, limit, withClause) { + return { + type: "PatternMatch", + predicate, + binding: binding || null, + limit: limit || null, + body, + withClause: withClause || null + }; + } + function peg$f13(measure, variable, fusionStrategy, body, limit) { + return { + type: "CollectionProcessing", + measure, + variable, + fusion: fusionStrategy ? { strategy: fusionStrategy[1] } : null, + body, + limit: limit || null + }; + } + function peg$f14(name, args) { + return { type: "Predicate", name, args: args || [] }; + } + function peg$f15(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f16(name) { return { type: "Wildcard", name }; } + function peg$f17(name) { return name; } + function peg$f18(condition) { return condition; } + function peg$f19(strategy, expressions) { + return { type: "Fusion", strategy, expressions }; + } + function peg$f20(expressions, using) { + return { type: "Aggregation", expressions, using: using || null }; + } + function peg$f21(method) { return method; } + function peg$f22(name) { return { type: "TypeName", name }; } + function peg$f23(literal) { return { type: "TypeName", name: literal.value }; } + function peg$f24(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f25(name, paramType, isArray) { + return { type: "Parameter", name, paramType, isArray: !!isArray }; + } + function peg$f26(providesType) { return providesType; } + function peg$f27(behavior) { + return { type: "BehaviorAnnotation", behavior }; + } + function peg$f28() { return "transitive"; } + function peg$f29() { return "symmetrical"; } + function peg$f30(value) { return value; } + function peg$f31(b) { return b; } + function peg$f32(direction, period) { + return { type: "Behavior", behaviorType: "decay", direction, period }; + } + function peg$f33(mode, confidence) { + return { type: "Behavior", behaviorType: "blur", mode, confidence: confidence ? confidence[1] : null }; + } + function peg$f34(duration) { + return { type: "Behavior", behaviorType: "ttl", duration }; + } + function peg$f35(directive) { return directive; } + function peg$f36(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f37(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f38(head, typeName) { + return { type: "BinaryExpression", operator: "is", left: head, right: typeName }; + } + function peg$f39(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f40(head, right) { + return { type: "BinaryExpression", operator: "within", left: head, right }; + } + function peg$f41(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f42(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f43(operator, operand) { return { type: "UnaryExpression", operator: "NOT", operand }; } + function peg$f44(primary, binding) { + if (binding) { + return { type: "BindingAccess", expression: primary, binding }; + } + return primary; + } + function peg$f45(head, tail) { + return tail.reduce((obj, part) => { + return { + type: "AttributeAccess", + object: obj, + attribute: part[3], // The Identifier is the 4th element (index 3) + location: location() + }; + }, head); + } + function peg$f46(expr) { return expr; } + function peg$f47(name, args) { + return { type: "PredicateCall", name, args: args || [], challenge: true }; + } + function peg$f48(name, args) { + return { type: "PredicateCall", name, args: args || [] }; + } + function peg$f49(name) { return { type: "Variable", name }; } + function peg$f50(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f51(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f52(chars) { + return { type: "Literal", value: JSON.parse(text()) }; + } + function peg$f53(chars) { + return { type: "Literal", value: JSON.parse("\"" + chars.map(c => c[0] === '\\' ? c[1] : c[1]).join('') + "\"") }; + } + function peg$f54(value) { return { type: "Literal", value: parseFloat(text()) }; } + function peg$f55(value) { return { type: "Literal", value: parseInt(text(), 10) }; } + function peg$f56(value) { return { type: "Literal", value: value === "true" }; } + function peg$f57(value) { return { type: "Literal", value: text(), unit: text().slice(-1) }; } + function peg$f58(name) { return name; } + let peg$currPos = options.peg$currPos | 0; + let peg$savedPos = peg$currPos; + const peg$posDetailsCache = [{ line: 1, column: 1 }]; + let peg$maxFailPos = peg$currPos; + let peg$maxFailExpected = options.peg$maxFailExpected || []; + let peg$silentFails = options.peg$silentFails | 0; + + let peg$result; + + if (options.startRule) { + if (!(options.startRule in peg$startRuleFunctions)) { + throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); + } + + peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; + } + + function text() { + return input.substring(peg$savedPos, peg$currPos); + } + + function offset() { + return peg$savedPos; + } + + function range() { + return { + source: peg$source, + start: peg$savedPos, + end: peg$currPos, + }; + } + + function location() { + return peg$computeLocation(peg$savedPos, peg$currPos); + } + + function expected(description, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildStructuredError( + [peg$otherExpectation(description)], + input.substring(peg$savedPos, peg$currPos), + location + ); + } + + function error(message, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildSimpleError(message, location); + } + + function peg$getUnicode(pos = peg$currPos) { + const cp = input.codePointAt(pos); + if (cp === undefined) { + return ""; + } + return String.fromCodePoint(cp); + } + + function peg$literalExpectation(text, ignoreCase) { + return { type: "literal", text, ignoreCase }; + } + + function peg$classExpectation(parts, inverted, ignoreCase, unicode) { + return { type: "class", parts, inverted, ignoreCase, unicode }; + } + + function peg$anyExpectation() { + return { type: "any" }; + } + + function peg$endExpectation() { + return { type: "end" }; + } + + function peg$otherExpectation(description) { + return { type: "other", description }; + } + + function peg$computePosDetails(pos) { + let details = peg$posDetailsCache[pos]; + let p; + + if (details) { + return details; + } else { + if (pos >= peg$posDetailsCache.length) { + p = peg$posDetailsCache.length - 1; + } else { + p = pos; + while (!peg$posDetailsCache[--p]) {} + } + + details = peg$posDetailsCache[p]; + details = { + line: details.line, + column: details.column, + }; + + while (p < pos) { + if (input.charCodeAt(p) === 10) { + details.line++; + details.column = 1; + } else { + details.column++; + } + + p++; + } + + peg$posDetailsCache[pos] = details; + + return details; + } + } + + function peg$computeLocation(startPos, endPos, offset) { + const startPosDetails = peg$computePosDetails(startPos); + const endPosDetails = peg$computePosDetails(endPos); + + const res = { + source: peg$source, + start: { + offset: startPos, + line: startPosDetails.line, + column: startPosDetails.column, + }, + end: { + offset: endPos, + line: endPosDetails.line, + column: endPosDetails.column, + }, + }; + if (offset && peg$source && (typeof peg$source.offset === "function")) { + res.start = peg$source.offset(res.start); + res.end = peg$source.offset(res.end); + } + return res; + } + + function peg$fail(expected) { + if (peg$currPos < peg$maxFailPos) { return; } + + if (peg$currPos > peg$maxFailPos) { + peg$maxFailPos = peg$currPos; + peg$maxFailExpected = []; + } + + peg$maxFailExpected.push(expected); + } + + function peg$buildSimpleError(message, location) { + return new peg$SyntaxError(message, null, null, location); + } + + function peg$buildStructuredError(expected, found, location) { + return new peg$SyntaxError( + peg$SyntaxError.buildMessage(expected, found), + expected, + found, + location + ); + } + + function peg$parseProgram() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parse_(); + s2 = []; + s3 = peg$currPos; + s4 = peg$parseStatement(); + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parseStatement(); + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + s3 = peg$parse_(); + peg$savedPos = s0; + s0 = peg$f0(s2); + + return s0; + } + + function peg$parseStatement() { + let s0; + + s0 = peg$parseDefinition(); + if (s0 === peg$FAILED) { + s0 = peg$parseFact(); + if (s0 === peg$FAILED) { + s0 = peg$parseEvidence(); + if (s0 === peg$FAILED) { + s0 = peg$parseMeasure(); + } + } + } + + return s0; + } + + function peg$parseDefinition() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 10) === peg$c0) { + s1 = peg$c0; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c1) { + s1 = peg$c1; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = []; + s8 = peg$currPos; + s9 = peg$parseField(); + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + s9 = [s9, s10]; + s8 = s9; + } else { + peg$currPos = s8; + s8 = peg$FAILED; + } + while (s8 !== peg$FAILED) { + s7.push(s8); + s8 = peg$currPos; + s9 = peg$parseField(); + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + s9 = [s9, s10]; + s8 = s9; + } else { + peg$currPos = s8; + s8 = peg$FAILED; + } + } + if (input.charCodeAt(peg$currPos) === 125) { + s8 = peg$c3; + peg$currPos++; + } else { + s8 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s8 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f1(s3, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + + return s0; + } + + function peg$parseField() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 58) { + s3 = peg$c4; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e5); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseIdentifier(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c5) { + s7 = peg$c5; + peg$currPos += 2; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } + if (s7 === peg$FAILED) { + s7 = null; + } + s8 = peg$parse_(); + s9 = peg$parseBehavior(); + if (s9 === peg$FAILED) { + s9 = null; + } + s10 = peg$parse_(); + s11 = peg$parseCacheDirective(); + if (s11 === peg$FAILED) { + s11 = null; + } + peg$savedPos = s0; + s0 = peg$f2(s1, s5, s7, s9, s11); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseFact() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c6) { + s1 = peg$c6; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e8); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c7) { + s1 = peg$c7; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e9); } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s5 = peg$c8; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseParameterList(); + if (s7 === peg$FAILED) { + s7 = null; + } + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s9 = peg$c9; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + s11 = peg$parseBehaviorAnnotation(); + if (s11 === peg$FAILED) { + s11 = null; + } + s12 = peg$parse_(); + s13 = []; + s14 = peg$currPos; + s15 = peg$parseFactProperty(); + if (s15 !== peg$FAILED) { + s16 = peg$parse_(); + s15 = [s15, s16]; + s14 = s15; + } else { + peg$currPos = s14; + s14 = peg$FAILED; + } + while (s14 !== peg$FAILED) { + s13.push(s14); + s14 = peg$currPos; + s15 = peg$parseFactProperty(); + if (s15 !== peg$FAILED) { + s16 = peg$parse_(); + s15 = [s15, s16]; + s14 = s15; + } else { + peg$currPos = s14; + s14 = peg$FAILED; + } + } + s14 = peg$parseCacheDirective(); + if (s14 === peg$FAILED) { + s14 = null; + } + s15 = peg$parse_(); + s16 = peg$parseLimit(); + if (s16 === peg$FAILED) { + s16 = null; + } + peg$savedPos = s0; + s0 = peg$f3(s3, s7, s11, s13, s14, s16); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e7); } + } + + return s0; + } + + function peg$parseEvidence() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c10) { + s1 = peg$c10; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e13); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 42) { + s3 = peg$c11; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s3 === peg$FAILED) { + s3 = null; + } + s4 = peg$parseIdentifier(); + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s6 = peg$c8; + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s6 !== peg$FAILED) { + s7 = peg$parse_(); + s8 = peg$parseParameterList(); + if (s8 === peg$FAILED) { + s8 = null; + } + s9 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s10 = peg$c9; + peg$currPos++; + } else { + s10 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s10 !== peg$FAILED) { + s11 = peg$parse_(); + s12 = peg$parseLimit(); + if (s12 === peg$FAILED) { + s12 = null; + } + s13 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s14 = peg$c2; + peg$currPos++; + } else { + s14 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s14 !== peg$FAILED) { + s15 = peg$parse_(); + s16 = peg$parseEvidenceBody(); + s17 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s18 = peg$c3; + peg$currPos++; + } else { + s18 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s18 !== peg$FAILED) { + s19 = peg$parse_(); + s20 = peg$parseProvides(); + if (s20 === peg$FAILED) { + s20 = null; + } + peg$savedPos = s0; + s0 = peg$f4(s3, s4, s8, s12, s16, s20); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + + return s0; + } + + function peg$parseMeasure() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 7) === peg$c12) { + s1 = peg$c12; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s5 = peg$c8; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseParameterList(); + if (s7 === peg$FAILED) { + s7 = null; + } + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s9 = peg$c9; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s11 = peg$c2; + peg$currPos++; + } else { + s11 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s11 !== peg$FAILED) { + s12 = peg$parse_(); + s13 = peg$parseMeasureBody(); + s14 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s15 = peg$c3; + peg$currPos++; + } else { + s15 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s15 !== peg$FAILED) { + s16 = peg$parse_(); + s17 = peg$parseProvides(); + if (s17 === peg$FAILED) { + s17 = null; + } + peg$savedPos = s0; + s0 = peg$f5(s3, s7, s13, s17); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e15); } + } + + return s0; + } + + function peg$parseEvidenceBody() { + let s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = []; + s2 = peg$currPos; + s3 = peg$parseEvidenceStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$currPos; + s3 = peg$parseEvidenceStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + } + peg$savedPos = s0; + s1 = peg$f6(s1); + s0 = s1; + + return s0; + } + + function peg$parseEvidenceStatement() { + let s0; + + s0 = peg$parseDefeasibleLogic(); + if (s0 === peg$FAILED) { + s0 = peg$parseFusion(); + if (s0 === peg$FAILED) { + s0 = peg$parseCollectionProcessing(); + if (s0 === peg$FAILED) { + s0 = peg$parsePatternMatch(); + if (s0 === peg$FAILED) { + s0 = peg$parseLogicalOr(); + } + } + } + } + + return s0; + } + + function peg$parseMeasureBody() { + let s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = []; + s2 = peg$currPos; + s3 = peg$parseMeasureStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$currPos; + s3 = peg$parseMeasureStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + } + s2 = peg$parseReturnStatement(); + if (s2 === peg$FAILED) { + s2 = null; + } + peg$savedPos = s0; + s0 = peg$f7(s1, s2); + + return s0; + } + + function peg$parseMeasureStatement() { + let s0; + + s0 = peg$parseFusion(); + if (s0 === peg$FAILED) { + s0 = peg$parseAggregation(); + if (s0 === peg$FAILED) { + s0 = peg$parsePatternMatch(); + if (s0 === peg$FAILED) { + s0 = peg$parseLogicalOr(); + } + } + } + + return s0; + } + + function peg$parseReturnStatement() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c13) { + s1 = peg$c13; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f8(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseDefeasibleLogic() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c14) { + s1 = peg$c14; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c15) { + s1 = peg$c15; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e19); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c16) { + s1 = peg$c16; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e20); } + } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f9(s1, s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c17) { + s1 = peg$c17; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c18) { + s5 = peg$c18; + peg$currPos += 6; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse__(); + if (s6 !== peg$FAILED) { + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f10(s3, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c17) { + s1 = peg$c17; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f11(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + } + + return s0; + } + + function peg$parsePatternMatch() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13; + + s0 = peg$currPos; + s1 = peg$parsePatternPredicate(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseBindingClause(); + if (s3 === peg$FAILED) { + s3 = null; + } + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseEvidenceBody(); + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s9 = peg$c3; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + s11 = peg$parseLimit(); + if (s11 === peg$FAILED) { + s11 = null; + } + s12 = peg$parse_(); + s13 = peg$parseWithClause(); + if (s13 === peg$FAILED) { + s13 = null; + } + peg$savedPos = s0; + s0 = peg$f12(s1, s3, s7, s11, s13); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseCollectionProcessing() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17; + + s0 = peg$currPos; + s1 = peg$parseLogicalOr(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 124) { + s3 = peg$c19; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseIdentifier(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 124) { + s7 = peg$c19; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + s9 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c20) { + s10 = peg$c20; + peg$currPos += 6; + } else { + s10 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + if (s10 !== peg$FAILED) { + s11 = peg$parse__(); + if (s11 !== peg$FAILED) { + s12 = peg$parseIdentifier(); + if (s12 !== peg$FAILED) { + s10 = [s10, s11, s12]; + s9 = s10; + } else { + peg$currPos = s9; + s9 = peg$FAILED; + } + } else { + peg$currPos = s9; + s9 = peg$FAILED; + } + } else { + peg$currPos = s9; + s9 = peg$FAILED; + } + if (s9 === peg$FAILED) { + s9 = null; + } + s10 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s11 = peg$c2; + peg$currPos++; + } else { + s11 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s11 !== peg$FAILED) { + s12 = peg$parse_(); + s13 = peg$parseEvidenceBody(); + if (s13 !== peg$FAILED) { + s14 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s15 = peg$c3; + peg$currPos++; + } else { + s15 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s15 !== peg$FAILED) { + s16 = peg$parse_(); + s17 = peg$parseLimit(); + if (s17 === peg$FAILED) { + s17 = null; + } + peg$savedPos = s0; + s0 = peg$f13(s1, s5, s9, s13, s17); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePatternPredicate() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s3 = peg$c8; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parsePatternArgumentList(); + if (s5 === peg$FAILED) { + s5 = null; + } + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s7 = peg$c9; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s7 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f14(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePatternArgumentList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parsePatternArgument(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parsePatternArgument(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parsePatternArgument(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f15(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePatternArgument() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 42) { + s1 = peg$c11; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f16(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseLogicalOr(); + } + + return s0; + } + + function peg$parseBindingClause() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 124) { + s1 = peg$c19; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 124) { + s5 = peg$c19; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f17(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseWithClause() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c22) { + s1 = peg$c22; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e26); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f18(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseFusion() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c20) { + s1 = peg$c20; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseExpressionList(); + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s9 = peg$c3; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s9 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f19(s3, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseAggregation() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 9) === peg$c23) { + s1 = peg$c23; + peg$currPos += 9; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s3 = peg$c2; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseExpressionList(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s7 = peg$c3; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + s9 = peg$parseUsing(); + if (s9 === peg$FAILED) { + s9 = null; + } + peg$savedPos = s0; + s0 = peg$f20(s5, s9); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseUsing() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c24) { + s1 = peg$c24; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e28); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f21(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseTypeName() { + let s0, s1; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f22(s1); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseString(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f23(s1); + } + s0 = s1; + } + + return s0; + } + + function peg$parseParameterList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseParameter(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseParameter(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseParameter(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f24(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseParameter() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 58) { + s3 = peg$c4; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e5); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseIdentifier(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c5) { + s7 = peg$c5; + peg$currPos += 2; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } + if (s7 === peg$FAILED) { + s7 = null; + } + peg$savedPos = s0; + s0 = peg$f25(s1, s5, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseProvides() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c25) { + s1 = peg$c25; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f26(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseBehaviorAnnotation() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 7) === peg$c26) { + s1 = peg$c26; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e30); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c27) { + s3 = peg$c27; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c28) { + s5 = peg$c28; + peg$currPos += 4; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c29) { + s5 = peg$c29; + peg$currPos += 10; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 12) === peg$c30) { + s5 = peg$c30; + peg$currPos += 12; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 17) === peg$c31) { + s5 = peg$c31; + peg$currPos += 17; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e35); } + } + } + } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f27(s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseFactProperty() { + let s0, s1; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 10) === peg$c29) { + s1 = peg$c29; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f28(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 11) === peg$c32) { + s1 = peg$c32; + peg$currPos += 11; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e36); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f29(); + } + s0 = s1; + } + + return s0; + } + + function peg$parseLimit() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c33) { + s1 = peg$c33; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseInteger(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f30(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseBehavior() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 7) === peg$c26) { + s1 = peg$c26; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e30); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s3 = peg$c2; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseDecayBehavior(); + if (s5 === peg$FAILED) { + s5 = peg$parseBlurBehavior(); + if (s5 === peg$FAILED) { + s5 = peg$parseTTLBehavior(); + } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s7 = peg$c3; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s7 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f31(s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseDecayBehavior() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c34) { + s1 = peg$c34; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e38); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c35) { + s3 = peg$c35; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e39); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c36) { + s3 = peg$c36; + peg$currPos += 4; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e40); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c37) { + s3 = peg$c37; + peg$currPos += 7; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e41); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c38) { + s3 = peg$c38; + peg$currPos += 6; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e42); } + } + } + } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c39) { + s5 = peg$c39; + peg$currPos += 6; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e43); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c40) { + s5 = peg$c40; + peg$currPos += 5; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e44); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c41) { + s5 = peg$c41; + peg$currPos += 6; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e45); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c42) { + s5 = peg$c42; + peg$currPos += 7; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e46); } + } + } + } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f32(s3, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseBlurBehavior() { + let s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c43) { + s1 = peg$c43; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e47); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c44) { + s3 = peg$c44; + peg$currPos += 5; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e48); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c45) { + s3 = peg$c45; + peg$currPos += 8; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e49); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c46) { + s3 = peg$c46; + peg$currPos += 10; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e50); } + } + } + } + if (s3 !== peg$FAILED) { + s4 = peg$currPos; + s5 = peg$parse__(); + if (s5 !== peg$FAILED) { + if (input.substr(peg$currPos, 13) === peg$c47) { + s6 = peg$c47; + peg$currPos += 13; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e51); } + } + if (s6 === peg$FAILED) { + if (input.substr(peg$currPos, 13) === peg$c48) { + s6 = peg$c48; + peg$currPos += 13; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e52); } + } + if (s6 === peg$FAILED) { + if (input.substr(peg$currPos, 13) === peg$c49) { + s6 = peg$c49; + peg$currPos += 13; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e53); } + } + } + } + if (s6 !== peg$FAILED) { + s5 = [s5, s6]; + s4 = s5; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 === peg$FAILED) { + s4 = null; + } + peg$savedPos = s0; + s0 = peg$f33(s3, s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseTTLBehavior() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 3) === peg$c50) { + s1 = peg$c50; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e54); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseDuration(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f34(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseCacheDirective() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c51) { + s1 = peg$c51; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e55); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c52) { + s3 = peg$c52; + peg$currPos += 5; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e56); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c53) { + s3 = peg$c53; + peg$currPos += 4; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e57); } + } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f35(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLogicalOr() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseLogicalAnd(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c54) { + s5 = peg$c54; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e58); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalAnd(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c54) { + s5 = peg$c54; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e58); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalAnd(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f36(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLogicalAnd() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseComparison(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c55) { + s5 = peg$c55; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e59); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c55) { + s5 = peg$c55; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e59); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f37(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseComparison() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseTemporalComparison(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c56) { + s3 = peg$c56; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e60); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + s5 = peg$parseTypeName(); + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f38(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseTemporalComparison(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c57) { + s5 = peg$c57; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e61); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c58) { + s5 = peg$c58; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e62); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c59) { + s5 = peg$c59; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e63); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c60) { + s5 = peg$c60; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e64); } + } + if (s5 === peg$FAILED) { + s5 = input.charAt(peg$currPos); + if (peg$r0.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e65); } + } + } + } + } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseTemporalComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c57) { + s5 = peg$c57; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e61); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c58) { + s5 = peg$c58; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e62); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c59) { + s5 = peg$c59; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e63); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c60) { + s5 = peg$c60; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e64); } + } + if (s5 === peg$FAILED) { + s5 = input.charAt(peg$currPos); + if (peg$r0.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e65); } + } + } + } + } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseTemporalComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f39(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + function peg$parseTemporalComparison() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseAddition(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.substr(peg$currPos, 6) === peg$c61) { + s3 = peg$c61; + peg$currPos += 6; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e66); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + s5 = peg$parseDuration(); + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f40(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseAddition(); + } + + return s0; + } + + function peg$parseAddition() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseMultiplication(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + s5 = input.charAt(peg$currPos); + if (peg$r1.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e67); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseMultiplication(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + s5 = input.charAt(peg$currPos); + if (peg$r1.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e67); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseMultiplication(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f41(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseMultiplication() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseUnary(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + s5 = input.charAt(peg$currPos); + if (peg$r2.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e68); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseUnary(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + s5 = input.charAt(peg$currPos); + if (peg$r2.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e68); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseUnary(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f42(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseUnary() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 3) === peg$c62) { + s1 = peg$c62; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e69); } + } + if (s1 === peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 33) { + s1 = peg$c63; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e70); } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseUnary(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f43(s1, s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parsePostfix(); + } + + return s0; + } + + function peg$parsePostfix() { + let s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$parseAttributeAccess(); + if (s1 === peg$FAILED) { + s1 = peg$parsePrimaryTerm(); + } + if (s1 !== peg$FAILED) { + s2 = peg$parseBindingClause(); + if (s2 === peg$FAILED) { + s2 = null; + } + peg$savedPos = s0; + s0 = peg$f44(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseAttributeAccess() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parsePrimaryTerm(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 46) { + s5 = peg$c64; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e71); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseIdentifier(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 46) { + s5 = peg$c64; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e71); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseIdentifier(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f45(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePrimaryTerm() { + let s0, s1, s2, s3, s4, s5; + + peg$silentFails++; + s0 = peg$parseChallengePredicate(); + if (s0 === peg$FAILED) { + s0 = peg$parseLiteral(); + if (s0 === peg$FAILED) { + s0 = peg$parsePredicateCall(); + if (s0 === peg$FAILED) { + s0 = peg$parseVariable(); + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 40) { + s1 = peg$c8; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s5 = peg$c9; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f46(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + } + } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e72); } + } + + return s0; + } + + function peg$parseChallengePredicate() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 42) { + s1 = peg$c11; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseIdentifier(); + if (s2 !== peg$FAILED) { + s3 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s4 = peg$c8; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + s6 = peg$parseArgumentList(); + if (s6 === peg$FAILED) { + s6 = null; + } + s7 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s8 = peg$c9; + peg$currPos++; + } else { + s8 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s8 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f47(s2, s6); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePredicateCall() { + let s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 40) { + s2 = peg$c8; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s2 !== peg$FAILED) { + s3 = peg$parse_(); + s4 = peg$parseArgumentList(); + if (s4 === peg$FAILED) { + s4 = null; + } + s5 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s6 = peg$c9; + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s6 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f48(s1, s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseVariable() { + let s0, s1; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f49(s1); + } + s0 = s1; + + return s0; + } + + function peg$parseArgumentList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseLogicalOr(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f50(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseExpressionList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseLogicalOr(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f51(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLiteral() { + let s0; + + s0 = peg$parseString(); + if (s0 === peg$FAILED) { + s0 = peg$parseFloat(); + if (s0 === peg$FAILED) { + s0 = peg$parseInteger(); + if (s0 === peg$FAILED) { + s0 = peg$parseBoolean(); + if (s0 === peg$FAILED) { + s0 = peg$parseDuration(); + } + } + } + } + + return s0; + } + + function peg$parseString() { + let s0, s1, s2, s3, s4, s5; + + peg$silentFails++; + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 34) { + s1 = peg$c65; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e74); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r3.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e75); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c66; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e77); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r3.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e75); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c66; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e77); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } + if (input.charCodeAt(peg$currPos) === 34) { + s3 = peg$c65; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e74); } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f52(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 39) { + s1 = peg$c67; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e78); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r4.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c66; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e77); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r4.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c66; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e77); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } + if (input.charCodeAt(peg$currPos) === 39) { + s3 = peg$c67; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e78); } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f53(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e73); } + } + + return s0; + } + + function peg$parseFloat() { + let s0, s1, s2, s3, s4, s5; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$currPos; + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 46) { + s3 = peg$c64; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e71); } + } + if (s3 !== peg$FAILED) { + s4 = []; + s5 = input.charAt(peg$currPos); + if (peg$r5.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + if (s5 !== peg$FAILED) { + while (s5 !== peg$FAILED) { + s4.push(s5); + s5 = input.charAt(peg$currPos); + if (peg$r5.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + } + } else { + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + s2 = [s2, s3, s4]; + s1 = s2; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f54(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e80); } + } + + return s0; + } + + function peg$parseInteger() { + let s0, s1, s2; + + peg$silentFails++; + s0 = peg$currPos; + s1 = []; + s2 = input.charAt(peg$currPos); + if (peg$r5.test(s2)) { + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = input.charAt(peg$currPos); + if (peg$r5.test(s2)) { + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f55(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e82); } + } + + return s0; + } + + function peg$parseBoolean() { + let s0, s1; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c68) { + s1 = peg$c68; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c69) { + s1 = peg$c69; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e85); } + } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f56(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e83); } + } + + return s0; + } + + function peg$parseDuration() { + let s0, s1, s2, s3; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$currPos; + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = input.charAt(peg$currPos); + if (peg$r6.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e87); } + } + if (s3 !== peg$FAILED) { + s2 = [s2, s3]; + s1 = s2; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f57(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e86); } + } + + return s0; + } + + function peg$parseIdentifier() { + let s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + s2 = peg$parseKeyword(); + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = undefined; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + s3 = peg$currPos; + s4 = input.charAt(peg$currPos); + if (peg$r7.test(s4)) { + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e88); } + } + if (s4 !== peg$FAILED) { + s5 = []; + s6 = input.charAt(peg$currPos); + if (peg$r8.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e89); } + } + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = input.charAt(peg$currPos); + if (peg$r8.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e89); } + } + } + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + s2 = input.substring(s2, peg$currPos); + } else { + s2 = s3; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f58(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseKeyword() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 10) === peg$c0) { + s1 = peg$c0; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c1) { + s1 = peg$c1; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c6) { + s1 = peg$c6; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e8); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c7) { + s1 = peg$c7; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e9); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c10) { + s1 = peg$c10; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e13); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c12) { + s1 = peg$c12; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c26) { + s1 = peg$c26; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e30); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c27) { + s1 = peg$c27; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c51) { + s1 = peg$c51; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e55); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c34) { + s1 = peg$c34; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e38); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c43) { + s1 = peg$c43; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e47); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 3) === peg$c50) { + s1 = peg$c50; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e54); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c29) { + s1 = peg$c29; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 11) === peg$c32) { + s1 = peg$c32; + peg$currPos += 11; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e36); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 12) === peg$c30) { + s1 = peg$c30; + peg$currPos += 12; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 17) === peg$c31) { + s1 = peg$c31; + peg$currPos += 17; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e35); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c28) { + s1 = peg$c28; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c33) { + s1 = peg$c33; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c25) { + s1 = peg$c25; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c20) { + s1 = peg$c20; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 9) === peg$c23) { + s1 = peg$c23; + peg$currPos += 9; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c24) { + s1 = peg$c24; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e28); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c14) { + s1 = peg$c14; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c15) { + s1 = peg$c15; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e19); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c17) { + s1 = peg$c17; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c18) { + s1 = peg$c18; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c16) { + s1 = peg$c16; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e20); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c22) { + s1 = peg$c22; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e26); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c68) { + s1 = peg$c68; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c69) { + s1 = peg$c69; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e85); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 3) === peg$c62) { + s1 = peg$c62; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e69); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c61) { + s1 = peg$c61; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e66); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c13) { + s1 = peg$c13; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c56) { + s1 = peg$c56; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e60); } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + peg$silentFails++; + s3 = input.charAt(peg$currPos); + if (peg$r8.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e89); } + } + peg$silentFails--; + if (s3 === peg$FAILED) { + s2 = undefined; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parse_() { + let s0, s1; + + s0 = []; + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + } + + return s0; + } + + function peg$parse__() { + let s0, s1; + + s0 = []; + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + if (s1 !== peg$FAILED) { + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + } + } else { + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseWhiteSpace() { + let s0; + + s0 = input.charAt(peg$currPos); + if (peg$r9.test(s0)) { + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e90); } + } + + return s0; + } + + function peg$parseComment() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c70) { + s1 = peg$c70; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e91); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r10.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e92); } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r10.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e92); } + } + } + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c71) { + s1 = peg$c71; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e93); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c72) { + s5 = peg$c72; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e94); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c72) { + s5 = peg$c72; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e94); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + if (input.substr(peg$currPos, 2) === peg$c72) { + s3 = peg$c72; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e94); } + } + if (s3 !== peg$FAILED) { + s1 = [s1, s2, s3]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + + // The location() function provides line/column info for error reporting. + // The text() function returns the matched text for a rule. + + // Helper function to build a left-associative binary expression tree. + function buildLeftAssoc(head, tail) { + return tail.reduce((result, element) => { + return { + type: "BinaryExpression", + operator: element[1], + left: result, + right: element[3], + location: location() + }; + }, head); + } + + peg$result = peg$startRuleFunction(); + + const peg$success = (peg$result !== peg$FAILED && peg$currPos === input.length); + function peg$throw() { + if (peg$result !== peg$FAILED && peg$currPos < input.length) { + peg$fail(peg$endExpectation()); + } + + throw peg$buildStructuredError( + peg$maxFailExpected, + peg$maxFailPos < input.length ? peg$getUnicode(peg$maxFailPos) : null, + peg$maxFailPos < input.length + ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) + : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) + ); + } + if (options.peg$library) { + return /** @type {any} */ ({ + peg$result, + peg$currPos, + peg$FAILED, + peg$maxFailExpected, + peg$maxFailPos, + peg$success, + peg$throw: peg$success ? undefined : peg$throw, + }); + } + if (peg$success) { + return peg$result; + } else { + peg$throw(); + } +} + +const peg$allowedStartRules = [ + "Program" +]; + +export { + peg$allowedStartRules as StartRules, + peg$SyntaxError as SyntaxError, + peg$parse as parse +}; diff --git a/src/parser/ExpressionParser.js b/src/parser/ExpressionParser.js new file mode 100644 index 0000000..c7ad57b --- /dev/null +++ b/src/parser/ExpressionParser.js @@ -0,0 +1,1614 @@ +// @generated by Peggy 5.1.0. +// +// https://peggyjs.org/ + + +class peg$SyntaxError extends SyntaxError { + constructor(message, expected, found, location) { + super(message); + this.expected = expected; + this.found = found; + this.location = location; + this.name = "SyntaxError"; + } + + format(sources) { + let str = "Error: " + this.message; + if (this.location) { + let src = null; + const st = sources.find(s => s.source === this.location.source); + if (st) { + src = st.text.split(/\r\n|\n|\r/g); + } + const s = this.location.start; + const offset_s = (this.location.source && (typeof this.location.source.offset === "function")) + ? this.location.source.offset(s) + : s; + const loc = this.location.source + ":" + offset_s.line + ":" + offset_s.column; + if (src) { + const e = this.location.end; + const filler = "".padEnd(offset_s.line.toString().length, " "); + const line = src[s.line - 1]; + const last = s.line === e.line ? e.column : line.length + 1; + const hatLen = (last - s.column) || 1; + str += "\n --> " + loc + "\n" + + filler + " |\n" + + offset_s.line + " | " + line + "\n" + + filler + " | " + "".padEnd(s.column - 1, " ") + + "".padEnd(hatLen, "^"); + } else { + str += "\n at " + loc; + } + } + return str; + } + + static buildMessage(expected, found) { + function hex(ch) { + return ch.codePointAt(0).toString(16).toUpperCase(); + } + + const nonPrintable = Object.prototype.hasOwnProperty.call(RegExp.prototype, "unicode") + ? new RegExp("[\\p{C}\\p{Mn}\\p{Mc}]", "gu") + : null; + function unicodeEscape(s) { + if (nonPrintable) { + return s.replace(nonPrintable, ch => "\\u{" + hex(ch) + "}"); + } + return s; + } + + function literalEscape(s) { + return unicodeEscape(s + .replace(/\\/g, "\\\\") + .replace(/"/g, "\\\"") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch)) + .replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch))); + } + + function classEscape(s) { + return unicodeEscape(s + .replace(/\\/g, "\\\\") + .replace(/\]/g, "\\]") + .replace(/\^/g, "\\^") + .replace(/-/g, "\\-") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch)) + .replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch))); + } + + const DESCRIBE_EXPECTATION_FNS = { + literal(expectation) { + return "\"" + literalEscape(expectation.text) + "\""; + }, + + class(expectation) { + const escapedParts = expectation.parts.map( + part => (Array.isArray(part) + ? classEscape(part[0]) + "-" + classEscape(part[1]) + : classEscape(part)) + ); + + return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]" + (expectation.unicode ? "u" : ""); + }, + + any() { + return "any character"; + }, + + end() { + return "end of input"; + }, + + other(expectation) { + return expectation.description; + }, + }; + + function describeExpectation(expectation) { + return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation); + } + + function describeExpected(expected) { + const descriptions = expected.map(describeExpectation); + descriptions.sort(); + + if (descriptions.length > 0) { + let j = 1; + for (let i = 1; i < descriptions.length; i++) { + if (descriptions[i - 1] !== descriptions[i]) { + descriptions[j] = descriptions[i]; + j++; + } + } + descriptions.length = j; + } + + switch (descriptions.length) { + case 1: + return descriptions[0]; + + case 2: + return descriptions[0] + " or " + descriptions[1]; + + default: + return descriptions.slice(0, -1).join(", ") + + ", or " + + descriptions[descriptions.length - 1]; + } + } + + function describeFound(found) { + return found ? "\"" + literalEscape(found) + "\"" : "end of input"; + } + + return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found."; + } +} + +function peg$parse(input, options) { + options = options !== undefined ? options : {}; + + const peg$FAILED = {}; + const peg$source = options.grammarSource; + + const peg$startRuleFunctions = { + Expression: peg$parseExpression, + }; + let peg$startRuleFunction = peg$parseExpression; + + const peg$c0 = "UNLESS"; + const peg$c1 = "FUSION"; + const peg$c2 = "{"; + const peg$c3 = "}"; + const peg$c4 = "max"; + const peg$c5 = "min"; + const peg$c6 = "majority"; + const peg$c7 = "average"; + const peg$c8 = "sum"; + const peg$c9 = "sum_unbounded"; + const peg$c10 = "median"; + const peg$c11 = "optimistic"; + const peg$c12 = "pessimistic"; + const peg$c13 = "top2"; + const peg$c14 = "top3"; + const peg$c15 = "priority"; + const peg$c16 = "*"; + const peg$c17 = "("; + const peg$c18 = ")"; + const peg$c19 = ","; + const peg$c20 = ":"; + const peg$c21 = "."; + const peg$c22 = "::"; + const peg$c23 = "\""; + const peg$c24 = "'"; + const peg$c25 = "\\"; + const peg$c26 = "//"; + const peg$c27 = "/*"; + const peg$c28 = "*/"; + + const peg$r0 = /^[^"\\]/; + const peg$r1 = /^[^'\\]/; + const peg$r2 = /^["'\\nrt]/; + const peg$r3 = /^[0-9]/; + const peg$r4 = /^[a-zA-Z_]/; + const peg$r5 = /^[a-zA-Z0-9_\-]/; + const peg$r6 = /^[ \t\n\r]/; + const peg$r7 = /^[^\n]/; + + const peg$e0 = peg$literalExpectation("UNLESS", false); + const peg$e1 = peg$literalExpectation("FUSION", false); + const peg$e2 = peg$literalExpectation("{", false); + const peg$e3 = peg$literalExpectation("}", false); + const peg$e4 = peg$literalExpectation("max", false); + const peg$e5 = peg$literalExpectation("min", false); + const peg$e6 = peg$literalExpectation("majority", false); + const peg$e7 = peg$literalExpectation("average", false); + const peg$e8 = peg$literalExpectation("sum", false); + const peg$e9 = peg$literalExpectation("sum_unbounded", false); + const peg$e10 = peg$literalExpectation("median", false); + const peg$e11 = peg$literalExpectation("optimistic", false); + const peg$e12 = peg$literalExpectation("pessimistic", false); + const peg$e13 = peg$literalExpectation("top2", false); + const peg$e14 = peg$literalExpectation("top3", false); + const peg$e15 = peg$literalExpectation("priority", false); + const peg$e16 = peg$literalExpectation("*", false); + const peg$e17 = peg$literalExpectation("(", false); + const peg$e18 = peg$literalExpectation(")", false); + const peg$e19 = peg$literalExpectation(",", false); + const peg$e20 = peg$literalExpectation(":", false); + const peg$e21 = peg$literalExpectation(".", false); + const peg$e22 = peg$literalExpectation("::", false); + const peg$e23 = peg$literalExpectation("\"", false); + const peg$e24 = peg$classExpectation(["\"", "\\"], true, false, false); + const peg$e25 = peg$literalExpectation("'", false); + const peg$e26 = peg$classExpectation(["'", "\\"], true, false, false); + const peg$e27 = peg$literalExpectation("\\", false); + const peg$e28 = peg$classExpectation(["\"", "'", "\\", "n", "r", "t"], false, false, false); + const peg$e29 = peg$classExpectation([["0", "9"]], false, false, false); + const peg$e30 = peg$classExpectation([["a", "z"], ["A", "Z"], "_"], false, false, false); + const peg$e31 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"], "_", "-"], false, false, false); + const peg$e32 = peg$classExpectation([" ", "\t", "\n", "\r"], false, false, false); + const peg$e33 = peg$literalExpectation("//", false); + const peg$e34 = peg$classExpectation(["\n"], true, false, false); + const peg$e35 = peg$literalExpectation("/*", false); + const peg$e36 = peg$literalExpectation("*/", false); + const peg$e37 = peg$anyExpectation(); + + function peg$f0(expr) { return expr; } + function peg$f1(primary, exception) { + return makeDefeasible(primary, exception); + } + function peg$f2(aggregator, expressions) { + return makeFusion(expressions, aggregator); + } + function peg$f3(head, tail) { + const exprs = [head]; + for (const t of tail) { + exprs.push(t[1]); + } + return exprs; + } + function peg$f4(name, args) { + return makeChallengePredicate(name, args || []); + } + function peg$f5(name, args) { + return makePredicate(name, args || []); + } + function peg$f6(head, tail) { + const args = [head]; + for (const t of tail) { + args.push(t[3]); + } + return args; + } + function peg$f7(name, path) { + return makeVariable(name, path.map(p => p[1])); + } + function peg$f8(refType, path) { + return { type: 'Reference', refType: refType, path: path }; + } + function peg$f9(head, tail) { + const parts = [head]; + for (const t of tail) { + parts.push(t[1]); + } + return parts; + } + function peg$f10(chars) { + return { type: 'Literal', value: chars.join(''), dataType: 'string' }; + } + function peg$f11(chars) { + return { type: 'Literal', value: chars.join(''), dataType: 'string' }; + } + function peg$f12(char) { + const escapes = { '"': '"', "'": "'", '\\': '\\', 'n': '\n', 'r': '\r', 't': '\t' }; + return escapes[char] || char; + } + function peg$f13(digits) { + return { type: 'Literal', value: parseInt(digits.join(''), 10), dataType: 'number' }; + } + function peg$f14(first, rest) { + return first + rest.join(''); + } + let peg$currPos = options.peg$currPos | 0; + let peg$savedPos = peg$currPos; + const peg$posDetailsCache = [{ line: 1, column: 1 }]; + let peg$maxFailPos = peg$currPos; + let peg$maxFailExpected = options.peg$maxFailExpected || []; + let peg$silentFails = options.peg$silentFails | 0; + + let peg$result; + + if (options.startRule) { + if (!(options.startRule in peg$startRuleFunctions)) { + throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); + } + + peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; + } + + function text() { + return input.substring(peg$savedPos, peg$currPos); + } + + function offset() { + return peg$savedPos; + } + + function range() { + return { + source: peg$source, + start: peg$savedPos, + end: peg$currPos, + }; + } + + function location() { + return peg$computeLocation(peg$savedPos, peg$currPos); + } + + function expected(description, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildStructuredError( + [peg$otherExpectation(description)], + input.substring(peg$savedPos, peg$currPos), + location + ); + } + + function error(message, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildSimpleError(message, location); + } + + function peg$getUnicode(pos = peg$currPos) { + const cp = input.codePointAt(pos); + if (cp === undefined) { + return ""; + } + return String.fromCodePoint(cp); + } + + function peg$literalExpectation(text, ignoreCase) { + return { type: "literal", text, ignoreCase }; + } + + function peg$classExpectation(parts, inverted, ignoreCase, unicode) { + return { type: "class", parts, inverted, ignoreCase, unicode }; + } + + function peg$anyExpectation() { + return { type: "any" }; + } + + function peg$endExpectation() { + return { type: "end" }; + } + + function peg$otherExpectation(description) { + return { type: "other", description }; + } + + function peg$computePosDetails(pos) { + let details = peg$posDetailsCache[pos]; + let p; + + if (details) { + return details; + } else { + if (pos >= peg$posDetailsCache.length) { + p = peg$posDetailsCache.length - 1; + } else { + p = pos; + while (!peg$posDetailsCache[--p]) {} + } + + details = peg$posDetailsCache[p]; + details = { + line: details.line, + column: details.column, + }; + + while (p < pos) { + if (input.charCodeAt(p) === 10) { + details.line++; + details.column = 1; + } else { + details.column++; + } + + p++; + } + + peg$posDetailsCache[pos] = details; + + return details; + } + } + + function peg$computeLocation(startPos, endPos, offset) { + const startPosDetails = peg$computePosDetails(startPos); + const endPosDetails = peg$computePosDetails(endPos); + + const res = { + source: peg$source, + start: { + offset: startPos, + line: startPosDetails.line, + column: startPosDetails.column, + }, + end: { + offset: endPos, + line: endPosDetails.line, + column: endPosDetails.column, + }, + }; + if (offset && peg$source && (typeof peg$source.offset === "function")) { + res.start = peg$source.offset(res.start); + res.end = peg$source.offset(res.end); + } + return res; + } + + function peg$fail(expected) { + if (peg$currPos < peg$maxFailPos) { return; } + + if (peg$currPos > peg$maxFailPos) { + peg$maxFailPos = peg$currPos; + peg$maxFailExpected = []; + } + + peg$maxFailExpected.push(expected); + } + + function peg$buildSimpleError(message, location) { + return new peg$SyntaxError(message, null, null, location); + } + + function peg$buildStructuredError(expected, found, location) { + return new peg$SyntaxError( + peg$SyntaxError.buildMessage(expected, found), + expected, + found, + location + ); + } + + function peg$parseExpression() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = peg$parse_(); + s2 = peg$parseDefeasibleExpr(); + if (s2 !== peg$FAILED) { + s3 = peg$parse_(); + peg$savedPos = s0; + s0 = peg$f0(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseDefeasibleExpr() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parsePrimaryExpr(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.substr(peg$currPos, 6) === peg$c0) { + s3 = peg$c0; + peg$currPos += 6; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parsePredicateCall(); + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f1(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parsePrimaryExpr(); + } + + return s0; + } + + function peg$parsePrimaryExpr() { + let s0; + + s0 = peg$parseFusionBlock(); + if (s0 === peg$FAILED) { + s0 = peg$parseChallengePredicate(); + if (s0 === peg$FAILED) { + s0 = peg$parsePredicateCall(); + } + } + + return s0; + } + + function peg$parseFusionBlock() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c1) { + s1 = peg$c1; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseAggregatorKeyword(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseExpressionList(); + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s9 = peg$c3; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s9 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f2(s3, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseAggregatorKeyword() { + let s0; + + if (input.substr(peg$currPos, 3) === peg$c4) { + s0 = peg$c4; + peg$currPos += 3; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 3) === peg$c5) { + s0 = peg$c5; + peg$currPos += 3; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e5); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c6) { + s0 = peg$c6; + peg$currPos += 8; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c7) { + s0 = peg$c7; + peg$currPos += 7; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e7); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 3) === peg$c8) { + s0 = peg$c8; + peg$currPos += 3; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e8); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 13) === peg$c9) { + s0 = peg$c9; + peg$currPos += 13; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e9); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c10) { + s0 = peg$c10; + peg$currPos += 6; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c11) { + s0 = peg$c11; + peg$currPos += 10; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 11) === peg$c12) { + s0 = peg$c12; + peg$currPos += 11; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c13) { + s0 = peg$c13; + peg$currPos += 4; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e13); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c14) { + s0 = peg$c14; + peg$currPos += 4; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c15) { + s0 = peg$c15; + peg$currPos += 8; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e15); } + } + } + } + } + } + } + } + } + } + } + } + } + + return s0; + } + + function peg$parseExpressionList() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseExpression(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + s5 = peg$parseExpression(); + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + s5 = peg$parseExpression(); + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f3(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseChallengePredicate() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 42) { + s1 = peg$c16; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseIdentifier(); + if (s2 !== peg$FAILED) { + s3 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s4 = peg$c17; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + s6 = peg$parseArgumentList(); + if (s6 === peg$FAILED) { + s6 = null; + } + s7 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s8 = peg$c18; + peg$currPos++; + } else { + s8 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } + if (s8 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f4(s2, s6); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePredicateCall() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s3 = peg$c17; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseArgumentList(); + if (s5 === peg$FAILED) { + s5 = null; + } + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s7 = peg$c18; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } + if (s7 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f5(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseArgumentList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseArgument(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c19; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e19); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseArgument(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c19; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e19); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseArgument(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f6(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseArgument() { + let s0; + + s0 = peg$parseVariableBinding(); + if (s0 === peg$FAILED) { + s0 = peg$parseLiteral(); + if (s0 === peg$FAILED) { + s0 = peg$parseTypedReference(); + } + } + + return s0; + } + + function peg$parseVariableBinding() { + let s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 58) { + s1 = peg$c20; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e20); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseIdentifier(); + if (s2 !== peg$FAILED) { + s3 = []; + s4 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 46) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parseIdentifier(); + if (s6 !== peg$FAILED) { + s5 = [s5, s6]; + s4 = s5; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 46) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parseIdentifier(); + if (s6 !== peg$FAILED) { + s5 = [s5, s6]; + s4 = s5; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f7(s2, s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseTypedReference() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c22) { + s2 = peg$c22; + peg$currPos += 2; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + if (s2 !== peg$FAILED) { + s3 = peg$parsePath(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f8(s1, s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePath() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 46) { + s4 = peg$c21; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s4 !== peg$FAILED) { + s5 = peg$parseIdentifier(); + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 46) { + s4 = peg$c21; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s4 !== peg$FAILED) { + s5 = peg$parseIdentifier(); + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f9(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLiteral() { + let s0; + + s0 = peg$parseStringLiteral(); + if (s0 === peg$FAILED) { + s0 = peg$parseNumberLiteral(); + } + + return s0; + } + + function peg$parseStringLiteral() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 34) { + s1 = peg$c23; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r0.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + if (s3 === peg$FAILED) { + s3 = peg$parseEscapeSequence(); + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r0.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + if (s3 === peg$FAILED) { + s3 = peg$parseEscapeSequence(); + } + } + if (input.charCodeAt(peg$currPos) === 34) { + s3 = peg$c23; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f10(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 39) { + s1 = peg$c24; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r1.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e26); } + } + if (s3 === peg$FAILED) { + s3 = peg$parseEscapeSequence(); + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r1.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e26); } + } + if (s3 === peg$FAILED) { + s3 = peg$parseEscapeSequence(); + } + } + if (input.charCodeAt(peg$currPos) === 39) { + s3 = peg$c24; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f11(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + function peg$parseEscapeSequence() { + let s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c25; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + if (s1 !== peg$FAILED) { + s2 = input.charAt(peg$currPos); + if (peg$r2.test(s2)) { + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e28); } + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f12(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseNumberLiteral() { + let s0, s1, s2; + + s0 = peg$currPos; + s1 = []; + s2 = input.charAt(peg$currPos); + if (peg$r3.test(s2)) { + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = input.charAt(peg$currPos); + if (peg$r3.test(s2)) { + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f13(s1); + } + s0 = s1; + + return s0; + } + + function peg$parseIdentifier() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = input.charAt(peg$currPos); + if (peg$r4.test(s1)) { + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e30); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } + } + } + peg$savedPos = s0; + s0 = peg$f14(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parse_() { + let s0, s1; + + peg$silentFails++; + s0 = []; + s1 = peg$parseWS(); + if (s1 === peg$FAILED) { + s1 = peg$parseLineComment(); + if (s1 === peg$FAILED) { + s1 = peg$parseBlockComment(); + } + } + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = peg$parseWS(); + if (s1 === peg$FAILED) { + s1 = peg$parseLineComment(); + if (s1 === peg$FAILED) { + s1 = peg$parseBlockComment(); + } + } + } + peg$silentFails--; + + return s0; + } + + function peg$parseWS() { + let s0, s1; + + s0 = []; + s1 = input.charAt(peg$currPos); + if (peg$r6.test(s1)) { + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + if (s1 !== peg$FAILED) { + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = input.charAt(peg$currPos); + if (peg$r6.test(s1)) { + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + } + } else { + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLineComment() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c26) { + s1 = peg$c26; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r7.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r7.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + } + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseBlockComment() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c27) { + s1 = peg$c27; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e35); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c28) { + s5 = peg$c28; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e36); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c28) { + s5 = peg$c28; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e36); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + if (input.substr(peg$currPos, 2) === peg$c28) { + s3 = peg$c28; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e36); } + } + if (s3 !== peg$FAILED) { + s1 = [s1, s2, s3]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + + // Helper functions + function makeVariable(name, path) { + return { type: 'Variable', name, path: path || [] }; + } + + function makePredicate(name, args) { + return { type: 'Predicate', name, args: args || [] }; + } + + function makeChallengePredicate(name, args) { + return { type: 'Predicate', name, args: args || [], challenge: true }; + } + + function makeFusion(expressions, aggregator) { + return { type: 'Fusion', aggregator, expressions }; + } + + function makeDefeasible(primary, exception) { + return { type: 'Defeasible', primary, exception }; + } + + peg$result = peg$startRuleFunction(); + + const peg$success = (peg$result !== peg$FAILED && peg$currPos === input.length); + function peg$throw() { + if (peg$result !== peg$FAILED && peg$currPos < input.length) { + peg$fail(peg$endExpectation()); + } + + throw peg$buildStructuredError( + peg$maxFailExpected, + peg$maxFailPos < input.length ? peg$getUnicode(peg$maxFailPos) : null, + peg$maxFailPos < input.length + ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) + : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) + ); + } + if (options.peg$library) { + return /** @type {any} */ ({ + peg$result, + peg$currPos, + peg$FAILED, + peg$maxFailExpected, + peg$maxFailPos, + peg$success, + peg$throw: peg$success ? undefined : peg$throw, + }); + } + if (peg$success) { + return peg$result; + } else { + peg$throw(); + } +} + +const peg$allowedStartRules = [ + "Expression" +]; + +export { + peg$allowedStartRules as StartRules, + peg$SyntaxError as SyntaxError, + peg$parse as parse +}; diff --git a/src/parser/GeneratedParser.js b/src/parser/GeneratedParser.js new file mode 100644 index 0000000..e61da22 --- /dev/null +++ b/src/parser/GeneratedParser.js @@ -0,0 +1,5186 @@ +// @generated by Peggy 5.1.0. +// +// https://peggyjs.org/ + + +class peg$SyntaxError extends SyntaxError { + constructor(message, expected, found, location) { + super(message); + this.expected = expected; + this.found = found; + this.location = location; + this.name = "SyntaxError"; + } + + format(sources) { + let str = "Error: " + this.message; + if (this.location) { + let src = null; + const st = sources.find(s => s.source === this.location.source); + if (st) { + src = st.text.split(/\r\n|\n|\r/g); + } + const s = this.location.start; + const offset_s = (this.location.source && (typeof this.location.source.offset === "function")) + ? this.location.source.offset(s) + : s; + const loc = this.location.source + ":" + offset_s.line + ":" + offset_s.column; + if (src) { + const e = this.location.end; + const filler = "".padEnd(offset_s.line.toString().length, " "); + const line = src[s.line - 1]; + const last = s.line === e.line ? e.column : line.length + 1; + const hatLen = (last - s.column) || 1; + str += "\n --> " + loc + "\n" + + filler + " |\n" + + offset_s.line + " | " + line + "\n" + + filler + " | " + "".padEnd(s.column - 1, " ") + + "".padEnd(hatLen, "^"); + } else { + str += "\n at " + loc; + } + } + return str; + } + + static buildMessage(expected, found) { + function hex(ch) { + return ch.codePointAt(0).toString(16).toUpperCase(); + } + + const nonPrintable = Object.prototype.hasOwnProperty.call(RegExp.prototype, "unicode") + ? new RegExp("[\\p{C}\\p{Mn}\\p{Mc}]", "gu") + : null; + function unicodeEscape(s) { + if (nonPrintable) { + return s.replace(nonPrintable, ch => "\\u{" + hex(ch) + "}"); + } + return s; + } + + function literalEscape(s) { + return unicodeEscape(s + .replace(/\\/g, "\\\\") + .replace(/"/g, "\\\"") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch)) + .replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch))); + } + + function classEscape(s) { + return unicodeEscape(s + .replace(/\\/g, "\\\\") + .replace(/\]/g, "\\]") + .replace(/\^/g, "\\^") + .replace(/-/g, "\\-") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch)) + .replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch))); + } + + const DESCRIBE_EXPECTATION_FNS = { + literal(expectation) { + return "\"" + literalEscape(expectation.text) + "\""; + }, + + class(expectation) { + const escapedParts = expectation.parts.map( + part => (Array.isArray(part) + ? classEscape(part[0]) + "-" + classEscape(part[1]) + : classEscape(part)) + ); + + return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]" + (expectation.unicode ? "u" : ""); + }, + + any() { + return "any character"; + }, + + end() { + return "end of input"; + }, + + other(expectation) { + return expectation.description; + }, + }; + + function describeExpectation(expectation) { + return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation); + } + + function describeExpected(expected) { + const descriptions = expected.map(describeExpectation); + descriptions.sort(); + + if (descriptions.length > 0) { + let j = 1; + for (let i = 1; i < descriptions.length; i++) { + if (descriptions[i - 1] !== descriptions[i]) { + descriptions[j] = descriptions[i]; + j++; + } + } + descriptions.length = j; + } + + switch (descriptions.length) { + case 1: + return descriptions[0]; + + case 2: + return descriptions[0] + " or " + descriptions[1]; + + default: + return descriptions.slice(0, -1).join(", ") + + ", or " + + descriptions[descriptions.length - 1]; + } + } + + function describeFound(found) { + return found ? "\"" + literalEscape(found) + "\"" : "end of input"; + } + + return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found."; + } +} + +function peg$parse(input, options) { + options = options !== undefined ? options : {}; + + const peg$FAILED = {}; + const peg$source = options.grammarSource; + + const peg$startRuleFunctions = { + Program: peg$parseProgram, + }; + let peg$startRuleFunction = peg$parseProgram; + + const peg$c0 = "definition"; + const peg$c1 = "type"; + const peg$c2 = "{"; + const peg$c3 = "}"; + const peg$c4 = ":"; + const peg$c5 = "[]"; + const peg$c6 = "fact"; + const peg$c7 = "relation"; + const peg$c8 = "*"; + const peg$c9 = "("; + const peg$c10 = ")"; + const peg$c11 = "source"; + const peg$c12 = "within"; + const peg$c13 = "evidence"; + const peg$c14 = "measure"; + const peg$c15 = "return"; + const peg$c16 = "NEVER"; + const peg$c17 = "ALWAYS"; + const peg$c18 = "REQUIRES"; + const peg$c19 = "WHEN"; + const peg$c20 = "UNLESS"; + const peg$c21 = "|"; + const peg$c22 = "fusion"; + const peg$c23 = ","; + const peg$c24 = "with"; + const peg$c25 = "aggregate"; + const peg$c26 = "USING"; + const peg$c27 = "PROVIDES"; + const peg$c28 = "BEHAVES"; + const peg$c29 = "AS"; + const peg$c30 = "edge"; + const peg$c31 = "transitive"; + const peg$c32 = "hierarchical"; + const peg$c33 = "symmetrical_graph"; + const peg$c34 = "symmetrical"; + const peg$c35 = "limit"; + const peg$c36 = "decaying"; + const peg$c37 = "up"; + const peg$c38 = "down"; + const peg$c39 = "neutral"; + const peg$c40 = "stable"; + const peg$c41 = "hourly"; + const peg$c42 = "daily"; + const peg$c43 = "weekly"; + const peg$c44 = "monthly"; + const peg$c45 = "blurring"; + const peg$c46 = "fixed"; + const peg$c47 = "adaptive"; + const peg$c48 = "confidence"; + const peg$c49 = "confidence_90"; + const peg$c50 = "confidence_95"; + const peg$c51 = "confidence_99"; + const peg$c52 = "ttl"; + const peg$c53 = "CACHE"; + const peg$c54 = "eager"; + const peg$c55 = "lazy"; + const peg$c56 = "||"; + const peg$c57 = "&&"; + const peg$c58 = "is"; + const peg$c59 = "=="; + const peg$c60 = "!="; + const peg$c61 = ">="; + const peg$c62 = "<="; + const peg$c63 = "NOT"; + const peg$c64 = "!"; + const peg$c65 = "."; + const peg$c66 = "\""; + const peg$c67 = "\\"; + const peg$c68 = "'"; + const peg$c69 = "true"; + const peg$c70 = "false"; + const peg$c71 = "//"; + const peg$c72 = "/*"; + const peg$c73 = "*/"; + + const peg$r0 = /^[<>]/; + const peg$r1 = /^[+\-]/; + const peg$r2 = /^[*\/]/; + const peg$r3 = /^["\\]/; + const peg$r4 = /^['\\]/; + const peg$r5 = /^[0-9]/; + const peg$r6 = /^[dhmw]/; + const peg$r7 = /^[a-zA-Z_]/; + const peg$r8 = /^[a-zA-Z0-9_]/; + const peg$r9 = /^[ \t]/; + const peg$r10 = /^[ \t\r\n]/; + const peg$r11 = /^[^\r\n]/; + + const peg$e0 = peg$otherExpectation("A type definition"); + const peg$e1 = peg$literalExpectation("definition", false); + const peg$e2 = peg$literalExpectation("type", false); + const peg$e3 = peg$literalExpectation("{", false); + const peg$e4 = peg$literalExpectation("}", false); + const peg$e5 = peg$literalExpectation(":", false); + const peg$e6 = peg$literalExpectation("[]", false); + const peg$e7 = peg$otherExpectation("A statement of fact (or relation in ADR-000)"); + const peg$e8 = peg$literalExpectation("fact", false); + const peg$e9 = peg$literalExpectation("relation", false); + const peg$e10 = peg$literalExpectation("*", false); + const peg$e11 = peg$literalExpectation("(", false); + const peg$e12 = peg$literalExpectation(")", false); + const peg$e13 = peg$otherExpectation("An injectable source (proof provider)"); + const peg$e14 = peg$literalExpectation("source", false); + const peg$e15 = peg$otherExpectation("A freshness constraint on a source"); + const peg$e16 = peg$literalExpectation("within", false); + const peg$e17 = peg$otherExpectation("An evidence rule"); + const peg$e18 = peg$literalExpectation("evidence", false); + const peg$e19 = peg$otherExpectation("A derived measurement or value"); + const peg$e20 = peg$literalExpectation("measure", false); + const peg$e21 = peg$literalExpectation("return", false); + const peg$e22 = peg$literalExpectation("NEVER", false); + const peg$e23 = peg$literalExpectation("ALWAYS", false); + const peg$e24 = peg$literalExpectation("REQUIRES", false); + const peg$e25 = peg$literalExpectation("WHEN", false); + const peg$e26 = peg$literalExpectation("UNLESS", false); + const peg$e27 = peg$literalExpectation("|", false); + const peg$e28 = peg$literalExpectation("fusion", false); + const peg$e29 = peg$literalExpectation(",", false); + const peg$e30 = peg$literalExpectation("with", false); + const peg$e31 = peg$literalExpectation("aggregate", false); + const peg$e32 = peg$literalExpectation("USING", false); + const peg$e33 = peg$literalExpectation("PROVIDES", false); + const peg$e34 = peg$literalExpectation("BEHAVES", false); + const peg$e35 = peg$literalExpectation("AS", false); + const peg$e36 = peg$literalExpectation("edge", false); + const peg$e37 = peg$literalExpectation("transitive", false); + const peg$e38 = peg$literalExpectation("hierarchical", false); + const peg$e39 = peg$literalExpectation("symmetrical_graph", false); + const peg$e40 = peg$literalExpectation("symmetrical", false); + const peg$e41 = peg$literalExpectation("limit", false); + const peg$e42 = peg$literalExpectation("decaying", false); + const peg$e43 = peg$literalExpectation("up", false); + const peg$e44 = peg$literalExpectation("down", false); + const peg$e45 = peg$literalExpectation("neutral", false); + const peg$e46 = peg$literalExpectation("stable", false); + const peg$e47 = peg$literalExpectation("hourly", false); + const peg$e48 = peg$literalExpectation("daily", false); + const peg$e49 = peg$literalExpectation("weekly", false); + const peg$e50 = peg$literalExpectation("monthly", false); + const peg$e51 = peg$literalExpectation("blurring", false); + const peg$e52 = peg$literalExpectation("fixed", false); + const peg$e53 = peg$literalExpectation("adaptive", false); + const peg$e54 = peg$literalExpectation("confidence", false); + const peg$e55 = peg$literalExpectation("confidence_90", false); + const peg$e56 = peg$literalExpectation("confidence_95", false); + const peg$e57 = peg$literalExpectation("confidence_99", false); + const peg$e58 = peg$literalExpectation("ttl", false); + const peg$e59 = peg$literalExpectation("CACHE", false); + const peg$e60 = peg$literalExpectation("eager", false); + const peg$e61 = peg$literalExpectation("lazy", false); + const peg$e62 = peg$literalExpectation("||", false); + const peg$e63 = peg$literalExpectation("&&", false); + const peg$e64 = peg$literalExpectation("is", false); + const peg$e65 = peg$literalExpectation("==", false); + const peg$e66 = peg$literalExpectation("!=", false); + const peg$e67 = peg$literalExpectation(">=", false); + const peg$e68 = peg$literalExpectation("<=", false); + const peg$e69 = peg$classExpectation(["<", ">"], false, false, false); + const peg$e70 = peg$classExpectation(["+", "-"], false, false, false); + const peg$e71 = peg$classExpectation(["*", "/"], false, false, false); + const peg$e72 = peg$literalExpectation("NOT", false); + const peg$e73 = peg$literalExpectation("!", false); + const peg$e74 = peg$literalExpectation(".", false); + const peg$e75 = peg$otherExpectation("The non-recursive base for an expression chain"); + const peg$e76 = peg$otherExpectation("A string literal"); + const peg$e77 = peg$literalExpectation("\"", false); + const peg$e78 = peg$classExpectation(["\"", "\\"], false, false, false); + const peg$e79 = peg$anyExpectation(); + const peg$e80 = peg$literalExpectation("\\", false); + const peg$e81 = peg$literalExpectation("'", false); + const peg$e82 = peg$classExpectation(["'", "\\"], false, false, false); + const peg$e83 = peg$otherExpectation("A floating-point number"); + const peg$e84 = peg$classExpectation([["0", "9"]], false, false, false); + const peg$e85 = peg$otherExpectation("An integer"); + const peg$e86 = peg$otherExpectation("A boolean literal"); + const peg$e87 = peg$literalExpectation("true", false); + const peg$e88 = peg$literalExpectation("false", false); + const peg$e89 = peg$otherExpectation("A time duration literal"); + const peg$e90 = peg$classExpectation(["d", "h", "m", "w"], false, false, false); + const peg$e91 = peg$classExpectation([["a", "z"], ["A", "Z"], "_"], false, false, false); + const peg$e92 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"], "_"], false, false, false); + const peg$e93 = peg$classExpectation([" ", "\t"], false, false, false); + const peg$e94 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false, false); + const peg$e95 = peg$literalExpectation("//", false); + const peg$e96 = peg$classExpectation(["\r", "\n"], true, false, false); + const peg$e97 = peg$literalExpectation("/*", false); + const peg$e98 = peg$literalExpectation("*/", false); + + function peg$f0(statements) { + const allStatements = statements.map(s => s[0]); + return { + type: "Program", + body: allStatements, + definitions: allStatements.filter(s => s.type === "Definition"), + facts: allStatements.filter(s => s.type === "Fact"), + evidence: allStatements.filter(s => s.type === "Evidence"), + measures: allStatements.filter(s => s.type === "Measure"), + sources: allStatements.filter(s => s.type === "Source") + }; + } + function peg$f1(name, fields) { + return { type: "Definition", name, fields: fields.map(f => f[0]) }; + } + function peg$f2(name, fieldType, isArray, behavior, cache) { + return { + type: "Field", + name, + fieldType, + isArray: !!isArray, + behavior: behavior || null, + cache: cache || null + }; + } + function peg$f3(star, name, params, behavior, properties, cache, limit) { + return { + type: "Fact", + name, + params: params || [], + behavior: behavior || null, + properties: properties.map(p => p[0]), + cache: cache || null, + limit: limit || null, + injectable: !!star + }; + } + function peg$f4(star, name, params, provides, within) { + return { + type: "Source", + name, + params: params || [], + provides: provides || null, + injectable: !!star, + within: within || null + }; + } + function peg$f5(duration) { return duration; } + function peg$f6(star, name, params, limit, body, provides) { + return { + type: "Evidence", + name, + params: params || [], + limit: limit || null, + body, + provides: provides || null, + challenge: !!star + }; + } + function peg$f7(name, params, body, provides) { + return { + type: "Measure", + name, + params: params || [], + body, + provides: provides || null + }; + } + function peg$f8(statements) { + return { type: "EvidenceBody", statements: statements.map(s => s[0]) }; + } + function peg$f9(statements, returnStmt) { + return { + type: "MeasureBody", + statements: statements.map(s => s[0]), + returnStatement: returnStmt || null + }; + } + function peg$f10(expression) { + return { type: "ReturnStatement", expression }; + } + function peg$f11(type, condition) { + return { type: "DefeasibleLogic", logicType: type, condition }; + } + function peg$f12(condition, defeater) { + return { type: "DefeasibleLogic", logicType: "WHEN", condition, defeater }; + } + function peg$f13(condition) { + return { type: "DefeasibleLogic", logicType: "WHEN", condition }; + } + function peg$f14(predicate, binding, body, limit, withClause) { + return { + type: "PatternMatch", + predicate, + binding: binding || null, + limit: limit || null, + body, + withClause: withClause || null + }; + } + function peg$f15(measure, variable, fusionStrategy, body, limit) { + return { + type: "CollectionProcessing", + measure, + variable, + fusion: fusionStrategy ? { strategy: fusionStrategy[1] } : null, + body, + limit: limit || null + }; + } + function peg$f16(name, args) { + return { type: "Predicate", name, args: args || [] }; + } + function peg$f17(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f18(name) { return { type: "Wildcard", name }; } + function peg$f19(name) { return name; } + function peg$f20(condition) { return condition; } + function peg$f21(strategy, expressions) { + return { type: "Fusion", strategy, expressions }; + } + function peg$f22(expressions, using) { + return { type: "Aggregation", expressions, using: using || null }; + } + function peg$f23(method) { return method; } + function peg$f24(name) { return { type: "TypeName", name }; } + function peg$f25(literal) { return { type: "TypeName", name: literal.value }; } + function peg$f26(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f27(name, paramType, isArray) { + return { type: "Parameter", name, paramType, isArray: !!isArray }; + } + function peg$f28(providesType) { return providesType; } + function peg$f29(behavior) { + return { type: "BehaviorAnnotation", behavior }; + } + function peg$f30() { return "transitive"; } + function peg$f31() { return "symmetrical"; } + function peg$f32(value) { return value; } + function peg$f33(b) { return b; } + function peg$f34(direction, period) { + return { type: "Behavior", behaviorType: "decay", direction, period }; + } + function peg$f35(mode, confidence) { + return { type: "Behavior", behaviorType: "blur", mode, confidence: confidence ? confidence[1] : null }; + } + function peg$f36(duration) { + return { type: "Behavior", behaviorType: "ttl", duration }; + } + function peg$f37(directive) { return directive; } + function peg$f38(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f39(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f40(head, typeName) { + return { type: "BinaryExpression", operator: "is", left: head, right: typeName }; + } + function peg$f41(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f42(head, right) { + return { type: "BinaryExpression", operator: "within", left: head, right }; + } + function peg$f43(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f44(head, tail) { return buildLeftAssoc(head, tail); } + function peg$f45(operator, operand) { return { type: "UnaryExpression", operator: "NOT", operand }; } + function peg$f46(primary, binding) { + if (binding) { + return { type: "BindingAccess", expression: primary, binding }; + } + return primary; + } + function peg$f47(head, tail) { + return tail.reduce((obj, part) => { + return { + type: "AttributeAccess", + object: obj, + attribute: part[3], // The Identifier is the 4th element (index 3) + location: location() + }; + }, head); + } + function peg$f48(expr) { return expr; } + function peg$f49(name, args) { + return { type: "PredicateCall", name, args: args || [], challenge: true }; + } + function peg$f50(name, args) { + return { type: "PredicateCall", name, args: args || [] }; + } + function peg$f51(name) { return { type: "Variable", name }; } + function peg$f52(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f53(head, tail) { + return [head, ...tail.map(t => t[3])]; + } + function peg$f54(chars) { + return { type: "Literal", value: JSON.parse(text()) }; + } + function peg$f55(chars) { + return { type: "Literal", value: JSON.parse("\"" + chars.map(c => c[0] === '\\' ? c[1] : c[1]).join('') + "\"") }; + } + function peg$f56(value) { return { type: "Literal", value: parseFloat(text()) }; } + function peg$f57(value) { return { type: "Literal", value: parseInt(text(), 10) }; } + function peg$f58(value) { return { type: "Literal", value: value === "true" }; } + function peg$f59(value) { return { type: "Literal", value: text(), unit: text().slice(-1) }; } + function peg$f60(name) { return name; } + let peg$currPos = options.peg$currPos | 0; + let peg$savedPos = peg$currPos; + const peg$posDetailsCache = [{ line: 1, column: 1 }]; + let peg$maxFailPos = peg$currPos; + let peg$maxFailExpected = options.peg$maxFailExpected || []; + let peg$silentFails = options.peg$silentFails | 0; + + let peg$result; + + if (options.startRule) { + if (!(options.startRule in peg$startRuleFunctions)) { + throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); + } + + peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; + } + + function text() { + return input.substring(peg$savedPos, peg$currPos); + } + + function offset() { + return peg$savedPos; + } + + function range() { + return { + source: peg$source, + start: peg$savedPos, + end: peg$currPos, + }; + } + + function location() { + return peg$computeLocation(peg$savedPos, peg$currPos); + } + + function expected(description, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildStructuredError( + [peg$otherExpectation(description)], + input.substring(peg$savedPos, peg$currPos), + location + ); + } + + function error(message, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildSimpleError(message, location); + } + + function peg$getUnicode(pos = peg$currPos) { + const cp = input.codePointAt(pos); + if (cp === undefined) { + return ""; + } + return String.fromCodePoint(cp); + } + + function peg$literalExpectation(text, ignoreCase) { + return { type: "literal", text, ignoreCase }; + } + + function peg$classExpectation(parts, inverted, ignoreCase, unicode) { + return { type: "class", parts, inverted, ignoreCase, unicode }; + } + + function peg$anyExpectation() { + return { type: "any" }; + } + + function peg$endExpectation() { + return { type: "end" }; + } + + function peg$otherExpectation(description) { + return { type: "other", description }; + } + + function peg$computePosDetails(pos) { + let details = peg$posDetailsCache[pos]; + let p; + + if (details) { + return details; + } else { + if (pos >= peg$posDetailsCache.length) { + p = peg$posDetailsCache.length - 1; + } else { + p = pos; + while (!peg$posDetailsCache[--p]) {} + } + + details = peg$posDetailsCache[p]; + details = { + line: details.line, + column: details.column, + }; + + while (p < pos) { + if (input.charCodeAt(p) === 10) { + details.line++; + details.column = 1; + } else { + details.column++; + } + + p++; + } + + peg$posDetailsCache[pos] = details; + + return details; + } + } + + function peg$computeLocation(startPos, endPos, offset) { + const startPosDetails = peg$computePosDetails(startPos); + const endPosDetails = peg$computePosDetails(endPos); + + const res = { + source: peg$source, + start: { + offset: startPos, + line: startPosDetails.line, + column: startPosDetails.column, + }, + end: { + offset: endPos, + line: endPosDetails.line, + column: endPosDetails.column, + }, + }; + if (offset && peg$source && (typeof peg$source.offset === "function")) { + res.start = peg$source.offset(res.start); + res.end = peg$source.offset(res.end); + } + return res; + } + + function peg$fail(expected) { + if (peg$currPos < peg$maxFailPos) { return; } + + if (peg$currPos > peg$maxFailPos) { + peg$maxFailPos = peg$currPos; + peg$maxFailExpected = []; + } + + peg$maxFailExpected.push(expected); + } + + function peg$buildSimpleError(message, location) { + return new peg$SyntaxError(message, null, null, location); + } + + function peg$buildStructuredError(expected, found, location) { + return new peg$SyntaxError( + peg$SyntaxError.buildMessage(expected, found), + expected, + found, + location + ); + } + + function peg$parseProgram() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parse_(); + s2 = []; + s3 = peg$currPos; + s4 = peg$parseStatement(); + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parseStatement(); + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + s3 = peg$parse_(); + peg$savedPos = s0; + s0 = peg$f0(s2); + + return s0; + } + + function peg$parseStatement() { + let s0; + + s0 = peg$parseDefinition(); + if (s0 === peg$FAILED) { + s0 = peg$parseSource(); + if (s0 === peg$FAILED) { + s0 = peg$parseFact(); + if (s0 === peg$FAILED) { + s0 = peg$parseEvidence(); + if (s0 === peg$FAILED) { + s0 = peg$parseMeasure(); + } + } + } + } + + return s0; + } + + function peg$parseDefinition() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 10) === peg$c0) { + s1 = peg$c0; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c1) { + s1 = peg$c1; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = []; + s8 = peg$currPos; + s9 = peg$parseField(); + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + s9 = [s9, s10]; + s8 = s9; + } else { + peg$currPos = s8; + s8 = peg$FAILED; + } + while (s8 !== peg$FAILED) { + s7.push(s8); + s8 = peg$currPos; + s9 = peg$parseField(); + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + s9 = [s9, s10]; + s8 = s9; + } else { + peg$currPos = s8; + s8 = peg$FAILED; + } + } + if (input.charCodeAt(peg$currPos) === 125) { + s8 = peg$c3; + peg$currPos++; + } else { + s8 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s8 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f1(s3, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + + return s0; + } + + function peg$parseField() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 58) { + s3 = peg$c4; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e5); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseIdentifier(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c5) { + s7 = peg$c5; + peg$currPos += 2; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } + if (s7 === peg$FAILED) { + s7 = null; + } + s8 = peg$parse_(); + s9 = peg$parseBehavior(); + if (s9 === peg$FAILED) { + s9 = null; + } + s10 = peg$parse_(); + s11 = peg$parseCacheDirective(); + if (s11 === peg$FAILED) { + s11 = null; + } + peg$savedPos = s0; + s0 = peg$f2(s1, s5, s7, s9, s11); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseFact() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c6) { + s1 = peg$c6; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e8); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c7) { + s1 = peg$c7; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e9); } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 42) { + s3 = peg$c8; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s3 === peg$FAILED) { + s3 = null; + } + s4 = peg$parseIdentifier(); + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s6 = peg$c9; + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s6 !== peg$FAILED) { + s7 = peg$parse_(); + s8 = peg$parseParameterList(); + if (s8 === peg$FAILED) { + s8 = null; + } + s9 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s10 = peg$c10; + peg$currPos++; + } else { + s10 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + if (s10 !== peg$FAILED) { + s11 = peg$parse_(); + s12 = peg$parseBehaviorAnnotation(); + if (s12 === peg$FAILED) { + s12 = null; + } + s13 = peg$parse_(); + s14 = []; + s15 = peg$currPos; + s16 = peg$parseFactProperty(); + if (s16 !== peg$FAILED) { + s17 = peg$parse_(); + s16 = [s16, s17]; + s15 = s16; + } else { + peg$currPos = s15; + s15 = peg$FAILED; + } + while (s15 !== peg$FAILED) { + s14.push(s15); + s15 = peg$currPos; + s16 = peg$parseFactProperty(); + if (s16 !== peg$FAILED) { + s17 = peg$parse_(); + s16 = [s16, s17]; + s15 = s16; + } else { + peg$currPos = s15; + s15 = peg$FAILED; + } + } + s15 = peg$parseCacheDirective(); + if (s15 === peg$FAILED) { + s15 = null; + } + s16 = peg$parse_(); + s17 = peg$parseLimit(); + if (s17 === peg$FAILED) { + s17 = null; + } + peg$savedPos = s0; + s0 = peg$f3(s3, s4, s8, s12, s14, s15, s17); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e7); } + } + + return s0; + } + + function peg$parseSource() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c11) { + s1 = peg$c11; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 42) { + s3 = peg$c8; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s3 === peg$FAILED) { + s3 = null; + } + s4 = peg$parseIdentifier(); + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s6 = peg$c9; + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s6 !== peg$FAILED) { + s7 = peg$parse_(); + s8 = peg$parseParameterList(); + if (s8 === peg$FAILED) { + s8 = null; + } + s9 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s10 = peg$c10; + peg$currPos++; + } else { + s10 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + if (s10 !== peg$FAILED) { + s11 = peg$parse_(); + s12 = peg$parseProvides(); + if (s12 === peg$FAILED) { + s12 = null; + } + s13 = peg$parse_(); + s14 = peg$parseWithinClause(); + if (s14 === peg$FAILED) { + s14 = null; + } + peg$savedPos = s0; + s0 = peg$f4(s3, s4, s8, s12, s14); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e13); } + } + + return s0; + } + + function peg$parseWithinClause() { + let s0, s1, s2, s3; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c12) { + s1 = peg$c12; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseDuration(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f5(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e15); } + } + + return s0; + } + + function peg$parseEvidence() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c13) { + s1 = peg$c13; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 42) { + s3 = peg$c8; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s3 === peg$FAILED) { + s3 = null; + } + s4 = peg$parseIdentifier(); + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s6 = peg$c9; + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s6 !== peg$FAILED) { + s7 = peg$parse_(); + s8 = peg$parseParameterList(); + if (s8 === peg$FAILED) { + s8 = null; + } + s9 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s10 = peg$c10; + peg$currPos++; + } else { + s10 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + if (s10 !== peg$FAILED) { + s11 = peg$parse_(); + s12 = peg$parseLimit(); + if (s12 === peg$FAILED) { + s12 = null; + } + s13 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s14 = peg$c2; + peg$currPos++; + } else { + s14 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s14 !== peg$FAILED) { + s15 = peg$parse_(); + s16 = peg$parseEvidenceBody(); + s17 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s18 = peg$c3; + peg$currPos++; + } else { + s18 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s18 !== peg$FAILED) { + s19 = peg$parse_(); + s20 = peg$parseProvides(); + if (s20 === peg$FAILED) { + s20 = null; + } + peg$savedPos = s0; + s0 = peg$f6(s3, s4, s8, s12, s16, s20); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } + + return s0; + } + + function peg$parseMeasure() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 7) === peg$c14) { + s1 = peg$c14; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e20); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s5 = peg$c9; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseParameterList(); + if (s7 === peg$FAILED) { + s7 = null; + } + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s9 = peg$c10; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s11 = peg$c2; + peg$currPos++; + } else { + s11 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s11 !== peg$FAILED) { + s12 = peg$parse_(); + s13 = peg$parseMeasureBody(); + s14 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s15 = peg$c3; + peg$currPos++; + } else { + s15 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s15 !== peg$FAILED) { + s16 = peg$parse_(); + s17 = peg$parseProvides(); + if (s17 === peg$FAILED) { + s17 = null; + } + peg$savedPos = s0; + s0 = peg$f7(s3, s7, s13, s17); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e19); } + } + + return s0; + } + + function peg$parseEvidenceBody() { + let s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = []; + s2 = peg$currPos; + s3 = peg$parseEvidenceStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$currPos; + s3 = peg$parseEvidenceStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + } + peg$savedPos = s0; + s1 = peg$f8(s1); + s0 = s1; + + return s0; + } + + function peg$parseEvidenceStatement() { + let s0; + + s0 = peg$parseDefeasibleLogic(); + if (s0 === peg$FAILED) { + s0 = peg$parseFusion(); + if (s0 === peg$FAILED) { + s0 = peg$parseCollectionProcessing(); + if (s0 === peg$FAILED) { + s0 = peg$parsePatternMatch(); + if (s0 === peg$FAILED) { + s0 = peg$parseLogicalOr(); + } + } + } + } + + return s0; + } + + function peg$parseMeasureBody() { + let s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = []; + s2 = peg$currPos; + s3 = peg$parseMeasureStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$currPos; + s3 = peg$parseMeasureStatement(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + } + s2 = peg$parseReturnStatement(); + if (s2 === peg$FAILED) { + s2 = null; + } + peg$savedPos = s0; + s0 = peg$f9(s1, s2); + + return s0; + } + + function peg$parseMeasureStatement() { + let s0; + + s0 = peg$parseFusion(); + if (s0 === peg$FAILED) { + s0 = peg$parseAggregation(); + if (s0 === peg$FAILED) { + s0 = peg$parsePatternMatch(); + if (s0 === peg$FAILED) { + s0 = peg$parseLogicalOr(); + } + } + } + + return s0; + } + + function peg$parseReturnStatement() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c15) { + s1 = peg$c15; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f10(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseDefeasibleLogic() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c16) { + s1 = peg$c16; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c17) { + s1 = peg$c17; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c18) { + s1 = peg$c18; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f11(s1, s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c19) { + s1 = peg$c19; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c20) { + s5 = peg$c20; + peg$currPos += 6; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e26); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse__(); + if (s6 !== peg$FAILED) { + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f12(s3, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c19) { + s1 = peg$c19; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f13(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + } + + return s0; + } + + function peg$parsePatternMatch() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13; + + s0 = peg$currPos; + s1 = peg$parsePatternPredicate(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseBindingClause(); + if (s3 === peg$FAILED) { + s3 = null; + } + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseEvidenceBody(); + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s9 = peg$c3; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s9 !== peg$FAILED) { + s10 = peg$parse_(); + s11 = peg$parseLimit(); + if (s11 === peg$FAILED) { + s11 = null; + } + s12 = peg$parse_(); + s13 = peg$parseWithClause(); + if (s13 === peg$FAILED) { + s13 = null; + } + peg$savedPos = s0; + s0 = peg$f14(s1, s3, s7, s11, s13); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseCollectionProcessing() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17; + + s0 = peg$currPos; + s1 = peg$parseLogicalOr(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 124) { + s3 = peg$c21; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseIdentifier(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 124) { + s7 = peg$c21; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + s9 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c22) { + s10 = peg$c22; + peg$currPos += 6; + } else { + s10 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e28); } + } + if (s10 !== peg$FAILED) { + s11 = peg$parse__(); + if (s11 !== peg$FAILED) { + s12 = peg$parseIdentifier(); + if (s12 !== peg$FAILED) { + s10 = [s10, s11, s12]; + s9 = s10; + } else { + peg$currPos = s9; + s9 = peg$FAILED; + } + } else { + peg$currPos = s9; + s9 = peg$FAILED; + } + } else { + peg$currPos = s9; + s9 = peg$FAILED; + } + if (s9 === peg$FAILED) { + s9 = null; + } + s10 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 123) { + s11 = peg$c2; + peg$currPos++; + } else { + s11 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s11 !== peg$FAILED) { + s12 = peg$parse_(); + s13 = peg$parseEvidenceBody(); + if (s13 !== peg$FAILED) { + s14 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s15 = peg$c3; + peg$currPos++; + } else { + s15 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s15 !== peg$FAILED) { + s16 = peg$parse_(); + s17 = peg$parseLimit(); + if (s17 === peg$FAILED) { + s17 = null; + } + peg$savedPos = s0; + s0 = peg$f15(s1, s5, s9, s13, s17); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePatternPredicate() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s3 = peg$c9; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parsePatternArgumentList(); + if (s5 === peg$FAILED) { + s5 = null; + } + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s7 = peg$c10; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + if (s7 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f16(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePatternArgumentList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parsePatternArgument(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c23; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parsePatternArgument(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c23; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parsePatternArgument(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f17(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePatternArgument() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 42) { + s1 = peg$c8; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f18(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseLogicalOr(); + } + + return s0; + } + + function peg$parseBindingClause() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 124) { + s1 = peg$c21; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 124) { + s5 = peg$c21; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f19(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseWithClause() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c24) { + s1 = peg$c24; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e30); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f20(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseFusion() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c22) { + s1 = peg$c22; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e28); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseExpressionList(); + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s9 = peg$c3; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s9 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f21(s3, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseAggregation() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 9) === peg$c25) { + s1 = peg$c25; + peg$currPos += 9; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s3 = peg$c2; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseExpressionList(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s7 = peg$c3; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s7 !== peg$FAILED) { + s8 = peg$parse_(); + s9 = peg$parseUsing(); + if (s9 === peg$FAILED) { + s9 = null; + } + peg$savedPos = s0; + s0 = peg$f22(s5, s9); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseUsing() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c26) { + s1 = peg$c26; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f23(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseTypeName() { + let s0, s1; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f24(s1); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseString(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f25(s1); + } + s0 = s1; + } + + return s0; + } + + function peg$parseParameterList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseParameter(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c23; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseParameter(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c23; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseParameter(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f26(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseParameter() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 58) { + s3 = peg$c4; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e5); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseIdentifier(); + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c5) { + s7 = peg$c5; + peg$currPos += 2; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } + if (s7 === peg$FAILED) { + s7 = null; + } + peg$savedPos = s0; + s0 = peg$f27(s1, s5, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseProvides() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c27) { + s1 = peg$c27; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseIdentifier(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f28(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseBehaviorAnnotation() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 7) === peg$c28) { + s1 = peg$c28; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c29) { + s3 = peg$c29; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e35); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c30) { + s5 = peg$c30; + peg$currPos += 4; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e36); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c31) { + s5 = peg$c31; + peg$currPos += 10; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 12) === peg$c32) { + s5 = peg$c32; + peg$currPos += 12; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e38); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 17) === peg$c33) { + s5 = peg$c33; + peg$currPos += 17; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e39); } + } + } + } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f29(s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseFactProperty() { + let s0, s1; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 10) === peg$c31) { + s1 = peg$c31; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f30(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 11) === peg$c34) { + s1 = peg$c34; + peg$currPos += 11; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e40); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f31(); + } + s0 = s1; + } + + return s0; + } + + function peg$parseLimit() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c35) { + s1 = peg$c35; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e41); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseInteger(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f32(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseBehavior() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 7) === peg$c28) { + s1 = peg$c28; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 123) { + s3 = peg$c2; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + s5 = peg$parseDecayBehavior(); + if (s5 === peg$FAILED) { + s5 = peg$parseBlurBehavior(); + if (s5 === peg$FAILED) { + s5 = peg$parseTTLBehavior(); + } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 125) { + s7 = peg$c3; + peg$currPos++; + } else { + s7 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s7 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f33(s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseDecayBehavior() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c36) { + s1 = peg$c36; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e42); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c37) { + s3 = peg$c37; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e43); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c38) { + s3 = peg$c38; + peg$currPos += 4; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e44); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c39) { + s3 = peg$c39; + peg$currPos += 7; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e45); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c40) { + s3 = peg$c40; + peg$currPos += 6; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e46); } + } + } + } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c41) { + s5 = peg$c41; + peg$currPos += 6; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e47); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c42) { + s5 = peg$c42; + peg$currPos += 5; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e48); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c43) { + s5 = peg$c43; + peg$currPos += 6; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e49); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c44) { + s5 = peg$c44; + peg$currPos += 7; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e50); } + } + } + } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f34(s3, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseBlurBehavior() { + let s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c45) { + s1 = peg$c45; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e51); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c46) { + s3 = peg$c46; + peg$currPos += 5; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e52); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c47) { + s3 = peg$c47; + peg$currPos += 8; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e53); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c48) { + s3 = peg$c48; + peg$currPos += 10; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e54); } + } + } + } + if (s3 !== peg$FAILED) { + s4 = peg$currPos; + s5 = peg$parse__(); + if (s5 !== peg$FAILED) { + if (input.substr(peg$currPos, 13) === peg$c49) { + s6 = peg$c49; + peg$currPos += 13; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e55); } + } + if (s6 === peg$FAILED) { + if (input.substr(peg$currPos, 13) === peg$c50) { + s6 = peg$c50; + peg$currPos += 13; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e56); } + } + if (s6 === peg$FAILED) { + if (input.substr(peg$currPos, 13) === peg$c51) { + s6 = peg$c51; + peg$currPos += 13; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e57); } + } + } + } + if (s6 !== peg$FAILED) { + s5 = [s5, s6]; + s4 = s5; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 === peg$FAILED) { + s4 = null; + } + peg$savedPos = s0; + s0 = peg$f35(s3, s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseTTLBehavior() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 3) === peg$c52) { + s1 = peg$c52; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e58); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseDuration(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f36(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseCacheDirective() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c53) { + s1 = peg$c53; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e59); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c54) { + s3 = peg$c54; + peg$currPos += 5; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e60); } + } + if (s3 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c55) { + s3 = peg$c55; + peg$currPos += 4; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e61); } + } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f37(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLogicalOr() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseLogicalAnd(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c56) { + s5 = peg$c56; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e62); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalAnd(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c56) { + s5 = peg$c56; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e62); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalAnd(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f38(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLogicalAnd() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseComparison(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c57) { + s5 = peg$c57; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e63); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c57) { + s5 = peg$c57; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e63); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f39(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseComparison() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseTemporalComparison(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c58) { + s3 = peg$c58; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e64); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + s5 = peg$parseTypeName(); + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f40(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseTemporalComparison(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c59) { + s5 = peg$c59; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e65); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c60) { + s5 = peg$c60; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e66); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c61) { + s5 = peg$c61; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e67); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c62) { + s5 = peg$c62; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e68); } + } + if (s5 === peg$FAILED) { + s5 = input.charAt(peg$currPos); + if (peg$r0.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e69); } + } + } + } + } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseTemporalComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.substr(peg$currPos, 2) === peg$c59) { + s5 = peg$c59; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e65); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c60) { + s5 = peg$c60; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e66); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c61) { + s5 = peg$c61; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e67); } + } + if (s5 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c62) { + s5 = peg$c62; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e68); } + } + if (s5 === peg$FAILED) { + s5 = input.charAt(peg$currPos); + if (peg$r0.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e69); } + } + } + } + } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseTemporalComparison(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f41(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + function peg$parseTemporalComparison() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseAddition(); + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + if (input.substr(peg$currPos, 6) === peg$c12) { + s3 = peg$c12; + peg$currPos += 6; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + s5 = peg$parseDuration(); + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f42(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseAddition(); + } + + return s0; + } + + function peg$parseAddition() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseMultiplication(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_i(); + s5 = input.charAt(peg$currPos); + if (peg$r1.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e70); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_i(); + s7 = peg$parseMultiplication(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_i(); + s5 = input.charAt(peg$currPos); + if (peg$r1.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e70); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_i(); + s7 = peg$parseMultiplication(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f43(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseMultiplication() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseUnary(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_i(); + s5 = input.charAt(peg$currPos); + if (peg$r2.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e71); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_i(); + s7 = peg$parseUnary(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_i(); + s5 = input.charAt(peg$currPos); + if (peg$r2.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e71); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_i(); + s7 = peg$parseUnary(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f44(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseUnary() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 3) === peg$c63) { + s1 = peg$c63; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e72); } + } + if (s1 === peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 33) { + s1 = peg$c64; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e73); } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseUnary(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f45(s1, s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parsePostfix(); + } + + return s0; + } + + function peg$parsePostfix() { + let s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$parseAttributeAccess(); + if (s1 === peg$FAILED) { + s1 = peg$parsePrimaryTerm(); + } + if (s1 !== peg$FAILED) { + s2 = peg$parseBindingClause(); + if (s2 === peg$FAILED) { + s2 = null; + } + peg$savedPos = s0; + s0 = peg$f46(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseAttributeAccess() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parsePrimaryTerm(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 46) { + s5 = peg$c65; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e74); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseIdentifier(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 46) { + s5 = peg$c65; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e74); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseIdentifier(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f47(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePrimaryTerm() { + let s0, s1, s2, s3, s4, s5; + + peg$silentFails++; + s0 = peg$parseChallengePredicate(); + if (s0 === peg$FAILED) { + s0 = peg$parseLiteral(); + if (s0 === peg$FAILED) { + s0 = peg$parsePredicateCall(); + if (s0 === peg$FAILED) { + s0 = peg$parseVariable(); + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 40) { + s1 = peg$c9; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse_(); + s3 = peg$parseLogicalOr(); + if (s3 !== peg$FAILED) { + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s5 = peg$c10; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f48(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + } + } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e75); } + } + + return s0; + } + + function peg$parseChallengePredicate() { + let s0, s1, s2, s3, s4, s5, s6, s7, s8; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 42) { + s1 = peg$c8; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseIdentifier(); + if (s2 !== peg$FAILED) { + s3 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 40) { + s4 = peg$c9; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s4 !== peg$FAILED) { + s5 = peg$parse_(); + s6 = peg$parseArgumentList(); + if (s6 === peg$FAILED) { + s6 = null; + } + s7 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s8 = peg$c10; + peg$currPos++; + } else { + s8 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + if (s8 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f49(s2, s6); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsePredicateCall() { + let s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 40) { + s2 = peg$c9; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s2 !== peg$FAILED) { + s3 = peg$parse_(); + s4 = peg$parseArgumentList(); + if (s4 === peg$FAILED) { + s4 = null; + } + s5 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 41) { + s6 = peg$c10; + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + if (s6 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f50(s1, s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseVariable() { + let s0, s1; + + s0 = peg$currPos; + s1 = peg$parseIdentifier(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f51(s1); + } + s0 = s1; + + return s0; + } + + function peg$parseArgumentList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseLogicalOr(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c23; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c23; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f52(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseExpressionList() { + let s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parseLogicalOr(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c23; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parse_(); + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c23; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parse_(); + s7 = peg$parseLogicalOr(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + peg$savedPos = s0; + s0 = peg$f53(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLiteral() { + let s0; + + s0 = peg$parseString(); + if (s0 === peg$FAILED) { + s0 = peg$parseFloat(); + if (s0 === peg$FAILED) { + s0 = peg$parseInteger(); + if (s0 === peg$FAILED) { + s0 = peg$parseBoolean(); + if (s0 === peg$FAILED) { + s0 = peg$parseDuration(); + } + } + } + } + + return s0; + } + + function peg$parseString() { + let s0, s1, s2, s3, s4, s5; + + peg$silentFails++; + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 34) { + s1 = peg$c66; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e77); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r3.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e78); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c67; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e80); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r3.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e78); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c67; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e80); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } + if (input.charCodeAt(peg$currPos) === 34) { + s3 = peg$c66; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e77); } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f54(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 39) { + s1 = peg$c68; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r4.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e82); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c67; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e80); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + s5 = input.charAt(peg$currPos); + if (peg$r4.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e82); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s4 = peg$c67; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e80); } + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } + if (input.charCodeAt(peg$currPos) === 39) { + s3 = peg$c68; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e81); } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f55(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e76); } + } + + return s0; + } + + function peg$parseFloat() { + let s0, s1, s2, s3, s4, s5; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$currPos; + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 46) { + s3 = peg$c65; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e74); } + } + if (s3 !== peg$FAILED) { + s4 = []; + s5 = input.charAt(peg$currPos); + if (peg$r5.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + if (s5 !== peg$FAILED) { + while (s5 !== peg$FAILED) { + s4.push(s5); + s5 = input.charAt(peg$currPos); + if (peg$r5.test(s5)) { + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + } + } else { + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + s2 = [s2, s3, s4]; + s1 = s2; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f56(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e83); } + } + + return s0; + } + + function peg$parseInteger() { + let s0, s1, s2; + + peg$silentFails++; + s0 = peg$currPos; + s1 = []; + s2 = input.charAt(peg$currPos); + if (peg$r5.test(s2)) { + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = input.charAt(peg$currPos); + if (peg$r5.test(s2)) { + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f57(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e85); } + } + + return s0; + } + + function peg$parseBoolean() { + let s0, s1; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c69) { + s1 = peg$c69; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e87); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c70) { + s1 = peg$c70; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e88); } + } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f58(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e86); } + } + + return s0; + } + + function peg$parseDuration() { + let s0, s1, s2, s3; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$currPos; + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r5.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e84); } + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = input.charAt(peg$currPos); + if (peg$r6.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e90); } + } + if (s3 !== peg$FAILED) { + s2 = [s2, s3]; + s1 = s2; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f59(s1); + } + s0 = s1; + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e89); } + } + + return s0; + } + + function peg$parseIdentifier() { + let s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + s2 = peg$parseKeyword(); + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = undefined; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + s3 = peg$currPos; + s4 = input.charAt(peg$currPos); + if (peg$r7.test(s4)) { + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e91); } + } + if (s4 !== peg$FAILED) { + s5 = []; + s6 = input.charAt(peg$currPos); + if (peg$r8.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e92); } + } + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = input.charAt(peg$currPos); + if (peg$r8.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e92); } + } + } + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + s2 = input.substring(s2, peg$currPos); + } else { + s2 = s3; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f60(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseKeyword() { + let s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 10) === peg$c0) { + s1 = peg$c0; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c1) { + s1 = peg$c1; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c6) { + s1 = peg$c6; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e8); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c7) { + s1 = peg$c7; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e9); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c13) { + s1 = peg$c13; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c14) { + s1 = peg$c14; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e20); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c28) { + s1 = peg$c28; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c29) { + s1 = peg$c29; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e35); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c53) { + s1 = peg$c53; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e59); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c36) { + s1 = peg$c36; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e42); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c45) { + s1 = peg$c45; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e51); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 3) === peg$c52) { + s1 = peg$c52; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e58); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c31) { + s1 = peg$c31; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 11) === peg$c34) { + s1 = peg$c34; + peg$currPos += 11; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e40); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 12) === peg$c32) { + s1 = peg$c32; + peg$currPos += 12; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e38); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 17) === peg$c33) { + s1 = peg$c33; + peg$currPos += 17; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e39); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c30) { + s1 = peg$c30; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e36); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c35) { + s1 = peg$c35; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e41); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c27) { + s1 = peg$c27; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c22) { + s1 = peg$c22; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e28); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 9) === peg$c25) { + s1 = peg$c25; + peg$currPos += 9; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c26) { + s1 = peg$c26; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c16) { + s1 = peg$c16; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c17) { + s1 = peg$c17; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c19) { + s1 = peg$c19; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c20) { + s1 = peg$c20; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e26); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 8) === peg$c18) { + s1 = peg$c18; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c24) { + s1 = peg$c24; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e30); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c69) { + s1 = peg$c69; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e87); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c70) { + s1 = peg$c70; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e88); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 3) === peg$c63) { + s1 = peg$c63; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e72); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c12) { + s1 = peg$c12; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c15) { + s1 = peg$c15; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c58) { + s1 = peg$c58; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e64); } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + peg$silentFails++; + s3 = input.charAt(peg$currPos); + if (peg$r8.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e92); } + } + peg$silentFails--; + if (s3 === peg$FAILED) { + s2 = undefined; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parse_() { + let s0, s1; + + s0 = []; + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + } + + return s0; + } + + function peg$parse_i() { + let s0, s1; + + s0 = []; + s1 = input.charAt(peg$currPos); + if (peg$r9.test(s1)) { + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e93); } + } + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = input.charAt(peg$currPos); + if (peg$r9.test(s1)) { + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e93); } + } + } + + return s0; + } + + function peg$parse__() { + let s0, s1; + + s0 = []; + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + if (s1 !== peg$FAILED) { + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = peg$parseWhiteSpace(); + if (s1 === peg$FAILED) { + s1 = peg$parseComment(); + } + } + } else { + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseWhiteSpace() { + let s0; + + s0 = input.charAt(peg$currPos); + if (peg$r10.test(s0)) { + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e94); } + } + + return s0; + } + + function peg$parseComment() { + let s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c71) { + s1 = peg$c71; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e95); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r11.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e96); } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r11.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e96); } + } + } + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c72) { + s1 = peg$c72; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e97); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c73) { + s5 = peg$c73; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e98); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$currPos; + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c73) { + s5 = peg$c73; + peg$currPos += 2; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e98); } + } + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + if (input.length > peg$currPos) { + s5 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e79); } + } + if (s5 !== peg$FAILED) { + s4 = [s4, s5]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + if (input.substr(peg$currPos, 2) === peg$c73) { + s3 = peg$c73; + peg$currPos += 2; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e98); } + } + if (s3 !== peg$FAILED) { + s1 = [s1, s2, s3]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + + // The location() function provides line/column info for error reporting. + // The text() function returns the matched text for a rule. + + // Helper function to build a left-associative binary expression tree. + function buildLeftAssoc(head, tail) { + return tail.reduce((result, element) => { + return { + type: "BinaryExpression", + operator: element[1], + left: result, + right: element[3], + location: location() + }; + }, head); + } + + peg$result = peg$startRuleFunction(); + + const peg$success = (peg$result !== peg$FAILED && peg$currPos === input.length); + function peg$throw() { + if (peg$result !== peg$FAILED && peg$currPos < input.length) { + peg$fail(peg$endExpectation()); + } + + throw peg$buildStructuredError( + peg$maxFailExpected, + peg$maxFailPos < input.length ? peg$getUnicode(peg$maxFailPos) : null, + peg$maxFailPos < input.length + ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) + : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) + ); + } + if (options.peg$library) { + return /** @type {any} */ ({ + peg$result, + peg$currPos, + peg$FAILED, + peg$maxFailExpected, + peg$maxFailPos, + peg$success, + peg$throw: peg$success ? undefined : peg$throw, + }); + } + if (peg$success) { + return peg$result; + } else { + peg$throw(); + } +} + +const peg$allowedStartRules = [ + "Program" +]; + +export { + peg$allowedStartRules as StartRules, + peg$SyntaxError as SyntaxError, + peg$parse as parse +}; diff --git a/src/parser/PeggyDSLParser.js b/src/parser/PeggyDSLParser.js new file mode 100644 index 0000000..4dc7deb --- /dev/null +++ b/src/parser/PeggyDSLParser.js @@ -0,0 +1,114 @@ +import * as GeneratedParser from './GeneratedParser.js'; + +/** + * Peggy-based DSL Parser + * Uses the generated parser from Peggy grammar + */ +export class PeggyDSLParser { + constructor() { + this.parser = GeneratedParser; + this.errors = []; + } + + /** + * Parse DSL text into AST + * @param {string} dslText - DSL text to parse + * @returns {ProgramNode} Parsed AST + */ + parse(dslText) { + this.errors = []; + + try { + const program = this.parser.parse(dslText); + return program; + } catch (error) { + this.errors.push(`Parse error: ${error.message}`); + + // If the error has location information, add it to the error + if (error.location) { + const location = error.location; + this.errors.push(`Location: line ${location.start.line}, column ${location.start.column}`); + } + + // If the error has expected/found information, add it + if (error.expected && error.found) { + this.errors.push(`Expected: ${error.expected.join(', ')}`); + this.errors.push(`Found: ${error.found}`); + } + + throw new Error(`Parsing failed: ${this.errors.join('; ')}`); + } + } + + /** + * Get parser errors from last parse + * @returns {string[]} Array of parser errors + */ + getErrors() { + return this.errors; + } + + /** + * Validate DSL text without throwing errors + * @param {string} dslText - DSL text to validate + * @returns {Object} Validation result with success status and errors + */ + validate(dslText) { + try { + const program = this.parse(dslText); + return { + success: true, + errors: [], + program: program + }; + } catch (error) { + return { + success: false, + errors: this.errors, + program: null + }; + } + } + + /** + * Parse with options + * @param {string} dslText - DSL text to parse + * @param {Object} options - Parser options + * @returns {ProgramNode} Parsed AST + */ + parseWithOptions(dslText, options = {}) { + this.errors = []; + + try { + const program = this.parser.parse(dslText, options); + return program; + } catch (error) { + this.errors.push(`Parse error: ${error.message}`); + + if (error.location) { + const location = error.location; + this.errors.push(`Location: line ${location.start.line}, column ${location.start.column}`); + } + + if (error.expected && error.found) { + this.errors.push(`Expected: ${error.expected.join(', ')}`); + this.errors.push(`Found: ${error.found}`); + } + + throw new Error(`Parsing failed: ${this.errors.join('; ')}`); + } + } + + /** + * Get parser information + * @returns {Object} Parser information + */ + getParserInfo() { + return { + name: 'PeggyDSLParser', + version: '1.0.0', + generated: true, + grammar: 'dsl.peggy' + }; + } +} diff --git a/src/parser/PeggyWrapper.js b/src/parser/PeggyWrapper.js new file mode 100644 index 0000000..569019d --- /dev/null +++ b/src/parser/PeggyWrapper.js @@ -0,0 +1,45 @@ +// Wrapper for Peggy-generated parser that handles ESM imports correctly +import { + ProgramNode, DefinitionNode, FieldNode, BehaviorNode, FactNode, ParameterNode, + EvidenceNode, EvidenceBodyNode, DirectEvidenceNode, PatternMatchNode, + DefeasibleLogicNode, FusionNode, PredicateNode, ExpressionNode, WithClauseNode, + MeasureNode, MeasureBodyNode, AggregationNode +} from '../nodes/index.js'; + +// Make AST nodes globally available to the generated parser +global.ProgramNode = ProgramNode; +global.DefinitionNode = DefinitionNode; +global.FieldNode = FieldNode; +global.BehaviorNode = BehaviorNode; +global.FactNode = FactNode; +global.ParameterNode = ParameterNode; +global.EvidenceNode = EvidenceNode; +global.EvidenceBodyNode = EvidenceBodyNode; +global.DirectEvidenceNode = DirectEvidenceNode; +global.PatternMatchNode = PatternMatchNode; +global.DefeasibleLogicNode = DefeasibleLogicNode; +global.FusionNode = FusionNode; +global.PredicateNode = PredicateNode; +global.ExpressionNode = ExpressionNode; +global.WithClauseNode = WithClauseNode; +global.MeasureNode = MeasureNode; +global.MeasureBodyNode = MeasureBodyNode; +global.AggregationNode = AggregationNode; + +// Import the generated parser +import { parse } from './GeneratedParser.js'; + +// Create a wrapper class that matches the expected interface +export class PeggyDSLParser { + static parse(text) { + console.log('PeggyDSLParser: Parsing text:', text.substring(0, 100) + '...'); + try { + const result = parse(text); + console.log('PeggyDSLParser: Parse result:', result); + return result; + } catch (error) { + console.error('PeggyDSLParser: Parse error:', error.message); + throw error; + } + } +} diff --git a/src/utils/ip-utils-fast.js b/src/utils/ip-utils-fast.js new file mode 100644 index 0000000..485764c --- /dev/null +++ b/src/utils/ip-utils-fast.js @@ -0,0 +1,165 @@ +/** + * Optimized IP Address Utilities + * + * High-performance versions for hot paths + */ + +// CIDR cache for repeated lookups +const cidrCache = new Map(); +const CIDR_CACHE_SIZE = 1000; + +/** + * Fast IPv4 check - less strict but much faster + * Only validates format, not strict numeric ranges + */ +export function isIPv4Fast(ip) { + if (typeof ip !== 'string') return false; + + // Quick length check (min: 7 for "0.0.0.0", max: 15 for "255.255.255.255") + if (ip.length < 7 || ip.length > 15) return false; + + let dots = 0; + for (let i = 0; i < ip.length; i++) { + const c = ip.charCodeAt(i); + if (c === 46) { // '.' + dots++; + } else if (c < 48 || c > 57) { // not 0-9 + return false; + } + } + return dots === 3; +} + +/** + * Ultra-fast IP to integer conversion + * Direct character parsing, no string splitting + */ +export function ipToIntFast(ip) { + let result = 0; + let octet = 0; + let shift = 24; + + for (let i = 0; i < ip.length; i++) { + const c = ip.charCodeAt(i); + if (c === 46) { // '.' + result |= (octet << shift); + octet = 0; + shift -= 8; + } else { + octet = octet * 10 + (c - 48); + } + } + + return (result | octet) >>> 0; +} + +/** + * Fast CIDR parsing with caching + */ +export function parseCidrCached(cidr) { + // Check cache first + let cached = cidrCache.get(cidr); + if (cached) return cached; + + // Parse and cache + const slashIdx = cidr.indexOf('/'); + if (slashIdx === -1) return null; + + const ip = cidr.slice(0, slashIdx); + const prefix = parseInt(cidr.slice(slashIdx + 1), 10); + const mask = -1 << (32 - prefix); + + cached = { + network: ipToIntFast(ip), + mask, + prefix + }; + + // Simple LRU - clear if too big + if (cidrCache.size >= CIDR_CACHE_SIZE) { + cidrCache.clear(); + } + cidrCache.set(cidr, cached); + + return cached; +} + +/** + * Ultra-fast IP in CIDR check + * Uses caching and optimized parsing + */ +export function isIpInCidrFast(ip, cidr) { + const cached = parseCidrCached(cidr); + if (!cached) return false; + + const ipInt = ipToIntFast(ip); + return (ipInt & cached.mask) === (cached.network & cached.mask); +} + +/** + * Fast private IP check using bit manipulation + */ +export function isPrivateIpFast(ip) { + const ipInt = ipToIntFast(ip); + + // 10.0.0.0/8: 0x0A000000 to 0x0AFFFFFF + if ((ipInt >>> 24) === 10) return true; + + // 172.16.0.0/12: 0xAC100000 to 0xAC1FFFFF + const high16 = ipInt >>> 16; + if (high16 >= 0xAC10 && high16 <= 0xAC1F) return true; + + // 192.168.0.0/16: 0xC0A80000 to 0xC0A8FFFF + if (high16 === 0xC0A8) return true; + + // 127.0.0.0/8: 0x7F000000 to 0x7FFFFFFF + if ((ipInt >>> 24) === 127) return true; + + // 169.254.0.0/16: 0xA9FE0000 to 0xA9FEFFFF + if (high16 === 0xA9FE) return true; + + return false; +} + +/** + * Optimized built-in function evaluator + * Direct dispatch without object lookups + */ +export function evaluateBuiltInFast(name, args) { + switch (name) { + case 'ip_in_cidr': + return isIpInCidrFast(args[0], args[1]); + case 'ip_is_private': + return isPrivateIpFast(args[0]); + case 'ip_is_loopback': + return (ipToIntFast(args[0]) >>> 24) === 127; + case 'ip_version': + return isIPv4Fast(args[0]) ? 4 : (args[0].includes(':') ? 6 : null); + case 'ip_equals': + return args[0] === args[1]; + case 'contains': + return String(args[0]).includes(String(args[1])); + case 'starts_with': + return String(args[0]).startsWith(String(args[1])); + case 'ends_with': + return String(args[0]).endsWith(String(args[1])); + case 'equals': + return args[0] === args[1]; + case 'greater_than': + return args[0] > args[1]; + case 'less_than': + return args[0] < args[1]; + case 'in_range': + return args[0] >= args[1] && args[0] <= args[2]; + case 'hour_of_day': + return new Date(args[0]).getHours(); + case 'day_of_week': + return new Date(args[0]).getDay(); + default: + throw new Error(`Unknown: ${name}`); + } +} + +// Re-export original functions for compatibility +export { isIPv4, isIPv6, isLoopbackIp, getIpVersion, normalizeIp } from './ip-utils.js'; +export { isIpInCidr, isPrivateIp } from './ip-utils.js'; diff --git a/src/utils/ip-utils.js b/src/utils/ip-utils.js new file mode 100644 index 0000000..c9ebb0b --- /dev/null +++ b/src/utils/ip-utils.js @@ -0,0 +1,123 @@ +/** + * IP Address Utilities + */ + +import { createRequire } from 'node:module'; + +/** + * Check if string is IPv4 + */ +export function isIPv4(ip) { + if (typeof ip !== 'string') return false; + const parts = ip.split('.'); + if (parts.length !== 4) return false; + + return parts.every(part => { + const num = parseInt(part, 10); + return !isNaN(num) && num >= 0 && num <= 255 && part === String(num); + }); +} + +/** + * Check if string is IPv6 + */ +export function isIPv6(ip) { + if (typeof ip !== 'string') return false; + // Simple check - contains colons and valid hex + return ip.includes(':') && /^[0-9a-fA-F:]+$/.test(ip); +} + +/** + * Check if IP is in private range (RFC 1918) + */ +export function isPrivateIp(ip) { + if (!isIPv4(ip)) return false; + + const parts = ip.split('.').map(Number); + const [a, b, c, d] = parts; + + // 10.0.0.0/8 + if (a === 10) return true; + + // 172.16.0.0/12 + if (a === 172 && b >= 16 && b <= 31) return true; + + // 192.168.0.0/16 + if (a === 192 && b === 168) return true; + + // 127.0.0.0/8 (loopback) + if (a === 127) return true; + + // 169.254.0.0/16 (link-local) + if (a === 169 && b === 254) return true; + + return false; +} + +/** + * Check if IP is loopback + */ +export function isLoopbackIp(ip) { + if (!isIPv4(ip)) return false; + return ip.startsWith('127.'); +} + +/** + * Convert IP to integer for range comparison + */ +export function ipToInt(ip) { + return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0; +} + +/** + * Parse CIDR notation + */ +export function parseCidr(cidr) { + const [ip, prefix] = cidr.split('/'); + const mask = -1 << (32 - parseInt(prefix, 10)); + return { ip: ipToInt(ip), mask }; +} + +/** + * Check if IP is in CIDR range + */ +export function isIpInCidr(ip, cidr) { + if (!isIPv4(ip)) return false; + + try { + const ipInt = ipToInt(ip); + const { ip: networkInt, mask } = parseCidr(cidr); + + return (ipInt & mask) === (networkInt & mask); + } catch (err) { + return false; + } +} + +/** + * Check if two IPs are equal + */ +export function ipEquals(ip1, ip2) { + return ip1 === ip2; +} + +/** + * Get IP version (4 or 6) + */ +export function getIpVersion(ip) { + if (isIPv4(ip)) return 4; + if (isIPv6(ip)) return 6; + return null; +} + +/** + * Normalize IP (remove leading zeros, etc.) + */ +export function normalizeIp(ip) { + if (!isIPv4(ip)) return ip; + + return ip + .split('.') + .map(part => parseInt(part, 10).toString()) + .join('.'); +} diff --git a/src/validation/DSLPrelude.js b/src/validation/DSLPrelude.js new file mode 100644 index 0000000..b74bd79 --- /dev/null +++ b/src/validation/DSLPrelude.js @@ -0,0 +1,37 @@ +export const DSL_PRELUDE = ` +// Built-in types and relations available in every graph. +// These are intended for request-scoped auth/session evidence (partial graph inputs). + +definition User { + id: string +} + +definition Account { + id: string + tier: string +} + +definition Device { + id: string + device_risk: number + auth_method: string + ip_address: string + user_agent: string +} + +definition AuthSession { + login_time: timestamp + last_login_time: timestamp + mfa_used: boolean + auth_method: string + ip_address: string + user_agent: string + expires_at: timestamp + device_risk: number +} + +fact session_for_user(user: User, session: AuthSession) +fact session_for_account(account: Account, session: AuthSession) +fact session_for_device(device: Device, session: AuthSession) +fact logged_in_as(device: Device, account: Account) +`; diff --git a/src/validation/DSLValidation.js b/src/validation/DSLValidation.js new file mode 100644 index 0000000..465feee --- /dev/null +++ b/src/validation/DSLValidation.js @@ -0,0 +1,1002 @@ +import { parse } from '../parser/GeneratedParser.js'; +import { DSL_PRELUDE } from './DSLPrelude.js'; + +const BUILTIN_TYPES = new Set(['string', 'number', 'boolean', 'timestamp', 'duration', 'object', 'any']); +const BUILTIN_CHALLENGES = new Set([ + 'mfa', + 'webauthn', + 'login', + 'sms', + 'voice', + 'magic_link', + 'email_verified', + 'oauth_session' +]); + +const DEFAULT_EVIDENCE_RETURN = 'boolean'; +const DEFAULT_MEASURE_RETURN = 'any'; + +export function validateDslText(dslText, options = {}) { + const preludeProgram = parsePrelude(options); + let program; + try { + program = parse(dslText); + } catch (error) { + const syntaxError = formatPegError(error, dslText, options.source || 'input'); + return { + success: false, + program: null, + errors: [formatValidationError(syntaxError)], + warnings: [], + errorDetails: [syntaxError] + }; + } + + const errors = []; + const warnings = []; + + const tables = buildSymbolTables(program, preludeProgram); + validateDefinitions(program, tables, errors, warnings, dslText); + validateFacts(program, tables, errors, warnings, dslText); + validateSources(program, tables, errors, warnings, dslText); + validateMeasures(program, tables, errors, warnings, dslText); + validateEvidence(program, tables, errors, warnings, dslText); + + return { + success: errors.length === 0, + program, + errors: errors.map(formatValidationError), + warnings: warnings.map(formatValidationError), + errorDetails: errors, + warningDetails: warnings + }; +} + +function buildSymbolTables(program, preludeProgram) { + const definitions = new Map(); + const facts = new Map(); + const evidence = new Map(); + const measures = new Map(); + const sources = new Map(); + const builtins = { + definitions: new Set(), + facts: new Set(), + evidence: new Set(), + measures: new Set(), + sources: new Set() + }; + + for (const def of preludeProgram?.definitions || []) { + definitions.set(def.name, def); + builtins.definitions.add(def.name); + } + for (const fact of preludeProgram?.facts || []) { + facts.set(fact.name, fact); + builtins.facts.add(fact.name); + } + for (const ev of preludeProgram?.evidence || []) { + evidence.set(ev.name, ev); + builtins.evidence.add(ev.name); + } + for (const measure of preludeProgram?.measures || []) { + measures.set(measure.name, measure); + builtins.measures.add(measure.name); + } + for (const source of preludeProgram?.sources || []) { + sources.set(source.name, source); + builtins.sources.add(source.name); + } + + for (const def of program.definitions || []) { + definitions.set(def.name, def); + } + for (const fact of program.facts || []) { + facts.set(fact.name, fact); + } + for (const ev of program.evidence || []) { + evidence.set(ev.name, ev); + } + for (const measure of program.measures || []) { + measures.set(measure.name, measure); + } + for (const source of program.sources || []) { + sources.set(source.name, source); + } + + return { definitions, facts, evidence, measures, sources, builtins }; +} + +function validateDefinitions(program, tables, errors, warnings, source) { + const seen = new Map(); + for (const def of program.definitions || []) { + if (tables.builtins?.definitions?.has(def.name)) { + errors.push(createError({ + message: `Type '${def.name}' is a built-in and cannot be redefined.`, + rule: 'Built-in types are reserved and cannot be overridden.', + fix: `Rename your type or remove the duplicate definition for '${def.name}'.`, + location: findLocation(source, `definition ${def.name}`), + context: formatContext(source, findLocation(source, def.name)) + })); + } + if (seen.has(def.name)) { + errors.push(createError({ + message: `Duplicate type definition '${def.name}'.`, + rule: 'Each type name must be unique within a program.', + fix: 'Rename one of the type definitions to a unique name.', + location: findLocation(source, `definition ${def.name}`), + context: formatContext(source, findLocation(source, def.name)) + })); + } + seen.set(def.name, def); + + for (const field of def.fields || []) { + if (!isTypeKnown(field.fieldType, tables)) { + errors.push(createError({ + message: `Unknown field type '${field.fieldType}' in '${def.name}.${field.name}'.`, + rule: 'Field types must be built-in or declared as a definition.', + fix: `Add a type definition for '${field.fieldType}' or correct the field type.`, + location: findLocation(source, field.fieldType), + context: formatContext(source, findLocation(source, def.name)) + })); + } + } + } +} + +function validateFacts(program, tables, errors, warnings, source) { + for (const fact of program.facts || []) { + if (tables.builtins?.facts?.has(fact.name)) { + errors.push(createError({ + message: `Fact '${fact.name}' is a built-in and cannot be redefined.`, + rule: 'Built-in facts are reserved and cannot be overridden.', + fix: `Rename your fact or remove the duplicate definition for '${fact.name}'.`, + location: findLocation(source, `fact ${fact.name}`), + context: formatContext(source, findLocation(source, fact.name)) + })); + } + const arity = fact.params ? fact.params.length : 0; + if (!fact.params || arity === 0) { + warnings.push(createError({ + message: `Fact '${fact.name}' declares no parameters.`, + rule: 'Facts should declare parameters to capture relation signatures.', + fix: `Add parameters to '${fact.name}', e.g. (${fact.name}(user: User, doc: Document)).`, + location: findLocation(source, `fact ${fact.name}`), + context: formatContext(source, findLocation(source, fact.name)) + })); + } + + const paramNames = new Set(); + for (const param of fact.params || []) { + if (paramNames.has(param.name)) { + errors.push(createError({ + message: `Duplicate parameter '${param.name}' in fact '${fact.name}'.`, + rule: 'Each parameter name must be unique within a fact signature.', + fix: 'Rename the parameter to a unique name.', + location: findLocation(source, param.name), + context: formatContext(source, findLocation(source, fact.name)) + })); + } + paramNames.add(param.name); + + if (!isTypeKnown(param.paramType, tables)) { + errors.push(createError({ + message: `Unknown parameter type '${param.paramType}' in fact '${fact.name}'.`, + rule: 'Parameter types must be built-in or declared as a definition.', + fix: `Add a type definition for '${param.paramType}' or correct the parameter type.`, + location: findLocation(source, param.paramType), + context: formatContext(source, findLocation(source, fact.name)) + })); + } + } + } +} + +function validateSources(program, tables, errors, warnings, source) { + for (const src of program.sources || []) { + if (tables.builtins?.sources?.has(src.name)) { + errors.push(createError({ + message: `Source '${src.name}' is a built-in and cannot be redefined.`, + rule: 'Built-in sources are reserved and cannot be overridden.', + fix: `Rename your source or remove the duplicate definition for '${src.name}'.`, + location: findLocation(source, `source ${src.name}`), + context: formatContext(source, findLocation(source, src.name)) + })); + } + + const returnType = src.provides || null; + if (returnType && !isTypeKnown(returnType, tables)) { + errors.push(createError({ + message: `Unknown return type '${returnType}' for source '${src.name}'.`, + rule: 'Source PROVIDES types must be built-in or declared as a definition.', + fix: `Add a type definition for '${returnType}' or correct the PROVIDES clause.`, + location: findLocation(source, returnType), + context: formatContext(source, findLocation(source, src.name)) + })); + } + + if (src.within && !isValidDuration(src.within)) { + errors.push(createError({ + message: `Invalid duration '${src.within?.value || ''}' in within constraint for source '${src.name}'.`, + rule: 'Within constraints must be duration literals (e.g. 10m, 5h, 1d).', + fix: `Use a duration literal like '10m' in the within constraint for '${src.name}'.`, + location: findLocation(source, 'within'), + context: formatContext(source, findLocation(source, src.name)) + })); + } + + const paramNames = new Set(); + for (const param of src.params || []) { + if (paramNames.has(param.name)) { + errors.push(createError({ + message: `Duplicate parameter '${param.name}' in source '${src.name}'.`, + rule: 'Each parameter name must be unique within a source signature.', + fix: 'Rename the parameter to a unique name.', + location: findLocation(source, param.name), + context: formatContext(source, findLocation(source, src.name)) + })); + } + paramNames.add(param.name); + + if (!isTypeKnown(param.paramType, tables)) { + errors.push(createError({ + message: `Unknown parameter type '${param.paramType}' in source '${src.name}'.`, + rule: 'Parameter types must be built-in or declared as a definition.', + fix: `Add a type definition for '${param.paramType}' or correct the parameter type.`, + location: findLocation(source, param.paramType), + context: formatContext(source, findLocation(source, src.name)) + })); + } + } + } +} + +function isValidDuration(within) { + if (!within || typeof within.value !== 'string') return false; + return /^\d+(h|d|w|m)$/.test(within.value); +} + +function validateMeasures(program, tables, errors, warnings, source) { + for (const measure of program.measures || []) { + if (tables.builtins?.measures?.has(measure.name)) { + errors.push(createError({ + message: `Measure '${measure.name}' is a built-in and cannot be redefined.`, + rule: 'Built-in measures are reserved and cannot be overridden.', + fix: `Rename your measure or remove the duplicate definition for '${measure.name}'.`, + location: findLocation(source, `measure ${measure.name}`), + context: formatContext(source, findLocation(source, measure.name)) + })); + } + const returnType = measure.provides || DEFAULT_MEASURE_RETURN; + if (returnType !== DEFAULT_MEASURE_RETURN && !isTypeKnown(returnType, tables)) { + errors.push(createError({ + message: `Unknown return type '${returnType}' for measure '${measure.name}'.`, + rule: 'Measure return types must be built-in or declared as a definition.', + fix: `Add a type definition for '${returnType}' or correct the PROVIDES clause.`, + location: findLocation(source, returnType), + context: formatContext(source, findLocation(source, measure.name)) + })); + } + + const paramScope = buildParamScope(measure.params, tables, errors, source, `measure ${measure.name}`); + if (measure.body) { + validateMeasureBody(measure.body, paramScope, tables, errors, warnings, source, measure); + } + } +} + +function validateEvidence(program, tables, errors, warnings, source) { + for (const ev of program.evidence || []) { + if (tables.builtins?.evidence?.has(ev.name)) { + errors.push(createError({ + message: `Evidence '${ev.name}' is a built-in and cannot be redefined.`, + rule: 'Built-in evidence names are reserved and cannot be overridden.', + fix: `Rename your evidence or remove the duplicate definition for '${ev.name}'.`, + location: findLocation(source, `evidence ${ev.name}`), + context: formatContext(source, findLocation(source, ev.name)) + })); + } + const returnType = ev.provides || DEFAULT_EVIDENCE_RETURN; + if (returnType !== DEFAULT_EVIDENCE_RETURN && !isTypeKnown(returnType, tables)) { + errors.push(createError({ + message: `Unknown return type '${returnType}' for evidence '${ev.name}'.`, + rule: 'Evidence return types must be built-in or declared as a definition.', + fix: `Add a type definition for '${returnType}' or correct the PROVIDES clause.`, + location: findLocation(source, returnType), + context: formatContext(source, findLocation(source, ev.name)) + })); + } + + const paramScope = buildParamScope(ev.params, tables, errors, source, `evidence ${ev.name}`); + if (ev.body) { + validateEvidenceBody(ev.body, paramScope, tables, errors, warnings, source, ev); + } + } +} + +function validateEvidenceBody(body, scope, tables, errors, warnings, source, parent) { + for (const stmt of body.statements || []) { + switch (stmt.type) { + case 'DefeasibleLogic': + validateBooleanExpression(stmt.condition, scope, tables, errors, warnings, source, parent); + if (stmt.defeater) { + validateBooleanExpression(stmt.defeater, scope, tables, errors, warnings, source, parent); + } + break; + case 'Fusion': + for (const expr of stmt.expressions || []) { + validateBooleanExpression(expr, scope, tables, errors, warnings, source, parent); + } + break; + case 'CollectionProcessing': + validateCollectionProcessing(stmt, scope, tables, errors, warnings, source, parent); + break; + case 'PatternMatch': + validatePatternMatch(stmt, scope, tables, errors, warnings, source, parent); + break; + default: + validateBooleanExpression(stmt, scope, tables, errors, warnings, source, parent); + break; + } + } +} + +function validateMeasureBody(body, scope, tables, errors, warnings, source, measure) { + for (const stmt of body.statements || []) { + if (stmt.type === 'Fusion') { + for (const expr of stmt.expressions || []) { + inferExpressionType(expr, scope, tables, errors, warnings, source, measure); + } + } else if (stmt.type === 'Aggregation') { + for (const expr of stmt.expressions || []) { + inferExpressionType(expr, scope, tables, errors, warnings, source, measure); + } + } else { + inferExpressionType(stmt, scope, tables, errors, warnings, source, measure); + } + } + + if (body.returnStatement && body.returnStatement.expression) { + const returnType = measure.provides || DEFAULT_MEASURE_RETURN; + const inferred = inferExpressionType(body.returnStatement.expression, scope, tables, errors, warnings, source, measure); + if (returnType !== DEFAULT_MEASURE_RETURN && inferred && inferred.type) { + if (!isAssignable(inferred.type, returnType)) { + errors.push(createError({ + message: `Return type mismatch in measure '${measure.name}': expected '${returnType}', got '${inferred.type}'.`, + rule: 'Measure return expressions must match the PROVIDES type.', + fix: 'Update the return expression or adjust the PROVIDES type.', + location: findLocation(source, measure.name), + context: formatContext(source, findLocation(source, measure.name)) + })); + } + } + } +} + +function validateCollectionProcessing(stmt, scope, tables, errors, warnings, source, parent) { + const measureType = inferExpressionType(stmt.measure, scope, tables, errors, warnings, source, parent); + if (measureType && measureType.type && !measureType.isArray) { + errors.push(createError({ + message: 'Collection processing requires a measure that returns an array.', + rule: 'Use |var| only with measures that return array types.', + fix: 'Change the measure to return an array (Type[]) or use a direct predicate instead.', + location: findLocation(source, stmt.variable), + context: formatContext(source, findLocation(source, stmt.variable)) + })); + } + + const nextScope = new Map(scope); + if (measureType && measureType.elementType) { + nextScope.set(stmt.variable, { type: measureType.elementType, isArray: false }); + } else { + nextScope.set(stmt.variable, { type: 'any', isArray: false }); + } + validateEvidenceBody(stmt.body, nextScope, tables, errors, warnings, source, parent); +} + +function validatePatternMatch(stmt, scope, tables, errors, warnings, source, parent) { + const predicate = stmt.predicate; + if (!predicate || !predicate.name) return; + + const signature = resolvePredicateSignature(predicate.name, tables); + if (!signature) { + errors.push(createError({ + message: `Unknown predicate '${predicate.name}'.`, + rule: 'Predicates must reference defined facts, evidence, or measures.', + fix: `Define '${predicate.name}' as a fact/evidence/measure or correct the name.`, + location: findLocation(source, predicate.name), + context: formatContext(source, findLocation(source, predicate.name)) + })); + return; + } + + const nextScope = new Map(scope); + for (let i = 0; i < (predicate.args || []).length; i++) { + const arg = predicate.args[i]; + const expected = signature.params[i]; + if (!expected) continue; + if (arg && arg.type === 'Wildcard') { + nextScope.set(arg.name, { type: expected.type, isArray: expected.isArray }); + continue; + } + const inferred = inferExpressionType(arg, scope, tables, errors, warnings, source, parent); + if (inferred && inferred.type && expected.type) { + if (!isAssignable(inferred.type, expected.type, inferred.isArray, expected.isArray)) { + errors.push(createError({ + message: `Type mismatch for '${predicate.name}' argument ${i + 1}: expected '${formatType(expected)}', got '${formatType(inferred)}'.`, + rule: 'Predicate arguments must match declared parameter types.', + fix: 'Update the argument or adjust the predicate signature.', + location: findLocation(source, predicate.name), + context: formatContext(source, findLocation(source, predicate.name)) + })); + } + } + } + + if (stmt.binding) { + nextScope.set(stmt.binding, { type: 'relation', isArray: false, relation: predicate.name }); + } + + validateEvidenceBody(stmt.body, nextScope, tables, errors, warnings, source, parent); + + if (stmt.withClause) { + validateBooleanExpression(stmt.withClause.condition, nextScope, tables, errors, warnings, source, parent); + } +} + +function validateBooleanExpression(expr, scope, tables, errors, warnings, source, parent) { + const inferred = inferExpressionType(expr, scope, tables, errors, warnings, source, parent); + if (!inferred || !inferred.type) return; + // Challenge / injectable-source predicate calls are proof requirements, + // not value expressions — they are valid evidence-body statements. + if (inferred.type !== 'boolean' && inferred.type !== 'challenge_proof') { + errors.push(createError({ + message: `Expected a boolean expression, got '${inferred.type}'.`, + rule: 'Evidence statements must evaluate to boolean or possibilistic truth values.', + fix: 'Use a predicate, comparison, or logical expression that returns boolean.', + location: expr?.location || findLocation(source, inferred.name || ''), + context: formatContext(source, expr?.location || findLocation(source, inferred.name || '')) + })); + } +} + +function inferExpressionType(expr, scope, tables, errors, warnings, source, parent) { + if (!expr) return null; + + switch (expr.type) { + case 'Literal': + return inferLiteralType(expr); + case 'Variable': + return resolveVariableType(expr, scope, errors, source); + case 'PredicateCall': + return resolvePredicateCallType(expr, scope, tables, errors, warnings, source, parent); + case 'AttributeAccess': + return resolveAttributeType(expr, scope, tables, errors, warnings, source, parent); + case 'BindingAccess': + return inferExpressionType(expr.expression, scope, tables, errors, warnings, source, parent); + case 'UnaryExpression': + return { type: 'boolean', isArray: false }; + case 'BinaryExpression': + return resolveBinaryExpressionType(expr, scope, tables, errors, warnings, source, parent); + default: + return { type: 'any', isArray: false }; + } +} + +function resolveBinaryExpressionType(expr, scope, tables, errors, warnings, source, parent) { + const left = inferExpressionType(expr.left, scope, tables, errors, warnings, source, parent); + let scopedForRight = scope; + const guard = extractTypeGuard(expr.left, scope, tables, errors, warnings, source, parent); + if (expr.operator === '&&' && guard) { + scopedForRight = new Map(scope); + scopedForRight.set(guard.targetPath, { type: guard.typeName, isArray: false }); + } + const right = inferExpressionType(expr.right, scopedForRight, tables, errors, warnings, source, parent); + const operator = expr.operator; + + if (operator === 'is') { + const typeName = resolveTypeName(expr.right); + if (!typeName || !isTypeKnown(typeName, tables)) { + errors.push(createError({ + message: 'Type guard requires a known type name on the right-hand side.', + rule: 'Use "value is TypeName" with a declared type.', + fix: 'Ensure the right-hand side is a valid type name.', + location: expr.location || findLocation(source, 'is'), + context: formatContext(source, expr.location || findLocation(source, 'is')) + })); + } + return { type: 'boolean', isArray: false }; + } + + if (['+', '-', '*', '/'].includes(operator)) { + if (!isNumberType(left) || !isNumberType(right)) { + errors.push(createError({ + message: `Arithmetic operator '${operator}' requires numeric operands.`, + rule: 'Arithmetic expressions must use numbers.', + fix: 'Cast or convert operands to numbers before applying arithmetic operators.', + location: expr.location || findLocation(source, operator), + context: formatContext(source, expr.location || findLocation(source, operator)) + })); + } + return { type: 'number', isArray: false }; + } + + if (['==', '!=', '>=', '<=', '>', '<', 'within'].includes(operator)) { + if (operator === 'within') { + if (!isTimestampType(left) || !isDurationType(right)) { + errors.push(createError({ + message: 'Temporal comparisons require a timestamp on the left and duration on the right.', + rule: 'Use "within" with timestamp values and duration literals.', + fix: 'Ensure the left expression is a timestamp and the right is a duration (e.g., 1h).', + location: expr.location || findLocation(source, 'within'), + context: formatContext(source, expr.location || findLocation(source, 'within')) + })); + } + } else if (!areComparableTypes(left, right)) { + errors.push(createError({ + message: `Comparison '${operator}' uses incompatible types '${formatType(left)}' and '${formatType(right)}'.`, + rule: 'Comparisons require compatible operand types.', + fix: 'Align the types on both sides of the comparison.', + location: expr.location || findLocation(source, operator), + context: formatContext(source, expr.location || findLocation(source, operator)) + })); + } + return { type: 'boolean', isArray: false }; + } + + if (['&&', '||'].includes(operator)) { + return { type: 'boolean', isArray: false }; + } + + return { type: 'any', isArray: false }; +} + +function resolvePredicateCallType(expr, scope, tables, errors, warnings, source, parent) { + if (expr.name === 'is_type') { + const guard = extractTypeGuard(expr, scope, tables, errors, warnings, source, parent); + if (!guard) { + errors.push(createError({ + message: 'is_type requires a value expression and a known type name.', + rule: 'Type guards must be written as is_type(value, TypeName).', + fix: 'Pass a value expression and a declared type name.', + location: findLocation(source, expr.name), + context: formatContext(source, findLocation(source, expr.name)) + })); + } + return { type: 'boolean', isArray: false }; + } + + if (expr.challenge) { + if (!Array.isArray(expr.args) || expr.args.length === 0) { + errors.push(createError({ + message: `Challenge predicate '${expr.name}' must specify a subject (e.g., !${expr.name}(user)).`, + rule: 'Challenge predicates require an explicit subject bound to the current session or user.', + fix: `Add an argument: !${expr.name}(user) or !${expr.name}(session).`, + location: findLocation(source, expr.name), + context: formatContext(source, findLocation(source, expr.name)) + })); + return { type: 'any', isArray: false }; + } + if (BUILTIN_CHALLENGES.has(expr.name)) { + return { type: 'challenge_proof', isArray: false, challenge: true }; + } + const ev = tables.evidence.get(expr.name); + if (ev && ev.challenge) { + return { type: 'challenge_proof', isArray: false, challenge: true }; + } + if (ev && !ev.challenge) { + errors.push(createError({ + message: `Evidence '${expr.name}' is not declared as a challenge evidence.`, + rule: 'Only challenge evidence can be referenced with a ! prefix.', + fix: `Declare '${expr.name}' as 'evidence !${expr.name}(...)' or remove the ! prefix.`, + location: findLocation(source, expr.name), + context: formatContext(source, findLocation(source, expr.name)) + })); + return { type: 'any', isArray: false }; + } + // Injectable sources and injectable facts are valid challenge-style + // references (witness requirements), even though they are not evidence. + const src = tables.sources.get(expr.name); + if (src) { + return { type: 'challenge_proof', isArray: false, challenge: true }; + } + const injectableFact = tables.facts.get(expr.name); + if (injectableFact && injectableFact.injectable) { + return { type: 'challenge_proof', isArray: false, challenge: true }; + } + errors.push(createError({ + message: `Unknown challenge predicate '${expr.name}'.`, + rule: 'Challenge predicates must be predefined or declared as challenge evidence.', + fix: `Define 'evidence !${expr.name}(...)' or use a predefined challenge predicate.`, + location: findLocation(source, expr.name), + context: formatContext(source, findLocation(source, expr.name)) + })); + return { type: 'any', isArray: false }; + } + + const signature = resolvePredicateSignature(expr.name, tables); + if (!signature) { + errors.push(createError({ + message: `Unknown predicate '${expr.name}'.`, + rule: 'Predicates must reference defined facts, evidence, or measures.', + fix: `Define '${expr.name}' as a fact/evidence/measure or correct the name.`, + location: findLocation(source, expr.name), + context: formatContext(source, findLocation(source, expr.name)) + })); + return { type: 'any', isArray: false }; + } + + const expectedCount = signature.params.length; + const actualCount = expr.args ? expr.args.length : 0; + if (expectedCount !== actualCount) { + errors.push(createError({ + message: `Predicate '${expr.name}' expects ${expectedCount} arguments, got ${actualCount}.`, + rule: 'Predicate calls must match the declared parameter count.', + fix: 'Add or remove arguments to match the predicate signature.', + location: findLocation(source, expr.name), + context: formatContext(source, findLocation(source, expr.name)) + })); + } + + for (let i = 0; i < Math.min(expectedCount, actualCount); i++) { + const expected = signature.params[i]; + const arg = expr.args[i]; + if (arg && arg.type === 'Wildcard') { + continue; + } + const inferred = inferExpressionType(arg, scope, tables, errors, warnings, source, parent); + if (inferred && expected) { + if (!isAssignable(inferred.type, expected.type, inferred.isArray, expected.isArray)) { + errors.push(createError({ + message: `Type mismatch for '${expr.name}' argument ${i + 1}: expected '${formatType(expected)}', got '${formatType(inferred)}'.`, + rule: 'Predicate arguments must match declared parameter types.', + fix: 'Update the argument or adjust the predicate signature.', + location: findLocation(source, expr.name), + context: formatContext(source, findLocation(source, expr.name)) + })); + } + } + } + + return signature.returnType; +} + +function resolveAttributeType(expr, scope, tables, errors, warnings, source, parent) { + const override = getScopeOverride(expr.object, scope); + const objectType = override || inferExpressionType(expr.object, scope, tables, errors, warnings, source, parent); + if (!objectType || !objectType.type) { + return { type: 'any', isArray: false }; + } + + if (objectType.challenge) { + const attr = expr.attribute; + if (attr === 'issued_at' || attr === 'issuedAt' || attr === 'expires_at' || attr === 'expiresAt') { + return { type: 'timestamp', isArray: false }; + } + errors.push(createError({ + message: `Unknown challenge proof field '${attr}'.`, + rule: 'Challenge proofs only expose issued_at and expires_at fields.', + fix: `Use ${objectType.challenge ? 'issued_at' : 'expires_at'} on the challenge proof.`, + location: expr.location || findLocation(source, attr), + context: formatContext(source, expr.location || findLocation(source, attr)) + })); + return { type: 'any', isArray: false }; + } + + if (objectType.type === 'relation') { + warnings.push(createError({ + message: `Attribute access '${expr.attribute}' cannot be validated on relation bindings yet.`, + rule: 'Relation attribute schemas are not declared in the current DSL grammar.', + fix: 'Add a relation attribute schema once supported, or validate this in application code.', + location: expr.location || findLocation(source, expr.attribute), + context: formatContext(source, expr.location || findLocation(source, expr.attribute)) + })); + return { type: 'any', isArray: false }; + } + + const def = tables.definitions.get(objectType.type); + if (!def) { + errors.push(createError({ + message: `Cannot access attribute '${expr.attribute}' on unknown type '${objectType.type}'.`, + rule: 'Attribute access requires a typed node definition.', + fix: `Define type '${objectType.type}' with the desired fields.`, + location: expr.location || findLocation(source, expr.attribute), + context: formatContext(source, expr.location || findLocation(source, expr.attribute)) + })); + return { type: 'any', isArray: false }; + } + + const field = (def.fields || []).find(f => f.name === expr.attribute); + if (!field) { + errors.push(createError({ + message: `Unknown field '${expr.attribute}' on type '${def.name}'.`, + rule: 'Attribute access must reference a declared field.', + fix: `Add field '${expr.attribute}' to type '${def.name}' or correct the attribute name.`, + location: expr.location || findLocation(source, expr.attribute), + context: formatContext(source, expr.location || findLocation(source, expr.attribute)) + })); + return { type: 'any', isArray: false }; + } + + return { + type: field.fieldType, + isArray: !!field.isArray + }; +} + +function extractTypeGuard(expr, scope, tables, errors, warnings, source, parent) { + if (expr && expr.type === 'BinaryExpression' && expr.operator === 'is') { + const targetPath = getExpressionPath(expr.left); + const typeName = resolveTypeName(expr.right); + if (targetPath && typeName && isTypeKnown(typeName, tables)) { + return { targetPath, typeName }; + } + return null; + } + const guardExpr = expr && expr.type === 'PredicateCall' ? expr : null; + if (!guardExpr || guardExpr.name !== 'is_type') return null; + const args = guardExpr.args || []; + if (args.length !== 2) return null; + const target = args[0]; + const typeArg = args[1]; + const targetPath = getExpressionPath(target); + if (!targetPath) return null; + const typeName = resolveTypeName(typeArg); + if (!typeName || !isTypeKnown(typeName, tables)) return null; + return { targetPath, typeName }; +} + +function resolveTypeName(expr) { + if (!expr) return null; + if (expr.type === 'TypeName') return expr.name; + if (expr.type === 'Variable') return expr.name; + if (expr.type === 'Literal' && typeof expr.value === 'string') return expr.value; + return null; +} + +function getExpressionPath(expr) { + if (!expr) return null; + if (expr.type === 'Variable') return expr.name; + if (expr.type === 'AttributeAccess') { + const base = getExpressionPath(expr.object); + if (!base || !expr.attribute) return null; + return `${base}.${expr.attribute}`; + } + return null; +} + +function getScopeOverride(expr, scope) { + const path = getExpressionPath(expr); + if (!path) return null; + const entry = scope.get(path); + if (!entry) return null; + return { type: entry.type, isArray: entry.isArray || false }; +} + +function resolveVariableType(expr, scope, errors, source) { + const entry = scope.get(expr.name); + if (!entry) { + if (isChallengePredicateName(expr.name)) { + errors.push(createError({ + message: `Challenge predicate '${expr.name}' must be prefixed with '!'.`, + rule: 'Challenge predicates are only valid as ! in Evidence DSL expressions.', + fix: `Rewrite '${expr.name}' as '!${expr.name}'.`, + location: findLocation(source, expr.name), + context: formatContext(source, findLocation(source, expr.name)) + })); + return { type: 'any', isArray: false, name: expr.name }; + } + errors.push(createError({ + message: `Unknown variable '${expr.name}'.`, + rule: 'Variables must be declared in the current scope or bound by a pattern.', + fix: 'Add the variable to the parameter list or bind it with a wildcard/pattern.', + location: findLocation(source, expr.name), + context: formatContext(source, findLocation(source, expr.name)) + })); + return { type: 'any', isArray: false, name: expr.name }; + } + return { type: entry.type, isArray: entry.isArray || false, name: expr.name }; +} + +function isChallengePredicateName(name) { + return BUILTIN_CHALLENGES.has(name); +} + +function buildParamScope(params, tables, errors, source, contextLabel) { + const scope = new Map(); + for (const param of params || []) { + if (!isTypeKnown(param.paramType, tables)) { + errors.push(createError({ + message: `Unknown parameter type '${param.paramType}' in ${contextLabel}.`, + rule: 'Parameter types must be built-in or declared as a definition.', + fix: `Add a type definition for '${param.paramType}' or correct the parameter type.`, + location: findLocation(source, param.paramType), + context: formatContext(source, findLocation(source, contextLabel)) + })); + } + scope.set(param.name, { type: param.paramType, isArray: !!param.isArray }); + } + return scope; +} + +function resolvePredicateSignature(name, tables) { + if (tables.facts.has(name)) { + const fact = tables.facts.get(name); + return { + params: (fact.params || []).map(param => ({ + type: param.paramType, + isArray: !!param.isArray + })), + returnType: { type: 'boolean', isArray: false } + }; + } + if (tables.evidence.has(name)) { + const ev = tables.evidence.get(name); + return { + params: (ev.params || []).map(param => ({ + type: param.paramType, + isArray: !!param.isArray + })), + returnType: { type: ev.provides || DEFAULT_EVIDENCE_RETURN, isArray: false } + }; + } + if (tables.measures.has(name)) { + const measure = tables.measures.get(name); + const returnType = measure.provides || DEFAULT_MEASURE_RETURN; + return { + params: (measure.params || []).map(param => ({ + type: param.paramType, + isArray: !!param.isArray + })), + returnType: { type: returnType, isArray: false } + }; + } + if (tables.sources.has(name)) { + const source = tables.sources.get(name); + return { + params: (source.params || []).map(param => ({ + type: param.paramType, + isArray: !!param.isArray + })), + returnType: { type: source.provides || 'boolean', isArray: false }, + injectable: !!source.injectable, + within: source.within || null + }; + } + return null; +} + +function inferLiteralType(expr) { + if (typeof expr.value === 'boolean') return { type: 'boolean', isArray: false }; + if (typeof expr.value === 'number') return { type: 'number', isArray: false }; + if (expr.unit) return { type: 'duration', isArray: false }; + if (typeof expr.value === 'string') return { type: 'string', isArray: false }; + return { type: 'any', isArray: false }; +} + +function isTypeKnown(type, tables) { + if (!type) return false; + if (BUILTIN_TYPES.has(type)) return true; + return tables.definitions.has(type); +} + +function isAssignable(actualType, expectedType, actualIsArray = false, expectedIsArray = false) { + if (!expectedType || expectedType === 'any') return true; + if (actualType === 'any') return true; + if (actualIsArray !== expectedIsArray) return false; + return actualType === expectedType; +} + +function areComparableTypes(left, right) { + if (!left || !right) return true; + if (left.type === 'any' || right.type === 'any') return true; + if (left.isArray || right.isArray) return false; + return left.type === right.type; +} + +function isNumberType(entry) { + if (!entry) return true; + return entry.type === 'number' || entry.type === 'any'; +} + +function isTimestampType(entry) { + if (!entry) return true; + return entry.type === 'timestamp' || entry.type === 'any'; +} + +function isDurationType(entry) { + if (!entry) return true; + return entry.type === 'duration' || entry.type === 'any'; +} + +function formatType(entry) { + if (!entry) return 'unknown'; + const base = entry.type || 'unknown'; + return entry.isArray ? `${base}[]` : base; +} + +function createError({ message, rule, fix, location, context, severity = 'error' }) { + return { message, rule, fix, location, context, severity }; +} + +function formatValidationError(error) { + const parts = []; + if (error.location && error.location.start) { + parts.push(`[${error.location.start.line}:${error.location.start.column}]`); + } + parts.push(error.message); + if (error.rule) parts.push(`Rule: ${error.rule}`); + if (error.fix) parts.push(`Fix: ${error.fix}`); + if (error.context) parts.push(`Context: ${error.context}`); + return parts.join(' '); +} + +function formatPegError(error, sourceText, sourceName) { + const expected = (error.expected || []).map(exp => exp.description || exp.text || exp.type || String(exp)); + const found = error.found === null ? 'end of input' : String(error.found); + const location = error.location || null; + const expectedStr = expected.length > 3 ? `${expected.slice(0, 3).join(', ')}, ...` : expected.join(' or '); + + let message = `Syntax error: unexpected ${found}`; + let fix = expectedStr ? `Expected ${expectedStr}.` : 'Check the DSL syntax.'; + let rule = 'Statements must follow the Evidence DSL grammar.'; + + if (expected.includes('"}"') || expected.includes('}')) { + message = 'Missing closing brace.'; + fix = 'Add a closing "}" to end the block.'; + rule = 'Blocks must be closed with "}".'; + } else if (expected.includes('")"') || expected.includes(')')) { + message = 'Missing closing parenthesis.'; + fix = 'Add a closing ")" to end the parameter list.'; + rule = 'Parameter lists must be closed with ")".'; + } else if (expected.includes('":"') || expected.includes(':')) { + message = 'Missing colon after identifier.'; + fix = 'Add ":" between a name and its type.'; + rule = 'Types must be declared using name: Type syntax.'; + } + + return createError({ + message, + rule, + fix, + location, + context: formatContext(sourceText, location), + severity: 'error', + expected: expectedStr, + found, + source: sourceName + }); +} + +function parsePrelude(options = {}) { + if (options.includePrelude === false) return null; + try { + return parse(DSL_PRELUDE); + } catch (error) { + return null; + } +} + +function formatContext(sourceText, location) { + if (!sourceText) return null; + if (!location || !location.start) return null; + const lines = sourceText.split('\n'); + const line = lines[location.start.line - 1]; + if (!line) return null; + const pointer = ' '.repeat(Math.max(0, location.start.column - 1)) + '^'; + return `${line.trimEnd()}\n${pointer}`; +} + +function findLocation(sourceText, needle) { + if (!sourceText || !needle) return null; + const lines = sourceText.split('\n'); + for (let i = 0; i < lines.length; i++) { + const col = lines[i].indexOf(needle); + if (col !== -1) { + return { + start: { line: i + 1, column: col + 1 }, + end: { line: i + 1, column: col + needle.length } + }; + } + } + return null; +} diff --git a/tests/DSLCompiler.test.js b/tests/DSLCompiler.test.js new file mode 100644 index 0000000..b58cabb --- /dev/null +++ b/tests/DSLCompiler.test.js @@ -0,0 +1,265 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { DSLCompiler } from '../src/DSLCompiler.js'; +import { parse } from '../src/parser/GeneratedParser.js'; +import { RuleGenerator } from '../src/generator/RuleGenerator.js'; + +function createMockArbiter() { + const relationConfigs = new Map(); + return { + relationConfigs, + setRelationConfig(relation, config) { + relationConfigs.set(relation, config); + } + }; +} + +describe('DSL Compiler', () => { + const arbiter = createMockArbiter(); + const compiler = new DSLCompiler(arbiter); + + test('Basic parsing', () => { + const dsl = ` + definition Employee { + role: string + isActive: boolean + } + + fact hasRole(user: Employee, role: string) + + evidence canRead(user: Employee, doc: Account) { + hasRole(user, 'admin') + } + `; + + const result = compiler.compile(dsl, 'test-basic'); + assert.ok(result.success, 'Basic parsing should succeed'); + assert.ok(result.program !== null, 'Program should be created'); + assert.ok(result.generatedRules.size > 0, 'Rules should be generated'); + }); + + test('Basic compilation', () => { + const dsl = ` + definition Employee { + role: string + isActive: boolean + } + + fact hasRole(user: Employee, role: string) CACHE eager + + evidence canRead(user: Employee, doc: Account) { + hasRole(user, 'admin') + } + `; + + const result = compiler.compile(dsl, 'test-compilation'); + assert.ok(result.success, 'Basic compilation should succeed'); + assert.ok(result.generatedRules.has('canRead'), 'canRead rule should be generated'); + + const canReadConfig = result.generatedRules.get('canRead'); + assert.ok(canReadConfig.type === 'direct', 'canRead should be direct rule'); + assert.ok(canReadConfig.relation === 'hasRole', 'canRead should use hasRole relation'); + }); + + test('Complex DSL compilation', () => { + const dsl = ` + definition Employee { + role: string + isActive: boolean + clearance: string BEHAVES { + blurring adaptive confidence_95 + } CACHE eager + } + + fact hasRole(user: Employee, role: string) CACHE eager + fact isMember(user: Employee, group: Device) transitive CACHE lazy + fact owns(user: Employee, doc: Account) CACHE eager + + evidence canRead(user: Employee, doc: Account) { + owns(user, doc) + hasRole(user, 'admin') + } + + evidence canAccessCritical(user: Employee, resource: AuthSession) { + fusion min { + hasRole(user, 'admin'), + hasRole(user, 'superadmin') + } + + fusion max { + hasRole(user, 'admin'), + hasRole(user, 'secret') + } + } + `; + + const result = compiler.compile(dsl, 'test-complex'); + assert.ok(result.success, 'Complex DSL compilation should succeed'); + assert.ok(result.generatedRules.has('canRead'), 'canRead rule should be generated'); + assert.ok(result.generatedRules.has('canAccessCritical'), 'canAccessCritical rule should be generated'); + + const canReadConfig = result.generatedRules.get('canRead'); + assert.ok(canReadConfig.type === 'logical', 'canRead should be logical rule'); + }); + + test('Error handling', () => { + const invalidDSL = ` + definition Employee { + role: string + // Missing closing brace + + fact hasRole(user: Employee, role: string) + // Missing semicolon + + evidence canRead(user: Employee, doc: Account) { + // Invalid syntax + invalid syntax here + } + `; + + const result = compiler.compile(invalidDSL, 'test-error'); + assert.ok(!result.success, 'Invalid DSL should fail'); + assert.ok(result.errors.length > 0, 'Should have error messages'); + }); + + test('Program management', () => { + const dsl1 = ` + definition Employee { + role: string + isActive: boolean + } + + fact hasRole(user: Employee, role: string) + evidence canRead(user: Employee, doc: Account) { + hasRole(user, 'admin') + } + `; + + const dsl2 = ` + definition Employee { + role: string + isActive: boolean + } + + fact hasBalance(user: Employee, amount: number) + evidence canWithdraw(user: Employee, amount: number) { + hasBalance(user, amount) + } + `; + + const result1 = compiler.compile(dsl1, 'test-auth'); + assert.ok(result1.success, 'First program should compile'); + + const programs = { 'auth': dsl1, 'finance': dsl2 }; + const result2 = compiler.compileMultiple(programs); + assert.ok(result2.success, 'Multiple programs should compile'); + + const authProgram = compiler.getCompiledProgram('test-auth'); + assert.ok(authProgram !== null, 'Should retrieve compiled program'); + + const removed = compiler.removeCompiledProgram('test-auth'); + assert.ok(removed, 'Should remove program'); + + compiler.clearCompiledPrograms(); + const allPrograms = compiler.getAllCompiledPrograms(); + assert.ok(allPrograms.size === 0, 'Should clear all programs'); + }); + + test('Validation', () => { + const validDSL = ` + definition Employee { + role: string + isActive: boolean + } + + fact hasRole(user: Employee, role: string) + + evidence canRead(user: Employee, doc: Account) { + hasRole(user, 'admin') + } + `; + + const invalidDSL = ` + definition Employee { + role: string + // Missing closing brace + + fact hasRole(user: Employee, role: string) + // Missing semicolon + `; + + const validResult = compiler.validate(validDSL); + assert.ok(validResult.success, 'Valid DSL should pass validation'); + + const invalidResult = compiler.validate(invalidDSL); + assert.ok(!invalidResult.success, 'Invalid DSL should fail validation'); + assert.ok(invalidResult.errors.length > 0, 'Should have validation errors'); + }); + + test('Rule generation', () => { + const dsl = ` + definition Employee { + role: string + isActive: boolean + } + + fact hasRole(user: Employee, role: string) CACHE eager + fact isMember(user: Employee, group: Device) transitive CACHE lazy + + evidence canRead(user: Employee, doc: Account) { + hasRole(user, 'admin') + } + + evidence canAccess(user: Employee, doc: Account) { + hasRole(user, 'reader') + } + `; + + const result = compiler.compile(dsl, 'test-rules'); + assert.ok(result.success, 'Rule generation should succeed'); + + const canReadConfig = result.generatedRules.get('canRead'); + assert.ok(canReadConfig.type === 'direct', 'canRead should be direct rule'); + + const canAccessConfig = result.generatedRules.get('canAccess'); + assert.ok(canAccessConfig.type === 'direct', 'canAccess should be direct rule'); + }); + + test('Multiple programs', () => { + const programs = { + 'auth': ` + definition Employee { + role: string + isActive: boolean + } + + fact hasRole(user: Employee, role: string) + evidence canRead(user: Employee, doc: Account) { + hasRole(user, 'admin') + } + `, + 'finance': ` + definition Employee { + role: string + isActive: boolean + } + + fact hasBalance(user: Employee, amount: number) + evidence canWithdraw(user: Employee, amount: number) { + hasBalance(user, amount) + } + `, + 'invalid': ` + // Invalid syntax + invalid syntax here + ` + }; + + const result = compiler.compileMultiple(programs); + assert.ok(!result.success, 'Should fail due to invalid program'); + assert.ok(result.errors.length > 0, 'Should have errors'); + assert.ok(result.results.auth.success, 'Auth program should succeed'); + assert.ok(result.results.finance.success, 'Finance program should succeed'); + assert.ok(!result.results.invalid.success, 'Invalid program should fail'); + }); +}); diff --git a/tests/DefinitionTests.js b/tests/DefinitionTests.js new file mode 100644 index 0000000..09e987e --- /dev/null +++ b/tests/DefinitionTests.js @@ -0,0 +1,283 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { DSLCompiler } from '../src/DSLCompiler.js'; + +function createMockArbiter() { + const relationConfigs = new Map(); + return { + relationConfigs, + setRelationConfig(relation, config) { + relationConfigs.set(relation, config); + } + }; +} + +describe('Type Definitions', () => { + const arbiter = createMockArbiter(); + const compiler = new DSLCompiler(arbiter); + + test('Basic definitions', () => { + const testCases = [ + { + input: `definition User { role: string }`, + description: 'Simple definition with one field' + }, + { + input: `definition User { + role: string + isActive: boolean + }`, + description: 'Definition with multiple fields' + }, + { + input: `definition Group { + name: string + description: string + created: timestamp + }`, + description: 'Definition with different field types' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-basic-def-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + assert.ok(result.program.definitions.length > 0, 'Should have definitions'); + }); + }); + + test('Field types', () => { + const testCases = [ + { type: 'string', description: 'String field type' }, + { type: 'number', description: 'Number field type' }, + { type: 'boolean', description: 'Boolean field type' }, + { type: 'timestamp', description: 'Timestamp field type' }, + { type: 'User', description: 'Custom type field' }, + { type: 'Permission', description: 'Another custom type field' } + ]; + + testCases.forEach(({ type, description }) => { + const dsl = `definition Test { field: ${type} }`; + const result = compiler.compile(dsl, `test-field-type-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Array types', () => { + const testCases = [ + { type: 'string[]', description: 'String array' }, + { type: 'number[]', description: 'Number array' }, + { type: 'boolean[]', description: 'Boolean array' }, + { type: 'Permission[]', description: 'Custom type array' }, + { type: 'User[]', description: 'User array' } + ]; + + testCases.forEach(({ type, description }) => { + const dsl = `definition Test { items: ${type} }`; + const result = compiler.compile(dsl, `test-array-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Behaviors', () => { + const testCases = [ + { + input: `definition User { + balance: number BEHAVES { decaying down hourly } + }`, + description: 'Decay behavior - down hourly' + }, + { + input: `definition User { + reputation: number BEHAVES { decaying up daily } + }`, + description: 'Decay behavior - up daily' + }, + { + input: `definition User { + score: number BEHAVES { decaying neutral weekly } + }`, + description: 'Decay behavior - neutral weekly' + }, + { + input: `definition User { + stability: number BEHAVES { decaying stable monthly } + }`, + description: 'Decay behavior - stable monthly' + }, + { + input: `definition User { + confidence: number BEHAVES { blurring fixed } + }`, + description: 'Blur behavior - fixed' + }, + { + input: `definition User { + accuracy: number BEHAVES { blurring adaptive } + }`, + description: 'Blur behavior - adaptive' + }, + { + input: `definition User { + precision: number BEHAVES { blurring confidence confidence_90 } + }`, + description: 'Blur behavior - confidence with level' + }, + { + input: `definition User { + session: string BEHAVES { ttl 1h } + }`, + description: 'TTL behavior - hours' + }, + { + input: `definition User { + token: string BEHAVES { ttl 24h } + }`, + description: 'TTL behavior - 24 hours' + }, + { + input: `definition User { + cache: string BEHAVES { ttl 7d } + }`, + description: 'TTL behavior - days' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-behavior-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Caching', () => { + const testCases = [ + { + input: `definition User { + role: string CACHE eager + }`, + description: 'Eager caching' + }, + { + input: `definition User { + score: number CACHE lazy + }`, + description: 'Lazy caching' + }, + { + input: `definition User { + balance: number BEHAVES { decaying down hourly } CACHE eager + }`, + description: 'Behavior with eager caching' + }, + { + input: `definition User { + reputation: number BEHAVES { blurring adaptive } CACHE lazy + }`, + description: 'Behavior with lazy caching' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-cache-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Complex definitions', () => { + const testCases = [ + { + input: `definition User { + role: string + isActive: boolean + lastActive: timestamp BEHAVES { + decaying down hourly + } CACHE lazy + isSuspended: boolean + balance: number BEHAVES { + decaying down hourly + } CACHE eager + score: number BEHAVES { + blurring adaptive confidence_95 + } CACHE lazy + session: string BEHAVES { + ttl 24h + } CACHE eager + }`, + description: 'Complex definition with multiple behaviors and caching' + }, + { + input: `definition Group { + name: string + permissions: Permission[] + members: User[] + created: timestamp BEHAVES { + decaying stable monthly + } CACHE lazy + isPublic: boolean CACHE eager + }`, + description: 'Definition with arrays and mixed behaviors' + }, + { + input: `definition Document { + level: string + owner: User + tags: string[] + content: string BEHAVES { + blurring fixed + } CACHE lazy + accessCount: number BEHAVES { + decaying up daily + } CACHE eager + expiresAt: timestamp BEHAVES { + ttl 30d + } CACHE eager + }`, + description: 'Definition with all behavior types' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-complex-def-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + assert.ok(result.program.definitions.length > 0, 'Should have definitions'); + }); + }); + + test('Definition error handling', () => { + const testCases = [ + { + input: `definition User { role: string`, + description: 'Missing closing brace should fail' + }, + { + input: `definition User { role: }`, + description: 'Missing field type should fail' + }, + { + input: `definition User { : string }`, + description: 'Missing field name should fail' + }, + { + input: `definition User { role: string BEHAVES { }`, + description: 'Incomplete behavior should fail' + }, + { + input: `definition User { role: string CACHE }`, + description: 'Incomplete cache directive should fail' + }, + { + input: `definition User { role: string BEHAVES { invalid } }`, + description: 'Invalid behavior should fail' + } + ]; + + testCases.forEach(({ input, description }) => { + try { + const result = compiler.compile(input, `test-def-error-${Date.now()}`); + assert.ok(!result.success, `${description} should fail to parse`); + } catch { + // Expected to fail + } + }); + }); +}); diff --git a/tests/EvidenceTests.js b/tests/EvidenceTests.js new file mode 100644 index 0000000..a941823 --- /dev/null +++ b/tests/EvidenceTests.js @@ -0,0 +1,374 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { DSLCompiler } from '../src/DSLCompiler.js'; + +function createMockArbiter() { + const relationConfigs = new Map(); + return { + relationConfigs, + setRelationConfig(relation, config) { + relationConfigs.set(relation, config); + } + }; +} + +describe('Evidence Rules', () => { + const arbiter = createMockArbiter(); + const compiler = new DSLCompiler(arbiter); + + test('Basic evidence', () => { + const testCases = [ + { + input: `evidence canRead(user: User, doc: Document) { + hasRole(user, 'admin') + }`, + description: 'Simple evidence with function call' + }, + { + input: `evidence canAccess(user: User, resource: Resource) { + user.isActive + }`, + description: 'Evidence with attribute access' + }, + { + input: `evidence canModify(user: User, doc: Document) { + user.isActive + hasRole(user, 'admin') + }`, + description: 'Evidence with multiple conditions' + }, + { + input: `evidence canDelete(user: User, doc: Document) { + owns(user, doc) + user.isActive + }`, + description: 'Evidence with ownership and status' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-basic-evidence-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + assert.ok(result.program.evidence.length > 0, 'Should have evidence'); + }); + }); + + test('Defeasible logic', () => { + const testCases = [ + { + input: `evidence canAccess(user: User, resource: Resource) { + ALWAYS user.isActive + }`, + description: 'ALWAYS rule - strict requirement' + }, + { + input: `evidence canAccess(user: User, resource: Resource) { + WHEN hasRole(user, 'admin') + }`, + description: 'WHEN rule - defeasible condition' + }, + { + input: `evidence canAccess(user: User, resource: Resource) { + WHEN hasRole(user, 'admin') UNLESS isSuspended(user) + }`, + description: 'WHEN/UNLESS rule - defeasible with defeater' + }, + { + input: `evidence canAccess(user: User, resource: Resource) { + REQUIRES hasClearance(user, resource.level) + }`, + description: 'REQUIRES rule - inverse defeater' + }, + { + input: `evidence canAccessCritical(user: User, resource: Resource) { + ALWAYS user.isActive + + WHEN hasRole(user, 'admin') UNLESS isSuspended(user) + + REQUIRES hasClearance(user, resource.level) + }`, + description: 'Complex defeasible logic with all rule types' + }, + { + input: `evidence canAccessSensitive(user: User, doc: Document) { + ALWAYS user.isActive + + WHEN hasRole(user, 'admin') UNLESS isSuspended(user) + + REQUIRES hasClearance(user, doc.level) + + fusion majority { + user.isTrusted + user.hasRecentActivity + } + }`, + description: 'Defeasible logic with fusion' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-defeasible-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Pattern matching', () => { + const testCases = [ + { + input: `evidence canRead(user: User, doc: Document) { + isMember(user, *group) { + canRead(group, doc) + } + }`, + description: 'Basic pattern matching with wildcard' + }, + { + input: `evidence canRead(user: User, doc: Document) { + isMember(user, *group) { + canRead(group, doc) + } limit 5 + }`, + description: 'Pattern matching with limit' + }, + { + input: `evidence canRead(user: User, doc: Document) { + similar(doc, *similar) |similarity| { + canRead(user, similar) + } with similarity > 0.7 + }`, + description: 'Pattern matching with binding and condition' + }, + { + input: `evidence canRead(user: User, doc: Document) { + similar(doc, *similar) |similarity| { + canRead(user, similar) + } with similarity > 0.7 limit 5 + }`, + description: 'Pattern matching with binding, condition, and limit' + }, + { + input: `evidence canRead(user: User, doc: Document) { + isMember(user, *group) { + isMember(group, *parentGroup) { + canRead(parentGroup, doc) + } limit 2 + } limit 3 + }`, + description: 'Nested pattern matching' + }, + { + input: `evidence canRead(user: User, doc: Document) { + isFriend(user, *friend) { + isMember(friend, *group) { + canRead(group, doc) + } limit 1 + } limit 5 + }`, + description: 'Multi-hop pattern matching' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-pattern-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Fusion', () => { + const testCases = [ + { + input: `evidence canAccess(user: User, resource: Resource) { + fusion min { + hasClearance(user, resource.level) + user.isActive + } + }`, + description: 'Min fusion - all conditions must be true' + }, + { + input: `evidence canAccess(user: User, resource: Resource) { + fusion max { + hasRole(user, 'admin') + hasRole(user, 'superuser') + } + }`, + description: 'Max fusion - any condition can be true' + }, + { + input: `evidence canAccess(user: User, resource: Resource) { + fusion majority { + hasClearance(user, 'secret') + user.isTrusted + user.hasRecentActivity + } + }`, + description: 'Majority fusion - most conditions must be true' + }, + { + input: `evidence canAccessCritical(user: User, resource: Resource) { + fusion min { + hasClearance(user, resource.level) + user.isActive + NOT user.isBlacklisted + } + + fusion max { + hasRole(user, 'admin') + fusion majority { + hasClearance(user, 'secret') + user.isTrusted + user.lastActive within 1hr + } + } + }`, + description: 'Nested fusion with different strategies' + }, + { + input: `evidence canAccess(user: User, resource: Resource) { + fusion average { + user.reputation + user.activityScore + user.verificationLevel + } + }`, + description: 'Average fusion for numeric values' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-fusion-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Complex evidence', () => { + const testCases = [ + { + input: `evidence canRead(user: User, doc: Document) { + owns(user, doc) + + isMember(user, *group) { + canRead(group, doc) + } limit 5 + + parentOf(user, *parent) { + canRead(parent, doc) + } limit 3 + + similar(doc, *similar) |similarity| { + canRead(user, similar) + } with similarity > 0.7 limit 5 + + WHEN hasRole(user, 'admin') UNLESS isSuspended(user) + }`, + description: 'Complex evidence with all features' + }, + { + input: `evidence canAccessCritical(user: User, resource: Resource) { + ALWAYS user.isActive + + WHEN hasRole(user, 'admin') UNLESS isSuspended(user) + + REQUIRES hasClearance(user, resource.level) + + fusion min { + hasClearance(user, resource.level) + user.isActive + NOT user.isBlacklisted + } + + fusion max { + hasRole(user, 'admin') + fusion majority { + hasClearance(user, 'secret') + user.isTrusted + user.lastActive within 1hr + } + } + }`, + description: 'Critical access with all rule types and fusion' + }, + { + input: `evidence canModify(user: User, doc: Document) { + owns(user, doc) + + isMember(user, *group) { + canModify(group, doc) + } limit 3 + + similar(doc, *similar) |similarity| { + canModify(user, similar) + similar.isEditable + } with similarity > 0.8 limit 2 + + fusion majority { + user.isTrusted + user.hasRecentActivity + doc.isPublic + } + }`, + description: 'Modification access with similarity and fusion' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-complex-evidence-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Evidence error handling', () => { + const testCases = [ + { + input: `evidence canRead(user: User, doc: Document) { + hasRole(user, 'admin' + }`, + description: 'Missing closing parenthesis should fail' + }, + { + input: `evidence canRead(user: User, doc: Document) { + WHEN hasRole(user, 'admin') UNLESS + }`, + description: 'Incomplete UNLESS condition should fail' + }, + { + input: `evidence canRead(user: User, doc: Document) { + fusion min { + hasRole(user, 'admin') + }`, + description: 'Incomplete fusion should fail' + }, + { + input: `evidence canRead(user: User, doc: Document) { + isMember(user, *group) { + canRead(group, doc) + } with + }`, + description: 'Incomplete with clause should fail' + }, + { + input: `evidence canRead(user: User, doc: Document) { + isMember(user, *group) { + canRead(group, doc) + } limit + }`, + description: 'Incomplete limit should fail' + }, + { + input: `evidence canRead(user: User, doc: Document) { + invalid syntax here + }`, + description: 'Invalid syntax should fail' + } + ]; + + testCases.forEach(({ input, description }) => { + try { + const result = compiler.compile(input, `test-evidence-error-${Date.now()}`); + assert.ok(!result.success, `${description} should fail to parse`); + } catch { + // Expected to fail + } + }); + }); +}); diff --git a/tests/ExpressionTests.js b/tests/ExpressionTests.js new file mode 100644 index 0000000..bbd9988 --- /dev/null +++ b/tests/ExpressionTests.js @@ -0,0 +1,233 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { DSLCompiler } from '../src/DSLCompiler.js'; + +function createMockArbiter() { + const relationConfigs = new Map(); + return { + relationConfigs, + setRelationConfig(relation, config) { + relationConfigs.set(relation, config); + } + }; +} + +describe('Expression Parsing', () => { + const arbiter = createMockArbiter(); + const compiler = new DSLCompiler(arbiter); + + test('Arithmetic operator precedence', () => { + const testCases = [ + { + input: '1 + 2 * 3', + expected: 'Should evaluate as 1 + (2 * 3) = 7', + description: 'Multiplication before addition' + }, + { + input: '10 - 3 * 2', + expected: 'Should evaluate as 10 - (3 * 2) = 4', + description: 'Multiplication before subtraction' + }, + { + input: '8 / 2 * 4', + expected: 'Should evaluate as (8 / 2) * 4 = 16', + description: 'Left-associative division and multiplication' + }, + { + input: '2 + 3 * 4 - 5', + expected: 'Should evaluate as 2 + (3 * 4) - 5 = 9', + description: 'Mixed arithmetic with correct precedence' + }, + { + input: '(1 + 2) * 3', + expected: 'Should evaluate as (1 + 2) * 3 = 9', + description: 'Parentheses override precedence' + } + ]; + + testCases.forEach(({ input, expected, description }) => { + const dsl = `evidence test() { ${input} }`; + const result = compiler.compile(dsl, `test-arithmetic-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Logical operator precedence', () => { + const testCases = [ + { + input: 'true && false || true', + expected: 'Should evaluate as (true && false) || true = true', + description: 'AND before OR' + }, + { + input: 'false || true && false', + expected: 'Should evaluate as false || (true && false) = false', + description: 'AND before OR (alternative)' + }, + { + input: 'NOT true && false', + expected: 'Should evaluate as (NOT true) && false = false', + description: 'NOT before AND' + }, + { + input: 'true && NOT false', + expected: 'Should evaluate as true && (NOT false) = true', + description: 'NOT before AND (alternative)' + }, + { + input: '(true || false) && true', + expected: 'Should evaluate as (true || false) && true = true', + description: 'Parentheses override logical precedence' + } + ]; + + testCases.forEach(({ input, expected, description }) => { + const dsl = `evidence test() { ${input} }`; + const result = compiler.compile(dsl, `test-logical-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Comparison operators', () => { + const testCases = [ + { input: '1 == 1', description: 'Equality comparison' }, + { input: '1 != 2', description: 'Inequality comparison' }, + { input: '5 > 3', description: 'Greater than' }, + { input: '3 < 5', description: 'Less than' }, + { input: '4 >= 4', description: 'Greater than or equal' }, + { input: '4 <= 4', description: 'Less than or equal' }, + { input: '1 == 1 && 2 > 1', description: 'Comparison with logical operators' }, + { input: '1 + 2 == 3', description: 'Arithmetic in comparison' } + ]; + + testCases.forEach(({ input, description }) => { + const dsl = `evidence test() { ${input} }`; + const result = compiler.compile(dsl, `test-comparison-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Temporal expressions', () => { + const testCases = [ + { input: 'user.lastActive within 1h', description: 'Temporal within expression' }, + { input: 'user.lastLogin within 24h', description: 'Temporal within with hours' }, + { input: 'user.createdAt within 7d', description: 'Temporal within with days' }, + { input: 'user.lastActivity within 1h && user.isActive', description: 'Temporal with logical operators' } + ]; + + testCases.forEach(({ input, description }) => { + const dsl = `evidence test() { ${input} }`; + const result = compiler.compile(dsl, `test-temporal-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Unary operators', () => { + const testCases = [ + { input: 'NOT true', description: 'NOT operator' }, + { input: '!false', description: 'Alternative NOT operator' }, + { input: 'NOT (true && false)', description: 'NOT with parenthesized expression' }, + { input: 'NOT user.isSuspended', description: 'NOT with attribute access' } + ]; + + testCases.forEach(({ input, description }) => { + const dsl = `evidence test() { ${input} }`; + const result = compiler.compile(dsl, `test-unary-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Attribute access', () => { + const testCases = [ + { input: 'user.role', description: 'Simple attribute access' }, + { input: 'user.profile.name', description: 'Nested attribute access' }, + { input: 'user.permissions[0]', description: 'Array access' }, + { input: 'user.role.permissions[0]', description: 'Nested attribute with array access' }, + { input: 'user.isActive && user.role == "admin"', description: 'Attribute access in logical expression' } + ]; + + testCases.forEach(({ input, description }) => { + const dsl = `evidence test() { ${input} }`; + const result = compiler.compile(dsl, `test-attribute-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Function calls', () => { + const testCases = [ + { input: 'hasRole(user, "admin")', description: 'Simple function call' }, + { input: 'isMember(user, group)', description: 'Function call with variables' }, + { input: 'hasPermission(user, resource, "read")', description: 'Function call with multiple arguments' }, + { input: 'hasRole(user, "admin") && isActive(user)', description: 'Multiple function calls' }, + { input: 'hasRole(user, user.role)', description: 'Function call with attribute access' } + ]; + + testCases.forEach(({ input, description }) => { + const dsl = `evidence test() { ${input} }`; + const result = compiler.compile(dsl, `test-function-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Complex expressions', () => { + const testCases = [ + { + input: 'user.isActive && (hasRole(user, "admin") || hasPermission(user, resource, "read"))', + description: 'Complex logical expression with function calls' + }, + { + input: 'user.balance > 100 && user.isActive && NOT user.isSuspended', + description: 'Multiple conditions with NOT' + }, + { + input: 'user.lastActive within 1h && (user.role == "admin" || user.hasEmergencyAccess)', + description: 'Temporal with logical conditions' + }, + { + input: 'hasRole(user, "admin") && user.isActive && NOT (user.isSuspended || user.isBlacklisted)', + description: 'Complex negation with multiple conditions' + }, + { + input: 'user.score > 0.8 && user.isTrusted && user.lastActivity within 24h', + description: 'Multiple attribute conditions with temporal' + } + ]; + + testCases.forEach(({ input, description }) => { + const dsl = `evidence test() { ${input} }`; + const result = compiler.compile(dsl, `test-complex-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Expression error handling', () => { + const testCases = [ + { + input: 'user.role ==', + description: 'Incomplete comparison should fail' + }, + { + input: 'user.role &&', + description: 'Incomplete logical expression should fail' + }, + { + input: 'hasRole(user,)', + description: 'Function call with missing argument should fail' + }, + { + input: 'user.role == "admin" &&', + description: 'Incomplete logical expression should fail' + } + ]; + + testCases.forEach(({ input, description }) => { + try { + const dsl = `evidence test() { ${input} }`; + const result = compiler.compile(dsl, `test-error-${Date.now()}`); + assert.ok(!result.success, `${description} should fail to parse`); + } catch { + // Expected to fail + } + }); + }); +}); diff --git a/tests/FactTests.js b/tests/FactTests.js new file mode 100644 index 0000000..200be7e --- /dev/null +++ b/tests/FactTests.js @@ -0,0 +1,232 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { DSLCompiler } from '../src/DSLCompiler.js'; + +function createMockArbiter() { + const relationConfigs = new Map(); + return { + relationConfigs, + setRelationConfig(relation, config) { + relationConfigs.set(relation, config); + } + }; +} + +describe('Fact Declarations', () => { + const arbiter = createMockArbiter(); + const compiler = new DSLCompiler(arbiter); + + test('Basic facts', () => { + const testCases = [ + { + input: `fact hasRole(user: User, role: string)`, + description: 'Simple fact with two parameters' + }, + { + input: `fact isMember(user: User, group: Group)`, + description: 'Fact with custom types' + }, + { + input: `fact owns(user: User, doc: Document)`, + description: 'Fact with multiple custom types' + }, + { + input: `fact isActive(user: User)`, + description: 'Fact with single parameter' + }, + { + input: `fact hasPermission(user: User, resource: Resource, action: string)`, + description: 'Fact with three parameters' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-basic-fact-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + assert.ok(result.program.facts.length > 0, 'Should have facts'); + }); + }); + + test('Fact properties', () => { + const testCases = [ + { + input: `fact isMember(user: User, group: Group) transitive`, + description: 'Transitive fact' + }, + { + input: `fact isFriend(user: User, friend: User) symmetrical`, + description: 'Symmetrical fact' + }, + { + input: `fact isMember(user: User, group: Group) transitive symmetrical`, + description: 'Fact with multiple properties' + }, + { + input: `fact isColleague(user: User, colleague: User) symmetrical`, + description: 'Symmetrical relationship fact' + }, + { + input: `fact isParentOf(parent: User, child: User) transitive`, + description: 'Transitive hierarchical fact' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-fact-property-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Fact caching', () => { + const testCases = [ + { + input: `fact hasRole(user: User, role: string) CACHE eager`, + description: 'Eager cached fact' + }, + { + input: `fact isMember(user: User, group: Group) CACHE lazy`, + description: 'Lazy cached fact' + }, + { + input: `fact isMember(user: User, group: Group) transitive CACHE eager`, + description: 'Transitive fact with eager caching' + }, + { + input: `fact isFriend(user: User, friend: User) symmetrical CACHE lazy`, + description: 'Symmetrical fact with lazy caching' + }, + { + input: `fact hasPermission(user: User, resource: Resource, action: string) CACHE eager`, + description: 'Multi-parameter fact with eager caching' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-fact-cache-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Fact limits', () => { + const testCases = [ + { + input: `fact isMember(user: User, group: Group) limit 10`, + description: 'Fact with simple limit' + }, + { + input: `fact isFriend(user: User, friend: User) limit 100`, + description: 'Fact with higher limit' + }, + { + input: `fact isMember(user: User, group: Group) transitive limit 5`, + description: 'Transitive fact with limit' + }, + { + input: `fact isFriend(user: User, friend: User) symmetrical limit 50`, + description: 'Symmetrical fact with limit' + }, + { + input: `fact isMember(user: User, group: Group) transitive CACHE lazy limit 3`, + description: 'Fact with properties, caching, and limit' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-fact-limit-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Parameter types', () => { + const testCases = [ + { type: 'string', description: 'String parameter' }, + { type: 'number', description: 'Number parameter' }, + { type: 'boolean', description: 'Boolean parameter' }, + { type: 'timestamp', description: 'Timestamp parameter' }, + { type: 'User', description: 'Custom type parameter' }, + { type: 'Group', description: 'Another custom type parameter' }, + { type: 'Permission[]', description: 'Array type parameter' } + ]; + + testCases.forEach(({ type, description }) => { + const dsl = `fact test(param: ${type})`; + const result = compiler.compile(dsl, `test-param-type-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Complex facts', () => { + const testCases = [ + { + input: `fact hasRole(user: User, role: string) CACHE eager + fact isMember(user: User, group: Group) transitive CACHE lazy limit 10 + fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100 + fact owns(user: User, doc: Document) CACHE eager + fact isSuspended(user: User) CACHE lazy`, + description: 'Multiple facts with different configurations' + }, + { + input: `fact hasPermission(user: User, resource: Resource, action: string) CACHE eager + fact isAdmin(user: User) CACHE eager + fact isOwner(user: User, resource: Resource) CACHE eager + fact hasAccess(user: User, resource: Resource, level: string) CACHE lazy`, + description: 'Permission-related facts' + }, + { + input: `fact isMember(user: User, group: Group) transitive CACHE lazy limit 5 + fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 50 + fact isColleague(user: User, colleague: User) symmetrical CACHE lazy limit 20 + fact isParentOf(parent: User, child: User) transitive CACHE eager limit 3`, + description: 'Relationship facts with various properties' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-complex-facts-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + assert.ok(result.program.facts.length > 0, 'Should have facts'); + }); + }); + + test('Fact error handling', () => { + const testCases = [ + { + input: `fact hasRole(user: User, role: string`, + description: 'Missing closing parenthesis should fail' + }, + { + input: `fact hasRole(user: User, )`, + description: 'Missing parameter name should fail' + }, + { + input: `fact hasRole(user: User, role: )`, + description: 'Missing parameter type should fail' + }, + { + input: `fact hasRole(, role: string)`, + description: 'Missing parameter name should fail' + }, + { + input: `fact hasRole(user: User, role: string) CACHE`, + description: 'Incomplete cache directive should fail' + }, + { + input: `fact hasRole(user: User, role: string) limit`, + description: 'Incomplete limit should fail' + }, + { + input: `fact hasRole(user: User, role: string) invalid`, + description: 'Invalid property should fail' + } + ]; + + testCases.forEach(({ input, description }) => { + try { + const result = compiler.compile(input, `test-fact-error-${Date.now()}`); + assert.ok(!result.success, `${description} should fail to parse`); + } catch { + // Expected to fail + } + }); + }); +}); diff --git a/tests/IntegrationTests.js b/tests/IntegrationTests.js new file mode 100644 index 0000000..57e5d93 --- /dev/null +++ b/tests/IntegrationTests.js @@ -0,0 +1,534 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { DSLCompiler } from '../src/DSLCompiler.js'; + +function createMockArbiter() { + const relationConfigs = new Map(); + return { + relationConfigs, + setRelationConfig(relation, config) { + relationConfigs.set(relation, config); + } + }; +} + +describe('Integration Tests', () => { + const arbiter = createMockArbiter(); + const compiler = new DSLCompiler(arbiter); + + test('Complete authorization system', () => { + const completeSystem = ` + // Type definitions with complex behaviors + definition User { + role: string + isActive: boolean + lastActive: timestamp BEHAVES { + decaying down hourly + } CACHE lazy + isSuspended: boolean + balance: number BEHAVES { + decaying down hourly + } CACHE eager + score: number BEHAVES { + blurring adaptive confidence_95 + } CACHE lazy + session: string BEHAVES { + ttl 24h + } CACHE eager + clearance: string BEHAVES { + blurring fixed + } CACHE eager + reputation: number BEHAVES { + decaying up daily + } CACHE lazy + } + + definition Group { + name: string + permissions: Permission[] + level: string + isPublic: boolean CACHE eager + created: timestamp BEHAVES { + decaying stable monthly + } CACHE lazy + } + + definition Document { + level: string + owner: User + tags: string[] + content: string BEHAVES { + blurring fixed + } CACHE lazy + accessCount: number BEHAVES { + decaying up daily + } CACHE eager + expiresAt: timestamp BEHAVES { + ttl 30d + } CACHE eager + isPublic: boolean CACHE eager + } + + definition Resource { + level: string + owner: User + permissions: Permission[] + isPublic: boolean CACHE eager + accessCount: number BEHAVES { + decaying up daily + } CACHE eager + } + + // Facts with various properties and caching + fact hasRole(user: User, role: string) CACHE eager + fact isMember(user: User, group: Group) transitive CACHE lazy limit 10 + fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100 + fact owns(user: User, doc: Document) CACHE eager + fact isSuspended(user: User) CACHE lazy + fact hasPermission(user: User, resource: Resource, action: string) CACHE eager + fact isAdmin(user: User) CACHE eager + fact isOwner(user: User, resource: Resource) CACHE eager + fact hasAccess(user: User, resource: Resource, level: string) CACHE lazy + fact isColleague(user: User, colleague: User) symmetrical CACHE lazy limit 50 + fact isParentOf(parent: User, child: User) transitive CACHE eager limit 3 + + // Evidence rules with complex logic + evidence canRead(user: User, doc: Document) { + owns(user, doc) + + isMember(user, *group) { + canRead(group, doc) + } limit 5 + + parentOf(user, *parent) { + canRead(parent, doc) + } limit 3 + + similar(doc, *similar) |similarity| { + canRead(user, similar) + } with similarity > 0.7 limit 5 + + WHEN hasRole(user, 'admin') UNLESS isSuspended(user) + } + + evidence canWrite(user: User, doc: Document) { + owns(user, doc) + + isMember(user, *group) { + canWrite(group, doc) + } limit 3 + + WHEN hasRole(user, 'admin') UNLESS isSuspended(user) + + REQUIRES user.isActive + } + + evidence canDelete(user: User, doc: Document) { + owns(user, doc) + + ALWAYS user.isActive + + WHEN hasRole(user, 'admin') UNLESS isSuspended(user) + + REQUIRES user.isActive + } + + evidence canAccessCritical(user: User, resource: Resource) { + fusion min { + hasClearance(user, resource.level) + user.isActive + NOT user.isBlacklisted + } + + fusion max { + hasRole(user, 'admin') + fusion majority { + hasClearance(user, 'secret') + user.isTrusted + user.lastActive within 1hr + } + } + } + + evidence canAccessSensitive(user: User, doc: Document) { + ALWAYS user.isActive + + WHEN hasRole(user, 'admin') UNLESS isSuspended(user) + + REQUIRES hasClearance(user, doc.level) + + fusion majority { + user.isTrusted + user.hasRecentActivity + } + } + + // Measures for computed values + measure userRole(user: User) { + user.role + } PROVIDES string + + measure userPermissions(user: User) { + fusion max { + user.role.permissions + user.group.permissions + } + } PROVIDES Permission[] + + measure effectiveClearance(user: User) { + fusion majority { + user.clearance + user.role.clearance + user.group.clearance + } + } PROVIDES string + + measure userTrustScore(user: User) { + fusion average { + user.reputation + user.activityScore + user.verificationLevel + } + } PROVIDES number + + measure userBalance(user: User) { + user.balance + } PROVIDES number + + measure userScore(user: User) { + user.score + } PROVIDES number + `; + + const result = compiler.compile(completeSystem, 'test-complete-system'); + assert.ok(result.success, 'Complete authorization system should compile successfully'); + assert.ok(result.program.definitions.length >= 4, 'Should have multiple definitions'); + assert.ok(result.program.facts.length >= 10, 'Should have multiple facts'); + assert.ok(result.program.evidence.length >= 5, 'Should have multiple evidence rules'); + assert.ok(result.program.measures.length >= 6, 'Should have multiple measures'); + }); + + test('Multi-domain system', () => { + const multiDomain = ` + // Authentication domain + definition User { + role: string + isActive: boolean + lastActive: timestamp BEHAVES { decaying down hourly } CACHE lazy + session: string BEHAVES { ttl 24h } CACHE eager + } + + fact hasRole(user: User, role: string) CACHE eager + fact isActive(user: User) CACHE eager + + evidence canAuthenticate(user: User) { + user.isActive + user.session within 24h + } + + // Authorization domain + definition Resource { + level: string + owner: User + permissions: Permission[] + } + + fact owns(user: User, resource: Resource) CACHE eager + fact hasPermission(user: User, resource: Resource, action: string) CACHE eager + + evidence canAccess(user: User, resource: Resource) { + owns(user, resource) + hasPermission(user, resource, 'read') + } + + // Finance domain + definition Account { + balance: number BEHAVES { decaying down hourly } CACHE eager + owner: User + isActive: boolean CACHE eager + } + + fact hasAccount(user: User, account: Account) CACHE eager + fact hasBalance(user: User, amount: number) CACHE eager + + evidence canWithdraw(user: User, amount: number) { + hasBalance(user, amount) + user.isActive + } + + // Social domain + definition Group { + name: string + members: User[] + isPublic: boolean CACHE eager + } + + fact isMember(user: User, group: Group) transitive CACHE lazy limit 10 + fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100 + + evidence canAccessGroup(user: User, group: Group) { + isMember(user, group) + group.isPublic + } + `; + + const result = compiler.compile(multiDomain, 'test-multi-domain'); + assert.ok(result.success, 'Multi-domain system should compile successfully'); + assert.ok(result.program.definitions.length >= 4, 'Should have multiple domain definitions'); + assert.ok(result.program.facts.length >= 8, 'Should have multiple domain facts'); + assert.ok(result.program.evidence.length >= 4, 'Should have multiple domain evidence rules'); + }); + + test('Hierarchical access', () => { + const hierarchicalSystem = ` + definition User { + role: string + level: string + isActive: boolean + clearance: string + } + + definition Organization { + name: string + level: string + parent: Organization + } + + fact isMember(user: User, org: Organization) transitive CACHE lazy limit 5 + fact isParentOf(parent: Organization, child: Organization) transitive CACHE eager limit 3 + fact hasRole(user: User, role: string) CACHE eager + fact hasClearance(user: User, level: string) CACHE eager + + evidence canAccessOrg(user: User, org: Organization) { + isMember(user, org) + + isParentOf(org, *parentOrg) { + canAccessOrg(user, parentOrg) + } limit 3 + + WHEN hasRole(user, 'admin') UNLESS user.isSuspended + } + + evidence canAccessResource(user: User, resource: Resource) { + isMember(user, *org) { + canAccessResource(org, resource) + } limit 5 + + parentOf(user, *parent) { + canAccessResource(parent, resource) + } limit 2 + } + `; + + const result = compiler.compile(hierarchicalSystem, 'test-hierarchical'); + assert.ok(result.success, 'Hierarchical access system should compile successfully'); + }); + + test('Similarity-based access', () => { + const similaritySystem = ` + definition User { + profile: string + interests: string[] + isActive: boolean + } + + definition Document { + content: string + tags: string[] + isPublic: boolean + owner: User + } + + fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100 + fact hasInterest(user: User, interest: string) CACHE lazy + fact hasTag(doc: Document, tag: string) CACHE lazy + + evidence canRead(user: User, doc: Document) { + owns(user, doc) + + similar(doc, *similar) |similarity| { + canRead(user, similar) + similar.isPublic + } with similarity > 0.7 limit 10 + + isFriend(user, *friend) { + canRead(friend, doc) + } limit 5 + + fusion majority { + user.interests + doc.tags + } + } + + evidence canRecommend(user: User, doc: Document) { + similar(user, *similarUser) |similarity| { + canRead(similarUser, doc) + } with similarity > 0.8 limit 20 + + fusion average { + user.profile + doc.content + } + } + `; + + const result = compiler.compile(similaritySystem, 'test-similarity'); + assert.ok(result.success, 'Similarity-based access system should compile successfully'); + }); + + test('Temporal access', () => { + const temporalSystem = ` + definition User { + lastActive: timestamp BEHAVES { decaying down hourly } CACHE lazy + session: string BEHAVES { ttl 24h } CACHE eager + isActive: boolean + } + + definition Event { + startTime: timestamp + endTime: timestamp + isPublic: boolean + } + + fact hasAccess(user: User, event: Event) CACHE lazy + fact isParticipant(user: User, event: Event) CACHE eager + + evidence canAccessEvent(user: User, event: Event) { + user.lastActive within 1h + + isParticipant(user, event) + + WHEN event.isPublic UNLESS user.isSuspended + + fusion min { + user.session within 24h + user.isActive + } + } + + evidence canAccessHistorical(user: User, event: Event) { + user.lastActive within 24h + + fusion majority { + user.isActive + user.session within 24h + event.isPublic + } + } + `; + + const result = compiler.compile(temporalSystem, 'test-temporal'); + assert.ok(result.success, 'Temporal access system should compile successfully'); + }); + + test('Complex behaviors', () => { + const behaviorSystem = ` + definition User { + balance: number BEHAVES { decaying down hourly } CACHE eager + score: number BEHAVES { blurring adaptive confidence_95 } CACHE lazy + session: string BEHAVES { ttl 24h } CACHE eager + reputation: number BEHAVES { decaying up daily } CACHE lazy + clearance: string BEHAVES { blurring fixed } CACHE eager + lastActive: timestamp BEHAVES { decaying down hourly } CACHE lazy + } + + definition Document { + content: string BEHAVES { blurring fixed } CACHE lazy + accessCount: number BEHAVES { decaying up daily } CACHE eager + expiresAt: timestamp BEHAVES { ttl 30d } CACHE eager + isPublic: boolean CACHE eager + } + + fact hasBalance(user: User, amount: number) CACHE eager + fact hasScore(user: User, score: number) CACHE lazy + fact hasReputation(user: User, reputation: number) CACHE lazy + + evidence canAccessDocument(user: User, doc: Document) { + user.balance > 0 + + user.score > 0.5 + + user.reputation > 0.3 + + doc.accessCount < 1000 + + fusion majority { + user.isActive + user.lastActive within 1h + doc.isPublic + } + } + + measure userEffectiveScore(user: User) { + fusion average { + user.score + user.reputation + user.balance + } + } PROVIDES number + + measure documentPopularity(doc: Document) { + doc.accessCount + } PROVIDES number + `; + + const result = compiler.compile(behaviorSystem, 'test-behaviors'); + assert.ok(result.success, 'Complex behaviors system should compile successfully'); + }); + + test('Performance scenarios', () => { + const performanceSystem = ` + definition User { + role: string + isActive: boolean + permissions: Permission[] CACHE eager + } + + definition Resource { + level: string + owner: User + permissions: Permission[] CACHE eager + } + + // High-frequency facts with limits + fact isMember(user: User, group: Group) transitive CACHE lazy limit 5 + fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 50 + fact hasPermission(user: User, resource: Resource, action: string) CACHE eager + fact owns(user: User, resource: Resource) CACHE eager + + // Optimized evidence rules + evidence canAccess(user: User, resource: Resource) { + owns(user, resource) + + isMember(user, *group) { + canAccess(group, resource) + } limit 3 + + WHEN hasPermission(user, resource, 'read') + } + + evidence canModify(user: User, resource: Resource) { + owns(user, resource) + + isMember(user, *group) { + canModify(group, resource) + } limit 2 + + WHEN hasPermission(user, resource, 'write') + } + + // Efficient measures + measure userEffectivePermissions(user: User) { + user.permissions + } PROVIDES Permission[] + + measure resourceAccessLevel(resource: Resource) { + resource.level + } PROVIDES string + `; + + const result = compiler.compile(performanceSystem, 'test-performance'); + assert.ok(result.success, 'Performance scenarios should compile successfully'); + }); +}); diff --git a/tests/MeasureTests.js b/tests/MeasureTests.js new file mode 100644 index 0000000..9dfc57f --- /dev/null +++ b/tests/MeasureTests.js @@ -0,0 +1,306 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { DSLCompiler } from '../src/DSLCompiler.js'; + +function createMockArbiter() { + const relationConfigs = new Map(); + return { + relationConfigs, + setRelationConfig(relation, config) { + relationConfigs.set(relation, config); + } + }; +} + +describe('Measure Definitions', () => { + const arbiter = createMockArbiter(); + const compiler = new DSLCompiler(arbiter); + + test('Basic measures', () => { + const testCases = [ + { + input: `measure userRole(user: User) { + user.role + } PROVIDES string`, + description: 'Simple measure with attribute access' + }, + { + input: `measure userBalance(user: User) { + user.balance + } PROVIDES number`, + description: 'Measure accessing numeric attribute' + }, + { + input: `measure isUserActive(user: User) { + user.isActive + } PROVIDES boolean`, + description: 'Measure accessing boolean attribute' + }, + { + input: `measure userPermissions(user: User) { + user.permissions + } PROVIDES Permission[]`, + description: 'Measure accessing array attribute' + }, + { + input: `measure userScore(user: User) { + user.score + } PROVIDES number`, + description: 'Measure with behavior-inherited attribute' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-basic-measure-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + assert.ok(result.program.measures.length > 0, 'Should have measures'); + }); + }); + + test('Measure return types', () => { + const testCases = [ + { type: 'string', description: 'String return type' }, + { type: 'number', description: 'Number return type' }, + { type: 'boolean', description: 'Boolean return type' }, + { type: 'timestamp', description: 'Timestamp return type' }, + { type: 'Permission[]', description: 'Array return type' }, + { type: 'User', description: 'Custom type return' }, + { type: 'Group[]', description: 'Custom array return type' } + ]; + + testCases.forEach(({ type, description }) => { + const dsl = `measure test() { true } PROVIDES ${type}`; + const result = compiler.compile(dsl, `test-measure-return-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Measure aggregation', () => { + const testCases = [ + { + input: `measure userPermissions(user: User) { + aggregate { + user.role.permissions + user.group.permissions + } USING majority + } PROVIDES Permission[]`, + description: 'Aggregation with majority strategy' + }, + { + input: `measure userClearance(user: User) { + aggregate { + user.clearance + user.role.clearance + user.group.clearance + } USING max + } PROVIDES string`, + description: 'Aggregation with max strategy' + }, + { + input: `measure userScore(user: User) { + aggregate { + user.reputation + user.activityScore + user.verificationLevel + } USING average + } PROVIDES number`, + description: 'Aggregation with average strategy' + }, + { + input: `measure userTrust(user: User) { + aggregate { + user.reputation + user.activityScore + user.verificationLevel + user.socialProof + } USING min + } PROVIDES number`, + description: 'Aggregation with min strategy' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-measure-aggregation-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Measure fusion', () => { + const testCases = [ + { + input: `measure effectiveClearance(user: User) { + fusion max { + user.clearance + user.role.clearance + user.group.clearance + } + } PROVIDES string`, + description: 'Fusion with max strategy' + }, + { + input: `measure userPermissions(user: User) { + fusion min { + user.role.permissions + user.group.permissions + } + } PROVIDES Permission[]`, + description: 'Fusion with min strategy' + }, + { + input: `measure userScore(user: User) { + fusion majority { + user.reputation + user.activityScore + user.verificationLevel + } + } PROVIDES number`, + description: 'Fusion with majority strategy' + }, + { + input: `measure userTrust(user: User) { + fusion average { + user.reputation + user.activityScore + user.verificationLevel + user.socialProof + } + } PROVIDES number`, + description: 'Fusion with average strategy' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-measure-fusion-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Complex measures', () => { + const testCases = [ + { + input: `measure userEffectivePermissions(user: User) { + aggregate { + user.role.permissions + user.group.permissions + user.directPermissions + } USING majority + } PROVIDES Permission[]`, + description: 'Complex aggregation with multiple sources' + }, + { + input: `measure userTrustScore(user: User) { + fusion average { + user.reputation + user.activityScore + user.verificationLevel + user.socialProof + user.peerRatings + } + } PROVIDES number`, + description: 'Complex fusion with multiple metrics' + }, + { + input: `measure userAccessLevel(user: User) { + fusion max { + user.clearance + user.role.clearance + user.group.clearance + user.temporaryClearance + } + } PROVIDES string`, + description: 'Complex clearance calculation' + }, + { + input: `measure userSimilarity(user1: User, user2: User) { + similar(user1, user2) |similarity| { + similarity + } with similarity > 0.5 + } PROVIDES number`, + description: 'Similarity measure with pattern matching' + }, + { + input: `measure userEffectiveRole(user: User) { + fusion majority { + user.role + user.temporaryRole + user.actingRole + } + } PROVIDES string`, + description: 'Role determination with multiple sources' + } + ]; + + testCases.forEach(({ input, description }) => { + const result = compiler.compile(input, `test-complex-measure-${Date.now()}`); + assert.ok(result.success, `${description} should parse successfully`); + }); + }); + + test('Measure error handling', () => { + const testCases = [ + { + input: `measure userRole(user: User) { + user.role + }`, + description: 'Missing PROVIDES clause should fail', + expectSuccess: false + }, + { + input: `measure userRole(user: User) { + user.role + } PROVIDES`, + description: 'Incomplete PROVIDES clause should fail', + expectSuccess: false + }, + { + input: `measure userRole(user: User) { + user.role + } PROVIDES string`, + description: 'Valid measure should succeed', + expectSuccess: true + }, + { + input: `measure userPermissions(user: User) { + aggregate { + user.role.permissions + user.group.permissions + } USING + } PROVIDES Permission[]`, + description: 'Incomplete USING clause should fail', + expectSuccess: false + }, + { + input: `measure userScore(user: User) { + fusion { + user.reputation + user.activityScore + } + } PROVIDES number`, + description: 'Missing fusion strategy should fail', + expectSuccess: false + }, + { + input: `measure userRole(user: User) { + invalid syntax here + } PROVIDES string`, + description: 'Invalid syntax should fail', + expectSuccess: false + } + ]; + + testCases.forEach(({ input, description, expectSuccess }) => { + try { + const result = compiler.compile(input, `test-measure-error-${Date.now()}`); + if (expectSuccess) { + assert.ok(result.success, `${description} should parse successfully`); + } else { + assert.ok(!result.success, `${description} should fail to parse`); + } + } catch { + if (!expectSuccess) { + // Expected to fail + } + } + }); + }); +}); diff --git a/tests/PeggyParser.test.js b/tests/PeggyParser.test.js new file mode 100644 index 0000000..5c7a9cf --- /dev/null +++ b/tests/PeggyParser.test.js @@ -0,0 +1,101 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { PeggyDSLParser } from '../src/parser/PeggyDSLParser.js'; + +describe('Peggy DSL Parser', () => { + const parser = new PeggyDSLParser(); + + test('Basic parsing', () => { + const dsl = ` + definition User { + role: string + isActive: boolean + } + + fact hasRole(user: User, role: string) + + evidence canRead(user: User, doc: Document) { + hasRole(user, 'admin') + } + `; + + const program = parser.parse(dsl); + assert.ok(program !== null, 'Program should be created'); + assert.ok(program.definitions.length === 1, 'Should have 1 definition'); + assert.ok(program.facts.length === 1, 'Should have 1 fact'); + assert.ok(program.evidence.length === 1, 'Should have 1 evidence'); + }); + + test('Complex DSL parsing', () => { + const dsl = ` + definition User { + role: string + isActive: boolean + clearance: string BEHAVES { + blurring adaptive confidence_95 + } CACHE eager + } + + fact hasRole(user: User, role: string) CACHE eager + fact isMember(user: User, group: Group) transitive CACHE lazy + + evidence canRead(user: User, doc: Document) { + hasRole(user, 'admin') + + isMember(user, *group) { + canRead(group, doc) + } limit 5 + + WHEN hasRole(user, 'admin') UNLESS isSuspended(user) + } + `; + + const program = parser.parse(dsl); + assert.ok(program !== null, 'Program should be created'); + assert.ok(program.definitions.length === 1, 'Should have 1 definition'); + assert.ok(program.facts.length === 2, 'Should have 2 facts'); + assert.ok(program.evidence.length === 1, 'Should have 1 evidence'); + }); + + test('Error handling', () => { + const invalidDSL = ` + definition User { + role: string + // Missing closing brace + + fact hasRole(user: User, role: string) + // Missing semicolon + `; + + assert.throws( + () => parser.parse(invalidDSL), + /Parsing failed/, + 'Should have parsing error message' + ); + }); + + test('Validation', () => { + const validDSL = ` + definition User { + role: string + isActive: boolean + } + + fact hasRole(user: User, role: string) + `; + + const invalidDSL = ` + definition User { + role: string + // Missing closing brace + `; + + const validResult = parser.validate(validDSL); + assert.ok(validResult.success, 'Valid DSL should pass validation'); + assert.ok(validResult.program !== null, 'Valid DSL should return program'); + + const invalidResult = parser.validate(invalidDSL); + assert.ok(!invalidResult.success, 'Invalid DSL should fail validation'); + assert.ok(invalidResult.errors.length > 0, 'Should have validation errors'); + }); +}); diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..d5c4f63 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,249 @@ +# Evidence DSL Test Suite + +## Overview + +This comprehensive test suite follows a **structural linguistic approach** to validate the Evidence DSL (Domain Specific Language) for authorization policies. The tests are organized incrementally from basic language primitives to complex integration scenarios. + +## Test Structure + +### 1. Structural Linguistic Tests (`StructuralLinguisticTests.js`) +**Level: Comprehensive** +- **Lexical Primitives**: Identifiers, literals, keywords, whitespace +- **Basic Expressions**: Arithmetic, logical, comparison, temporal +- **Type System**: Definitions, fields, behaviors, caching +- **Fact System**: Declarations, properties, caching, limits +- **Evidence System**: Rules, defeasible logic, pattern matching +- **Measure System**: Aggregation, fusion, return types +- **Complex Integration**: Multi-feature combinations + +### 2. Expression Tests (`ExpressionTests.js`) +**Level: Focused** +- Arithmetic operator precedence +- Logical operator precedence +- Comparison operators +- Temporal expressions +- Unary operators +- Attribute access +- Function calls +- Complex expressions +- Error handling + +### 3. Definition Tests (`DefinitionTests.js`) +**Level: Focused** +- Basic type definitions +- Field types (string, number, boolean, timestamp, custom) +- Array types +- Behaviors (decay, blur, TTL) +- Caching (eager, lazy) +- Complex definitions +- Error handling + +### 4. Fact Tests (`FactTests.js`) +**Level: Focused** +- Basic fact declarations +- Fact properties (transitive, symmetrical) +- Fact caching +- Fact limits +- Parameter types +- Complex facts +- Error handling + +### 5. Evidence Tests (`EvidenceTests.js`) +**Level: Focused** +- Basic evidence rules +- Defeasible logic (ALWAYS, WHEN/UNLESS, REQUIRES) +- Pattern matching with wildcards +- Fusion strategies (min, max, majority, average) +- Complex evidence composition +- Error handling + +### 6. Measure Tests (`MeasureTests.js`) +**Level: Focused** +- Basic measure definitions +- Return types +- Aggregation with different strategies +- Fusion with different strategies +- Complex measures +- Error handling + +### 7. Integration Tests (`IntegrationTests.js`) +**Level: Integration** +- Complete authorization systems +- Multi-domain systems +- Hierarchical access patterns +- Similarity-based access +- Temporal access patterns +- Complex behaviors +- Performance scenarios + +## Test Runner (`TestRunner.js`) + +The test runner orchestrates all test suites and provides: +- **Comprehensive Testing**: Run all test suites +- **Selective Testing**: Run specific test suites +- **Level-based Testing**: Run tests by complexity level +- **Detailed Reporting**: Summary and detailed results +- **Coverage Analysis**: Language feature coverage + +## Usage + +### Run All Tests +```javascript +import { runAllTests } from '../../../../../lib/src/ast/tests/tests/TestRunner.js'; + +const results = runAllTests(arbiter); +console.log(`Tests: ${results.passed}/${results.total} passed`); +``` + +### Run Specific Test Suites +```javascript +import { runSpecificTests } from '../../../../../lib/src/ast/tests/tests/TestRunner.js'; + +const results = runSpecificTests(arbiter, [ + 'Expression Tests', + 'Definition Tests' +]); +``` + +### Run Tests by Level +```javascript +import { runTestsByLevel } from '../../../../../lib/src/ast/tests/tests/TestRunner.js'; + +// Run only focused tests +const results = runTestsByLevel(arbiter, 'focused'); + +// Run only integration tests +const results = runTestsByLevel(arbiter, 'integration'); +``` + +## Language Feature Coverage + +### ✅ Lexical Primitives +- Identifiers (simple, with underscores, with numbers) +- Literals (string, number, boolean, duration) +- Keywords (reserved words) +- Whitespace and comments + +### ✅ Expression System +- Arithmetic operators (+, -, *, /) with precedence +- Logical operators (&&, ||, NOT) with precedence +- Comparison operators (==, !=, >, <, >=, <=) +- Temporal expressions (within) +- Unary operators (NOT, !) +- Attribute access (object.attribute) +- Function calls (predicate(args)) + +### ✅ Type System +- Type definitions with fields +- Field types (string, number, boolean, timestamp, custom) +- Array types (Type[]) +- Behaviors (decay, blur, TTL) +- Caching directives (eager, lazy) + +### ✅ Fact System +- Fact declarations with parameters +- Fact properties (transitive, symmetrical) +- Caching directives +- Limits for performance +- Parameter types + +### ✅ Evidence System +- Basic evidence rules +- Defeasible logic (ALWAYS, WHEN/UNLESS, REQUIRES) +- Pattern matching with wildcards (*) +- Binding clauses (|variable|) +- With clauses (with condition) +- Limits for pattern matching +- Fusion strategies (min, max, majority, average) + +### ✅ Measure System +- Measure definitions +- Return type specifications (PROVIDES) +- Aggregation with strategies (USING) +- Fusion with strategies +- Complex value computation + +### ✅ Integration Features +- Multi-domain systems +- Hierarchical access patterns +- Similarity-based access +- Temporal access patterns +- Complex behavior combinations +- Performance optimization scenarios + +## Test Philosophy + +### Structural Linguistic Approach +The tests follow a structural linguistic methodology: + +1. **Phonological Level**: Basic lexical elements (identifiers, literals) +2. **Morphological Level**: Word formation (operators, keywords) +3. **Syntactic Level**: Grammar rules (expressions, statements) +4. **Semantic Level**: Meaning (types, behaviors, logic) +5. **Pragmatic Level**: Usage (integration, real-world scenarios) + +### Incremental Complexity +Tests progress from simple to complex: +- **Level 1**: Lexical primitives +- **Level 2**: Basic expressions +- **Level 3**: Type system +- **Level 4**: Fact system +- **Level 5**: Evidence system +- **Level 6**: Measure system +- **Level 7**: Complex integration + +### Comprehensive Coverage +Each language feature is tested for: +- **Valid cases**: Correct syntax and semantics +- **Invalid cases**: Error handling and recovery +- **Edge cases**: Boundary conditions +- **Integration**: Multi-feature combinations + +## Running Tests + +### Prerequisites +- Node.js environment +- Arbiter instance for testing +- All dependencies installed + +### Basic Usage +```bash +# Run all tests +npm test + +# Run specific test file +node src/ast/tests/StructuralLinguisticTests.js + +# Run with specific arbiter +node -e " +import { runAllTests } from '../../../../../lib/src/ast/tests/src/ast/tests/TestRunner.js'; +const results = runAllTests(arbiter); +console.log(results); +" +``` + +### Test Output +The test runner provides: +- **Progress indicators**: Real-time test execution +- **Detailed results**: Pass/fail status for each test +- **Error reporting**: Specific error messages for failures +- **Performance metrics**: Execution time for each suite +- **Coverage analysis**: Language feature coverage + +## Contributing + +When adding new tests: +1. Follow the structural linguistic approach +2. Test both valid and invalid cases +3. Include error handling tests +4. Document test purpose and expected behavior +5. Maintain incremental complexity +6. Update coverage documentation + +## Test Maintenance + +- **Regular Updates**: Keep tests current with language changes +- **Performance Monitoring**: Track test execution time +- **Coverage Analysis**: Ensure comprehensive feature coverage +- **Error Handling**: Validate error messages and recovery +- **Integration Testing**: Test real-world scenarios