diff --git a/README.md b/README.md index b3b5867..62c45de 100644 --- a/README.md +++ b/README.md @@ -88,11 +88,6 @@ test(expressions[0]); // => true parse(expressions[0]); // => { result, tree } ``` -**Known limitation:** `$request.body#/...` and `$response.body#/...` expressions with JSON pointers cannot be reliably -extracted from `{expression}` syntax. This is because [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) -(JSON Pointer) allows the `}` character in pointer paths, making it impossible to determine where the expression ends. -Use `parse()` directly on the raw expression for these cases. - #### Parsing Parsing a Runtime Expression is as simple as importing the **parse** function and calling it. @@ -266,52 +261,67 @@ The runtime expression is defined by the following [ABNF](https://tools.ietf.org ```abnf ; Arazzo runtime expression ABNF syntax -expression = ( "$url" / "$method" / "$statusCode" / "$request." source / "$response." source / "$inputs." name / "$outputs." name / "$steps." name / "$workflows." name / "$sourceDescriptions." name / "$components." name ) -source = ( header-reference / query-reference / path-reference / body-reference ) -header-reference = "header." token -query-reference = "query." name -path-reference = "path." name -body-reference = "body" ["#" json-pointer ] -name = *( CHAR ) +expression = ( + "$url" / + "$method" / + "$statusCode" / + "$request." source / + "$response." source / + "$inputs." inputs-reference / + "$outputs." outputs-reference / + "$steps." steps-reference / + "$workflows." workflows-reference / + "$sourceDescriptions." source-reference / + "$components." components-reference / + "$self" + ) +; Request/Response sources +source = ( header-reference / query-reference / path-reference / body-reference ) +header-reference = "header." token +query-reference = "query." name +path-reference = "path." name +body-reference = "body" ["#" json-pointer ] + +; Input/Output references +inputs-reference = input-name ["#" json-pointer] +input-name = identifier +outputs-reference = output-name ["#" json-pointer] +output-name = identifier + +; Steps expressions +steps-reference = step-id ".outputs." output-name ["#" json-pointer] +step-id = identifier-strict + +; Workflows expressions +workflows-reference = workflow-id "." workflow-field "." workflow-field-name ["#" json-pointer] +workflow-id = identifier-strict +workflow-field = "inputs" / "outputs" +workflow-field-name = identifier + +; Source descriptions expressions +source-reference = source-name "." source-reference-id +source-name = identifier-strict +source-reference-id = 1*CHAR + +; Components expressions +components-reference = component-type "." component-name +component-type = "parameters" / "successActions" / "failureActions" +component-name = identifier + +; Unconstrained name rule for query/path references +name = *( CHAR ) ; Grammar for parsing template strings with embedded expressions expression-string = *( literal-char / embedded-expression ) embedded-expression = "{" expression "}" literal-char = %x00-7A / %x7C / %x7E-10FFFF ; anything except { (%x7B) and } (%x7D) -; Secondary grammar for parsing $steps name part -; Format: {stepId}.{field}.{subField}[#/{jsonPointer}] -steps-name = steps-id "." steps-field "." steps-sub-field ["#" json-pointer] -steps-id = 1*(ALPHA / DIGIT / "_" / "-") -steps-field = "outputs" -steps-sub-field = 1*(ALPHA / DIGIT / "." / "-" / "_") - -; Secondary grammar for parsing $workflows name part -; Format: {workflowId}.{field}.{subField}[#/{jsonPointer}] -workflows-name = workflows-id "." workflows-field "." workflows-sub-field ["#" json-pointer] -workflows-id = 1*(ALPHA / DIGIT / "_" / "-") -workflows-field = "inputs" / "outputs" -workflows-sub-field = 1*(ALPHA / DIGIT / "." / "-" / "_") - -; Secondary grammar for parsing $sourceDescriptions name part -; Format: {sourceName}.{reference} -; reference can be operationId (unconstrained) or workflowId (constrained) -source-descriptions-name = source-descriptions-source-name "." source-descriptions-reference -source-descriptions-source-name = 1*(ALPHA / DIGIT / "_" / "-") -source-descriptions-reference = 1*CHAR - -; Secondary grammar for parsing $components name part -; Format: {field}.{subField} -; Allowed fields: parameters, successActions, failureActions -components-name = components-field "." components-sub-field -components-field = "parameters" / "successActions" / "failureActions" -components-sub-field = 1*(ALPHA / DIGIT / "." / "-" / "_") - -; https://datatracker.ietf.org/doc/html/rfc6901#section-3 +; JSON Pointer (RFC 6901, adapted) +; { (%x7B) and } (%x7D) are excluded from 'unescaped' for unambiguous embedded expression parsing json-pointer = *( "/" reference-token ) reference-token = *( unescaped / escaped ) -unescaped = %x00-2E / %x30-7D / %x7F-10FFFF - ; %x2F ('/') and %x7E ('~') are excluded from 'unescaped' +unescaped = %x00-2E / %x30-7A / %x7C / %x7F-10FFFF + ; %x2F ('/'), %x7E ('~'), %x7B ('{'), %x7D ('}') are excluded escaped = "~" ( "0" / "1" ) ; representing '~' and '/', respectively @@ -338,6 +348,12 @@ escape = %x5C ; \ unescape = %x20-21 / %x23-5B / %x5D-7A / %x7C / %x7E-10FFFF ; %x7B ('{') and %x7D ('}') are excluded from 'unescape' +; Identifier rules +identifier = 1*(ALPHA / DIGIT / "." / "-" / "_") + ; Alphanumeric with dots, hyphens, underscores +identifier-strict = 1*(ALPHA / DIGIT / "_" / "-") + ; Alphanumeric with hyphens, underscores (no dots) + ; https://datatracker.ietf.org/doc/html/rfc5234#appendix-B.1 HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" DIGIT = %x30-39 ; 0-9 @@ -358,11 +374,15 @@ Request parameter | `$request.path.id` | Request parameters MUST be Request body property | `$request.body#/user/uuid` | In operations which accept payloads, references may be made to portions of the `requestBody` or the entire body. Request URL | `$url` | Response value | `$response.body#/status` | In operations which return payloads, references may be made to portions of the response body or the entire body. -Response header | `$response.header.Server` | Single header values only are available -workflow input | `$inputs.username` or `$workflows.foo.inputs.username` | Single input values only are available +Response header | `$response.header.Server` | Single header values only are available. +Self URI | `$self` | References the canonical URI of the current Arazzo Description. +Workflow input | `$inputs.username` | Single input values only are available. +Workflow input property | `$inputs.customer#/firstName` | To access nested properties within an input object, use JSON Pointer syntax. Step output value | `$steps.someStepId.outputs.pets` | In situations where the output named property return payloads, references may be made to portions of the response body (e.g., `$steps.someStepId.outputs.pets#/0/id`) or the entire body. Workflow output value | `$outputs.bar` or `$workflows.foo.outputs.bar` | In situations where the output named property return payloads, references may be made to portions of the response body (e.g., `$workflows.foo.outputs.mappedResponse#/name`) or the entire body. +Source description reference | `$sourceDescriptions.petstore.getPetById` | References an operationId or workflowId from the named source description. Components parameter | `$components.parameters.foo` | Accesses a foo parameter defined within the Components Object. +Components action | `$components.successActions.bar` | Accesses a success or failure action defined within the Components Object. Runtime expressions preserve the type of the referenced value. Expressions can be embedded into string values by surrounding the expression with `{}` curly braces. diff --git a/src/grammar.bnf b/src/grammar.bnf index d6d616e..e352a56 100644 --- a/src/grammar.bnf +++ b/src/grammar.bnf @@ -1,50 +1,65 @@ ; Arazzo runtime expression ABNF syntax -expression = ( "$url" / "$method" / "$statusCode" / "$request." source / "$response." source / "$inputs." name / "$outputs." name / "$steps." name / "$workflows." name / "$sourceDescriptions." name / "$components." name ) -source = ( header-reference / query-reference / path-reference / body-reference ) -header-reference = "header." token -query-reference = "query." name -path-reference = "path." name -body-reference = "body" ["#" json-pointer ] -name = *( CHAR ) +expression = ( + "$url" / + "$method" / + "$statusCode" / + "$request." source / + "$response." source / + "$inputs." inputs-reference / + "$outputs." outputs-reference / + "$steps." steps-reference / + "$workflows." workflows-reference / + "$sourceDescriptions." source-reference / + "$components." components-reference / + "$self" + ) +; Request/Response sources +source = ( header-reference / query-reference / path-reference / body-reference ) +header-reference = "header." token +query-reference = "query." name +path-reference = "path." name +body-reference = "body" ["#" json-pointer ] -; Grammar for parsing template strings with embedded expressions -expression-string = *( literal-char / embedded-expression ) -embedded-expression = "{" expression "}" -literal-char = %x00-7A / %x7C / %x7E-10FFFF ; anything except { (%x7B) and } (%x7D) +; Input/Output references +inputs-reference = input-name ["#" json-pointer] +input-name = identifier +outputs-reference = output-name ["#" json-pointer] +output-name = identifier + +; Steps expressions +steps-reference = step-id ".outputs." output-name ["#" json-pointer] +step-id = identifier-strict -; Secondary grammar for parsing $steps name part -; Format: {stepId}.{field}.{subField}[#/{jsonPointer}] -steps-name = steps-id "." steps-field "." steps-sub-field ["#" json-pointer] -steps-id = 1*(ALPHA / DIGIT / "_" / "-") -steps-field = "outputs" -steps-sub-field = 1*(ALPHA / DIGIT / "." / "-" / "_") +; Workflows expressions +workflows-reference = workflow-id "." workflow-field "." workflow-field-name ["#" json-pointer] +workflow-id = identifier-strict +workflow-field = "inputs" / "outputs" +workflow-field-name = identifier -; Secondary grammar for parsing $workflows name part -; Format: {workflowId}.{field}.{subField}[#/{jsonPointer}] -workflows-name = workflows-id "." workflows-field "." workflows-sub-field ["#" json-pointer] -workflows-id = 1*(ALPHA / DIGIT / "_" / "-") -workflows-field = "inputs" / "outputs" -workflows-sub-field = 1*(ALPHA / DIGIT / "." / "-" / "_") +; Source descriptions expressions +source-reference = source-name "." source-reference-id +source-name = identifier-strict +source-reference-id = 1*CHAR -; Secondary grammar for parsing $sourceDescriptions name part -; Format: {sourceName}.{reference} -; reference can be operationId (unconstrained) or workflowId (constrained) -source-descriptions-name = source-descriptions-source-name "." source-descriptions-reference -source-descriptions-source-name = 1*(ALPHA / DIGIT / "_" / "-") -source-descriptions-reference = 1*CHAR +; Components expressions +components-reference = component-type "." component-name +component-type = "parameters" / "successActions" / "failureActions" +component-name = identifier -; Secondary grammar for parsing $components name part -; Format: {field}.{subField} -; Allowed fields: parameters, successActions, failureActions -components-name = components-field "." components-sub-field -components-field = "parameters" / "successActions" / "failureActions" -components-sub-field = 1*(ALPHA / DIGIT / "." / "-" / "_") +; Unconstrained name rule for query/path references +name = *( CHAR ) -; https://datatracker.ietf.org/doc/html/rfc6901#section-3 +; Grammar for parsing template strings with embedded expressions +expression-string = *( literal-char / embedded-expression ) +embedded-expression = "{" expression "}" +literal-char = %x00-7A / %x7C / %x7E-10FFFF ; anything except { (%x7B) and } (%x7D) + +; JSON Pointer (RFC 6901, adapted) +; { (%x7B) and } (%x7D) are excluded from 'unescaped' for unambiguous embedded expression parsing json-pointer = *( "/" reference-token ) reference-token = *( unescaped / escaped ) -unescaped = %x00-2E / %x30-7D / %x7F-10FFFF - ; %x2F ('/') and %x7E ('~') are excluded from 'unescaped' +unescaped = %x00-2E / %x30-7A / %x7C / %x7F-10FFFF + ; %x2F ('/'), %x7E ('~'), %x7B ('{'), %x7D ('}') are excluded escaped = "~" ( "0" / "1" ) ; representing '~' and '/', respectively @@ -71,6 +86,12 @@ escape = %x5C ; \ unescape = %x20-21 / %x23-5B / %x5D-7A / %x7C / %x7E-10FFFF ; %x7B ('{') and %x7D ('}') are excluded from 'unescape' +; Identifier rules +identifier = 1*(ALPHA / DIGIT / "." / "-" / "_") + ; Alphanumeric with dots, hyphens, underscores +identifier-strict = 1*(ALPHA / DIGIT / "_" / "-") + ; Alphanumeric with hyphens, underscores (no dots) + ; https://datatracker.ietf.org/doc/html/rfc5234#appendix-B.1 HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" DIGIT = %x30-39 ; 0-9 diff --git a/src/grammar.js b/src/grammar.js index bf7fb55..a5d7122 100644 --- a/src/grammar.js +++ b/src/grammar.js @@ -5,16 +5,16 @@ export default function grammar(){ // ``` // SUMMARY - // rules = 36 + // rules = 40 // udts = 0 - // opcodes = 212 + // opcodes = 204 // --- ABNF original opcodes - // ALT = 21 - // CAT = 24 - // REP = 16 - // RNM = 55 - // TLS = 72 - // TBS = 12 + // ALT = 17 + // CAT = 28 + // REP = 14 + // RNM = 57 + // TLS = 63 + // TBS = 13 // TRG = 12 // --- SABNF superset opcodes // UDT = 0 @@ -33,36 +33,40 @@ export default function grammar(){ this.rules[3] = { name: 'query-reference', lower: 'query-reference', index: 3, isBkr: false }; this.rules[4] = { name: 'path-reference', lower: 'path-reference', index: 4, isBkr: false }; this.rules[5] = { name: 'body-reference', lower: 'body-reference', index: 5, isBkr: false }; - this.rules[6] = { name: 'name', lower: 'name', index: 6, isBkr: false }; - this.rules[7] = { name: 'expression-string', lower: 'expression-string', index: 7, isBkr: false }; - this.rules[8] = { name: 'embedded-expression', lower: 'embedded-expression', index: 8, isBkr: false }; - this.rules[9] = { name: 'literal-char', lower: 'literal-char', index: 9, isBkr: false }; - this.rules[10] = { name: 'steps-name', lower: 'steps-name', index: 10, isBkr: false }; - this.rules[11] = { name: 'steps-id', lower: 'steps-id', index: 11, isBkr: false }; - this.rules[12] = { name: 'steps-field', lower: 'steps-field', index: 12, isBkr: false }; - this.rules[13] = { name: 'steps-sub-field', lower: 'steps-sub-field', index: 13, isBkr: false }; - this.rules[14] = { name: 'workflows-name', lower: 'workflows-name', index: 14, isBkr: false }; - this.rules[15] = { name: 'workflows-id', lower: 'workflows-id', index: 15, isBkr: false }; - this.rules[16] = { name: 'workflows-field', lower: 'workflows-field', index: 16, isBkr: false }; - this.rules[17] = { name: 'workflows-sub-field', lower: 'workflows-sub-field', index: 17, isBkr: false }; - this.rules[18] = { name: 'source-descriptions-name', lower: 'source-descriptions-name', index: 18, isBkr: false }; - this.rules[19] = { name: 'source-descriptions-source-name', lower: 'source-descriptions-source-name', index: 19, isBkr: false }; - this.rules[20] = { name: 'source-descriptions-reference', lower: 'source-descriptions-reference', index: 20, isBkr: false }; - this.rules[21] = { name: 'components-name', lower: 'components-name', index: 21, isBkr: false }; - this.rules[22] = { name: 'components-field', lower: 'components-field', index: 22, isBkr: false }; - this.rules[23] = { name: 'components-sub-field', lower: 'components-sub-field', index: 23, isBkr: false }; - this.rules[24] = { name: 'json-pointer', lower: 'json-pointer', index: 24, isBkr: false }; - this.rules[25] = { name: 'reference-token', lower: 'reference-token', index: 25, isBkr: false }; - this.rules[26] = { name: 'unescaped', lower: 'unescaped', index: 26, isBkr: false }; - this.rules[27] = { name: 'escaped', lower: 'escaped', index: 27, isBkr: false }; - this.rules[28] = { name: 'token', lower: 'token', index: 28, isBkr: false }; - this.rules[29] = { name: 'tchar', lower: 'tchar', index: 29, isBkr: false }; - this.rules[30] = { name: 'CHAR', lower: 'char', index: 30, isBkr: false }; - this.rules[31] = { name: 'escape', lower: 'escape', index: 31, isBkr: false }; - this.rules[32] = { name: 'unescape', lower: 'unescape', index: 32, isBkr: false }; - this.rules[33] = { name: 'HEXDIG', lower: 'hexdig', index: 33, isBkr: false }; - this.rules[34] = { name: 'DIGIT', lower: 'digit', index: 34, isBkr: false }; - this.rules[35] = { name: 'ALPHA', lower: 'alpha', index: 35, isBkr: false }; + this.rules[6] = { name: 'inputs-reference', lower: 'inputs-reference', index: 6, isBkr: false }; + this.rules[7] = { name: 'input-name', lower: 'input-name', index: 7, isBkr: false }; + this.rules[8] = { name: 'outputs-reference', lower: 'outputs-reference', index: 8, isBkr: false }; + this.rules[9] = { name: 'output-name', lower: 'output-name', index: 9, isBkr: false }; + this.rules[10] = { name: 'steps-reference', lower: 'steps-reference', index: 10, isBkr: false }; + this.rules[11] = { name: 'step-id', lower: 'step-id', index: 11, isBkr: false }; + this.rules[12] = { name: 'workflows-reference', lower: 'workflows-reference', index: 12, isBkr: false }; + this.rules[13] = { name: 'workflow-id', lower: 'workflow-id', index: 13, isBkr: false }; + this.rules[14] = { name: 'workflow-field', lower: 'workflow-field', index: 14, isBkr: false }; + this.rules[15] = { name: 'workflow-field-name', lower: 'workflow-field-name', index: 15, isBkr: false }; + this.rules[16] = { name: 'source-reference', lower: 'source-reference', index: 16, isBkr: false }; + this.rules[17] = { name: 'source-name', lower: 'source-name', index: 17, isBkr: false }; + this.rules[18] = { name: 'source-reference-id', lower: 'source-reference-id', index: 18, isBkr: false }; + this.rules[19] = { name: 'components-reference', lower: 'components-reference', index: 19, isBkr: false }; + this.rules[20] = { name: 'component-type', lower: 'component-type', index: 20, isBkr: false }; + this.rules[21] = { name: 'component-name', lower: 'component-name', index: 21, isBkr: false }; + this.rules[22] = { name: 'name', lower: 'name', index: 22, isBkr: false }; + this.rules[23] = { name: 'expression-string', lower: 'expression-string', index: 23, isBkr: false }; + this.rules[24] = { name: 'embedded-expression', lower: 'embedded-expression', index: 24, isBkr: false }; + this.rules[25] = { name: 'literal-char', lower: 'literal-char', index: 25, isBkr: false }; + this.rules[26] = { name: 'json-pointer', lower: 'json-pointer', index: 26, isBkr: false }; + this.rules[27] = { name: 'reference-token', lower: 'reference-token', index: 27, isBkr: false }; + this.rules[28] = { name: 'unescaped', lower: 'unescaped', index: 28, isBkr: false }; + this.rules[29] = { name: 'escaped', lower: 'escaped', index: 29, isBkr: false }; + this.rules[30] = { name: 'token', lower: 'token', index: 30, isBkr: false }; + this.rules[31] = { name: 'tchar', lower: 'tchar', index: 31, isBkr: false }; + this.rules[32] = { name: 'CHAR', lower: 'char', index: 32, isBkr: false }; + this.rules[33] = { name: 'escape', lower: 'escape', index: 33, isBkr: false }; + this.rules[34] = { name: 'unescape', lower: 'unescape', index: 34, isBkr: false }; + this.rules[35] = { name: 'identifier', lower: 'identifier', index: 35, isBkr: false }; + this.rules[36] = { name: 'identifier-strict', lower: 'identifier-strict', index: 36, isBkr: false }; + this.rules[37] = { name: 'HEXDIG', lower: 'hexdig', index: 37, isBkr: false }; + this.rules[38] = { name: 'DIGIT', lower: 'digit', index: 38, isBkr: false }; + this.rules[39] = { name: 'ALPHA', lower: 'alpha', index: 39, isBkr: false }; /* UDTS */ this.udts = []; @@ -70,7 +74,7 @@ export default function grammar(){ /* OPCODES */ /* expression */ this.rules[0].opcodes = []; - this.rules[0].opcodes[0] = { type: 1, children: [1,2,3,4,7,10,13,16,19,22,25] };// ALT + this.rules[0].opcodes[0] = { type: 1, children: [1,2,3,4,7,10,13,16,19,22,25,28] };// ALT this.rules[0].opcodes[1] = { type: 7, string: [36,117,114,108] };// TLS this.rules[0].opcodes[2] = { type: 7, string: [36,109,101,116,104,111,100] };// TLS this.rules[0].opcodes[3] = { type: 7, string: [36,115,116,97,116,117,115,99,111,100,101] };// TLS @@ -82,22 +86,23 @@ export default function grammar(){ this.rules[0].opcodes[9] = { type: 4, index: 1 };// RNM(source) this.rules[0].opcodes[10] = { type: 2, children: [11,12] };// CAT this.rules[0].opcodes[11] = { type: 7, string: [36,105,110,112,117,116,115,46] };// TLS - this.rules[0].opcodes[12] = { type: 4, index: 6 };// RNM(name) + this.rules[0].opcodes[12] = { type: 4, index: 6 };// RNM(inputs-reference) this.rules[0].opcodes[13] = { type: 2, children: [14,15] };// CAT this.rules[0].opcodes[14] = { type: 7, string: [36,111,117,116,112,117,116,115,46] };// TLS - this.rules[0].opcodes[15] = { type: 4, index: 6 };// RNM(name) + this.rules[0].opcodes[15] = { type: 4, index: 8 };// RNM(outputs-reference) this.rules[0].opcodes[16] = { type: 2, children: [17,18] };// CAT this.rules[0].opcodes[17] = { type: 7, string: [36,115,116,101,112,115,46] };// TLS - this.rules[0].opcodes[18] = { type: 4, index: 6 };// RNM(name) + this.rules[0].opcodes[18] = { type: 4, index: 10 };// RNM(steps-reference) this.rules[0].opcodes[19] = { type: 2, children: [20,21] };// CAT this.rules[0].opcodes[20] = { type: 7, string: [36,119,111,114,107,102,108,111,119,115,46] };// TLS - this.rules[0].opcodes[21] = { type: 4, index: 6 };// RNM(name) + this.rules[0].opcodes[21] = { type: 4, index: 12 };// RNM(workflows-reference) this.rules[0].opcodes[22] = { type: 2, children: [23,24] };// CAT this.rules[0].opcodes[23] = { type: 7, string: [36,115,111,117,114,99,101,100,101,115,99,114,105,112,116,105,111,110,115,46] };// TLS - this.rules[0].opcodes[24] = { type: 4, index: 6 };// RNM(name) + this.rules[0].opcodes[24] = { type: 4, index: 16 };// RNM(source-reference) this.rules[0].opcodes[25] = { type: 2, children: [26,27] };// CAT this.rules[0].opcodes[26] = { type: 7, string: [36,99,111,109,112,111,110,101,110,116,115,46] };// TLS - this.rules[0].opcodes[27] = { type: 4, index: 6 };// RNM(name) + this.rules[0].opcodes[27] = { type: 4, index: 19 };// RNM(components-reference) + this.rules[0].opcodes[28] = { type: 7, string: [36,115,101,108,102] };// TLS /* source */ this.rules[1].opcodes = []; @@ -111,19 +116,19 @@ export default function grammar(){ this.rules[2].opcodes = []; this.rules[2].opcodes[0] = { type: 2, children: [1,2] };// CAT this.rules[2].opcodes[1] = { type: 7, string: [104,101,97,100,101,114,46] };// TLS - this.rules[2].opcodes[2] = { type: 4, index: 28 };// RNM(token) + this.rules[2].opcodes[2] = { type: 4, index: 30 };// RNM(token) /* query-reference */ this.rules[3].opcodes = []; this.rules[3].opcodes[0] = { type: 2, children: [1,2] };// CAT this.rules[3].opcodes[1] = { type: 7, string: [113,117,101,114,121,46] };// TLS - this.rules[3].opcodes[2] = { type: 4, index: 6 };// RNM(name) + this.rules[3].opcodes[2] = { type: 4, index: 22 };// RNM(name) /* path-reference */ this.rules[4].opcodes = []; this.rules[4].opcodes[0] = { type: 2, children: [1,2] };// CAT this.rules[4].opcodes[1] = { type: 7, string: [112,97,116,104,46] };// TLS - this.rules[4].opcodes[2] = { type: 4, index: 6 };// RNM(name) + this.rules[4].opcodes[2] = { type: 4, index: 22 };// RNM(name) /* body-reference */ this.rules[5].opcodes = []; @@ -132,312 +137,330 @@ export default function grammar(){ this.rules[5].opcodes[2] = { type: 3, min: 0, max: 1 };// REP this.rules[5].opcodes[3] = { type: 2, children: [4,5] };// CAT this.rules[5].opcodes[4] = { type: 7, string: [35] };// TLS - this.rules[5].opcodes[5] = { type: 4, index: 24 };// RNM(json-pointer) + this.rules[5].opcodes[5] = { type: 4, index: 26 };// RNM(json-pointer) - /* name */ + /* inputs-reference */ this.rules[6].opcodes = []; - this.rules[6].opcodes[0] = { type: 3, min: 0, max: Infinity };// REP - this.rules[6].opcodes[1] = { type: 4, index: 30 };// RNM(CHAR) - - /* expression-string */ + this.rules[6].opcodes[0] = { type: 2, children: [1,2] };// CAT + this.rules[6].opcodes[1] = { type: 4, index: 7 };// RNM(input-name) + this.rules[6].opcodes[2] = { type: 3, min: 0, max: 1 };// REP + this.rules[6].opcodes[3] = { type: 2, children: [4,5] };// CAT + this.rules[6].opcodes[4] = { type: 7, string: [35] };// TLS + this.rules[6].opcodes[5] = { type: 4, index: 26 };// RNM(json-pointer) + + /* input-name */ this.rules[7].opcodes = []; - this.rules[7].opcodes[0] = { type: 3, min: 0, max: Infinity };// REP - this.rules[7].opcodes[1] = { type: 1, children: [2,3] };// ALT - this.rules[7].opcodes[2] = { type: 4, index: 9 };// RNM(literal-char) - this.rules[7].opcodes[3] = { type: 4, index: 8 };// RNM(embedded-expression) + this.rules[7].opcodes[0] = { type: 4, index: 35 };// RNM(identifier) - /* embedded-expression */ + /* outputs-reference */ this.rules[8].opcodes = []; - this.rules[8].opcodes[0] = { type: 2, children: [1,2,3] };// CAT - this.rules[8].opcodes[1] = { type: 7, string: [123] };// TLS - this.rules[8].opcodes[2] = { type: 4, index: 0 };// RNM(expression) - this.rules[8].opcodes[3] = { type: 7, string: [125] };// TLS - - /* literal-char */ + this.rules[8].opcodes[0] = { type: 2, children: [1,2] };// CAT + this.rules[8].opcodes[1] = { type: 4, index: 9 };// RNM(output-name) + this.rules[8].opcodes[2] = { type: 3, min: 0, max: 1 };// REP + this.rules[8].opcodes[3] = { type: 2, children: [4,5] };// CAT + this.rules[8].opcodes[4] = { type: 7, string: [35] };// TLS + this.rules[8].opcodes[5] = { type: 4, index: 26 };// RNM(json-pointer) + + /* output-name */ this.rules[9].opcodes = []; - this.rules[9].opcodes[0] = { type: 1, children: [1,2,3] };// ALT - this.rules[9].opcodes[1] = { type: 5, min: 0, max: 122 };// TRG - this.rules[9].opcodes[2] = { type: 6, string: [124] };// TBS - this.rules[9].opcodes[3] = { type: 5, min: 126, max: 1114111 };// TRG + this.rules[9].opcodes[0] = { type: 4, index: 35 };// RNM(identifier) - /* steps-name */ + /* steps-reference */ this.rules[10].opcodes = []; - this.rules[10].opcodes[0] = { type: 2, children: [1,2,3,4,5,6] };// CAT - this.rules[10].opcodes[1] = { type: 4, index: 11 };// RNM(steps-id) - this.rules[10].opcodes[2] = { type: 7, string: [46] };// TLS - this.rules[10].opcodes[3] = { type: 4, index: 12 };// RNM(steps-field) - this.rules[10].opcodes[4] = { type: 7, string: [46] };// TLS - this.rules[10].opcodes[5] = { type: 4, index: 13 };// RNM(steps-sub-field) - this.rules[10].opcodes[6] = { type: 3, min: 0, max: 1 };// REP - this.rules[10].opcodes[7] = { type: 2, children: [8,9] };// CAT - this.rules[10].opcodes[8] = { type: 7, string: [35] };// TLS - this.rules[10].opcodes[9] = { type: 4, index: 24 };// RNM(json-pointer) - - /* steps-id */ + this.rules[10].opcodes[0] = { type: 2, children: [1,2,3,4] };// CAT + this.rules[10].opcodes[1] = { type: 4, index: 11 };// RNM(step-id) + this.rules[10].opcodes[2] = { type: 7, string: [46,111,117,116,112,117,116,115,46] };// TLS + this.rules[10].opcodes[3] = { type: 4, index: 9 };// RNM(output-name) + this.rules[10].opcodes[4] = { type: 3, min: 0, max: 1 };// REP + this.rules[10].opcodes[5] = { type: 2, children: [6,7] };// CAT + this.rules[10].opcodes[6] = { type: 7, string: [35] };// TLS + this.rules[10].opcodes[7] = { type: 4, index: 26 };// RNM(json-pointer) + + /* step-id */ this.rules[11].opcodes = []; - this.rules[11].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP - this.rules[11].opcodes[1] = { type: 1, children: [2,3,4,5] };// ALT - this.rules[11].opcodes[2] = { type: 4, index: 35 };// RNM(ALPHA) - this.rules[11].opcodes[3] = { type: 4, index: 34 };// RNM(DIGIT) - this.rules[11].opcodes[4] = { type: 7, string: [95] };// TLS - this.rules[11].opcodes[5] = { type: 7, string: [45] };// TLS - - /* steps-field */ - this.rules[12].opcodes = []; - this.rules[12].opcodes[0] = { type: 7, string: [111,117,116,112,117,116,115] };// TLS + this.rules[11].opcodes[0] = { type: 4, index: 36 };// RNM(identifier-strict) - /* steps-sub-field */ + /* workflows-reference */ + this.rules[12].opcodes = []; + this.rules[12].opcodes[0] = { type: 2, children: [1,2,3,4,5,6] };// CAT + this.rules[12].opcodes[1] = { type: 4, index: 13 };// RNM(workflow-id) + this.rules[12].opcodes[2] = { type: 7, string: [46] };// TLS + this.rules[12].opcodes[3] = { type: 4, index: 14 };// RNM(workflow-field) + this.rules[12].opcodes[4] = { type: 7, string: [46] };// TLS + this.rules[12].opcodes[5] = { type: 4, index: 15 };// RNM(workflow-field-name) + this.rules[12].opcodes[6] = { type: 3, min: 0, max: 1 };// REP + this.rules[12].opcodes[7] = { type: 2, children: [8,9] };// CAT + this.rules[12].opcodes[8] = { type: 7, string: [35] };// TLS + this.rules[12].opcodes[9] = { type: 4, index: 26 };// RNM(json-pointer) + + /* workflow-id */ this.rules[13].opcodes = []; - this.rules[13].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP - this.rules[13].opcodes[1] = { type: 1, children: [2,3,4,5,6] };// ALT - this.rules[13].opcodes[2] = { type: 4, index: 35 };// RNM(ALPHA) - this.rules[13].opcodes[3] = { type: 4, index: 34 };// RNM(DIGIT) - this.rules[13].opcodes[4] = { type: 7, string: [46] };// TLS - this.rules[13].opcodes[5] = { type: 7, string: [45] };// TLS - this.rules[13].opcodes[6] = { type: 7, string: [95] };// TLS - - /* workflows-name */ + this.rules[13].opcodes[0] = { type: 4, index: 36 };// RNM(identifier-strict) + + /* workflow-field */ this.rules[14].opcodes = []; - this.rules[14].opcodes[0] = { type: 2, children: [1,2,3,4,5,6] };// CAT - this.rules[14].opcodes[1] = { type: 4, index: 15 };// RNM(workflows-id) - this.rules[14].opcodes[2] = { type: 7, string: [46] };// TLS - this.rules[14].opcodes[3] = { type: 4, index: 16 };// RNM(workflows-field) - this.rules[14].opcodes[4] = { type: 7, string: [46] };// TLS - this.rules[14].opcodes[5] = { type: 4, index: 17 };// RNM(workflows-sub-field) - this.rules[14].opcodes[6] = { type: 3, min: 0, max: 1 };// REP - this.rules[14].opcodes[7] = { type: 2, children: [8,9] };// CAT - this.rules[14].opcodes[8] = { type: 7, string: [35] };// TLS - this.rules[14].opcodes[9] = { type: 4, index: 24 };// RNM(json-pointer) - - /* workflows-id */ + this.rules[14].opcodes[0] = { type: 1, children: [1,2] };// ALT + this.rules[14].opcodes[1] = { type: 7, string: [105,110,112,117,116,115] };// TLS + this.rules[14].opcodes[2] = { type: 7, string: [111,117,116,112,117,116,115] };// TLS + + /* workflow-field-name */ this.rules[15].opcodes = []; - this.rules[15].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP - this.rules[15].opcodes[1] = { type: 1, children: [2,3,4,5] };// ALT - this.rules[15].opcodes[2] = { type: 4, index: 35 };// RNM(ALPHA) - this.rules[15].opcodes[3] = { type: 4, index: 34 };// RNM(DIGIT) - this.rules[15].opcodes[4] = { type: 7, string: [95] };// TLS - this.rules[15].opcodes[5] = { type: 7, string: [45] };// TLS - - /* workflows-field */ + this.rules[15].opcodes[0] = { type: 4, index: 35 };// RNM(identifier) + + /* source-reference */ this.rules[16].opcodes = []; - this.rules[16].opcodes[0] = { type: 1, children: [1,2] };// ALT - this.rules[16].opcodes[1] = { type: 7, string: [105,110,112,117,116,115] };// TLS - this.rules[16].opcodes[2] = { type: 7, string: [111,117,116,112,117,116,115] };// TLS + this.rules[16].opcodes[0] = { type: 2, children: [1,2,3] };// CAT + this.rules[16].opcodes[1] = { type: 4, index: 17 };// RNM(source-name) + this.rules[16].opcodes[2] = { type: 7, string: [46] };// TLS + this.rules[16].opcodes[3] = { type: 4, index: 18 };// RNM(source-reference-id) - /* workflows-sub-field */ + /* source-name */ this.rules[17].opcodes = []; - this.rules[17].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP - this.rules[17].opcodes[1] = { type: 1, children: [2,3,4,5,6] };// ALT - this.rules[17].opcodes[2] = { type: 4, index: 35 };// RNM(ALPHA) - this.rules[17].opcodes[3] = { type: 4, index: 34 };// RNM(DIGIT) - this.rules[17].opcodes[4] = { type: 7, string: [46] };// TLS - this.rules[17].opcodes[5] = { type: 7, string: [45] };// TLS - this.rules[17].opcodes[6] = { type: 7, string: [95] };// TLS - - /* source-descriptions-name */ + this.rules[17].opcodes[0] = { type: 4, index: 36 };// RNM(identifier-strict) + + /* source-reference-id */ this.rules[18].opcodes = []; - this.rules[18].opcodes[0] = { type: 2, children: [1,2,3] };// CAT - this.rules[18].opcodes[1] = { type: 4, index: 19 };// RNM(source-descriptions-source-name) - this.rules[18].opcodes[2] = { type: 7, string: [46] };// TLS - this.rules[18].opcodes[3] = { type: 4, index: 20 };// RNM(source-descriptions-reference) + this.rules[18].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP + this.rules[18].opcodes[1] = { type: 4, index: 32 };// RNM(CHAR) - /* source-descriptions-source-name */ + /* components-reference */ this.rules[19].opcodes = []; - this.rules[19].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP - this.rules[19].opcodes[1] = { type: 1, children: [2,3,4,5] };// ALT - this.rules[19].opcodes[2] = { type: 4, index: 35 };// RNM(ALPHA) - this.rules[19].opcodes[3] = { type: 4, index: 34 };// RNM(DIGIT) - this.rules[19].opcodes[4] = { type: 7, string: [95] };// TLS - this.rules[19].opcodes[5] = { type: 7, string: [45] };// TLS - - /* source-descriptions-reference */ + this.rules[19].opcodes[0] = { type: 2, children: [1,2,3] };// CAT + this.rules[19].opcodes[1] = { type: 4, index: 20 };// RNM(component-type) + this.rules[19].opcodes[2] = { type: 7, string: [46] };// TLS + this.rules[19].opcodes[3] = { type: 4, index: 21 };// RNM(component-name) + + /* component-type */ this.rules[20].opcodes = []; - this.rules[20].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP - this.rules[20].opcodes[1] = { type: 4, index: 30 };// RNM(CHAR) + this.rules[20].opcodes[0] = { type: 1, children: [1,2,3] };// ALT + this.rules[20].opcodes[1] = { type: 7, string: [112,97,114,97,109,101,116,101,114,115] };// TLS + this.rules[20].opcodes[2] = { type: 7, string: [115,117,99,99,101,115,115,97,99,116,105,111,110,115] };// TLS + this.rules[20].opcodes[3] = { type: 7, string: [102,97,105,108,117,114,101,97,99,116,105,111,110,115] };// TLS - /* components-name */ + /* component-name */ this.rules[21].opcodes = []; - this.rules[21].opcodes[0] = { type: 2, children: [1,2,3] };// CAT - this.rules[21].opcodes[1] = { type: 4, index: 22 };// RNM(components-field) - this.rules[21].opcodes[2] = { type: 7, string: [46] };// TLS - this.rules[21].opcodes[3] = { type: 4, index: 23 };// RNM(components-sub-field) + this.rules[21].opcodes[0] = { type: 4, index: 35 };// RNM(identifier) - /* components-field */ + /* name */ this.rules[22].opcodes = []; - this.rules[22].opcodes[0] = { type: 1, children: [1,2,3] };// ALT - this.rules[22].opcodes[1] = { type: 7, string: [112,97,114,97,109,101,116,101,114,115] };// TLS - this.rules[22].opcodes[2] = { type: 7, string: [115,117,99,99,101,115,115,97,99,116,105,111,110,115] };// TLS - this.rules[22].opcodes[3] = { type: 7, string: [102,97,105,108,117,114,101,97,99,116,105,111,110,115] };// TLS + this.rules[22].opcodes[0] = { type: 3, min: 0, max: Infinity };// REP + this.rules[22].opcodes[1] = { type: 4, index: 32 };// RNM(CHAR) - /* components-sub-field */ + /* expression-string */ this.rules[23].opcodes = []; - this.rules[23].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP - this.rules[23].opcodes[1] = { type: 1, children: [2,3,4,5,6] };// ALT - this.rules[23].opcodes[2] = { type: 4, index: 35 };// RNM(ALPHA) - this.rules[23].opcodes[3] = { type: 4, index: 34 };// RNM(DIGIT) - this.rules[23].opcodes[4] = { type: 7, string: [46] };// TLS - this.rules[23].opcodes[5] = { type: 7, string: [45] };// TLS - this.rules[23].opcodes[6] = { type: 7, string: [95] };// TLS + this.rules[23].opcodes[0] = { type: 3, min: 0, max: Infinity };// REP + this.rules[23].opcodes[1] = { type: 1, children: [2,3] };// ALT + this.rules[23].opcodes[2] = { type: 4, index: 25 };// RNM(literal-char) + this.rules[23].opcodes[3] = { type: 4, index: 24 };// RNM(embedded-expression) - /* json-pointer */ + /* embedded-expression */ this.rules[24].opcodes = []; - this.rules[24].opcodes[0] = { type: 3, min: 0, max: Infinity };// REP - this.rules[24].opcodes[1] = { type: 2, children: [2,3] };// CAT - this.rules[24].opcodes[2] = { type: 7, string: [47] };// TLS - this.rules[24].opcodes[3] = { type: 4, index: 25 };// RNM(reference-token) + this.rules[24].opcodes[0] = { type: 2, children: [1,2,3] };// CAT + this.rules[24].opcodes[1] = { type: 7, string: [123] };// TLS + this.rules[24].opcodes[2] = { type: 4, index: 0 };// RNM(expression) + this.rules[24].opcodes[3] = { type: 7, string: [125] };// TLS - /* reference-token */ + /* literal-char */ this.rules[25].opcodes = []; - this.rules[25].opcodes[0] = { type: 3, min: 0, max: Infinity };// REP - this.rules[25].opcodes[1] = { type: 1, children: [2,3] };// ALT - this.rules[25].opcodes[2] = { type: 4, index: 26 };// RNM(unescaped) - this.rules[25].opcodes[3] = { type: 4, index: 27 };// RNM(escaped) + this.rules[25].opcodes[0] = { type: 1, children: [1,2,3] };// ALT + this.rules[25].opcodes[1] = { type: 5, min: 0, max: 122 };// TRG + this.rules[25].opcodes[2] = { type: 6, string: [124] };// TBS + this.rules[25].opcodes[3] = { type: 5, min: 126, max: 1114111 };// TRG - /* unescaped */ + /* json-pointer */ this.rules[26].opcodes = []; - this.rules[26].opcodes[0] = { type: 1, children: [1,2,3] };// ALT - this.rules[26].opcodes[1] = { type: 5, min: 0, max: 46 };// TRG - this.rules[26].opcodes[2] = { type: 5, min: 48, max: 125 };// TRG - this.rules[26].opcodes[3] = { type: 5, min: 127, max: 1114111 };// TRG + this.rules[26].opcodes[0] = { type: 3, min: 0, max: Infinity };// REP + this.rules[26].opcodes[1] = { type: 2, children: [2,3] };// CAT + this.rules[26].opcodes[2] = { type: 7, string: [47] };// TLS + this.rules[26].opcodes[3] = { type: 4, index: 27 };// RNM(reference-token) - /* escaped */ + /* reference-token */ this.rules[27].opcodes = []; - this.rules[27].opcodes[0] = { type: 2, children: [1,2] };// CAT - this.rules[27].opcodes[1] = { type: 7, string: [126] };// TLS - this.rules[27].opcodes[2] = { type: 1, children: [3,4] };// ALT - this.rules[27].opcodes[3] = { type: 7, string: [48] };// TLS - this.rules[27].opcodes[4] = { type: 7, string: [49] };// TLS + this.rules[27].opcodes[0] = { type: 3, min: 0, max: Infinity };// REP + this.rules[27].opcodes[1] = { type: 1, children: [2,3] };// ALT + this.rules[27].opcodes[2] = { type: 4, index: 28 };// RNM(unescaped) + this.rules[27].opcodes[3] = { type: 4, index: 29 };// RNM(escaped) - /* token */ + /* unescaped */ this.rules[28].opcodes = []; - this.rules[28].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP - this.rules[28].opcodes[1] = { type: 4, index: 29 };// RNM(tchar) + this.rules[28].opcodes[0] = { type: 1, children: [1,2,3,4] };// ALT + this.rules[28].opcodes[1] = { type: 5, min: 0, max: 46 };// TRG + this.rules[28].opcodes[2] = { type: 5, min: 48, max: 122 };// TRG + this.rules[28].opcodes[3] = { type: 6, string: [124] };// TBS + this.rules[28].opcodes[4] = { type: 5, min: 127, max: 1114111 };// TRG - /* tchar */ + /* escaped */ this.rules[29].opcodes = []; - this.rules[29].opcodes[0] = { type: 1, children: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17] };// ALT - this.rules[29].opcodes[1] = { type: 7, string: [33] };// TLS - this.rules[29].opcodes[2] = { type: 7, string: [35] };// TLS - this.rules[29].opcodes[3] = { type: 7, string: [36] };// TLS - this.rules[29].opcodes[4] = { type: 7, string: [37] };// TLS - this.rules[29].opcodes[5] = { type: 7, string: [38] };// TLS - this.rules[29].opcodes[6] = { type: 7, string: [39] };// TLS - this.rules[29].opcodes[7] = { type: 7, string: [42] };// TLS - this.rules[29].opcodes[8] = { type: 7, string: [43] };// TLS - this.rules[29].opcodes[9] = { type: 7, string: [45] };// TLS - this.rules[29].opcodes[10] = { type: 7, string: [46] };// TLS - this.rules[29].opcodes[11] = { type: 7, string: [94] };// TLS - this.rules[29].opcodes[12] = { type: 7, string: [95] };// TLS - this.rules[29].opcodes[13] = { type: 7, string: [96] };// TLS - this.rules[29].opcodes[14] = { type: 7, string: [124] };// TLS - this.rules[29].opcodes[15] = { type: 7, string: [126] };// TLS - this.rules[29].opcodes[16] = { type: 4, index: 34 };// RNM(DIGIT) - this.rules[29].opcodes[17] = { type: 4, index: 35 };// RNM(ALPHA) + this.rules[29].opcodes[0] = { type: 2, children: [1,2] };// CAT + this.rules[29].opcodes[1] = { type: 7, string: [126] };// TLS + this.rules[29].opcodes[2] = { type: 1, children: [3,4] };// ALT + this.rules[29].opcodes[3] = { type: 7, string: [48] };// TLS + this.rules[29].opcodes[4] = { type: 7, string: [49] };// TLS - /* CHAR */ + /* token */ this.rules[30].opcodes = []; - this.rules[30].opcodes[0] = { type: 1, children: [1,2] };// ALT - this.rules[30].opcodes[1] = { type: 4, index: 32 };// RNM(unescape) - this.rules[30].opcodes[2] = { type: 2, children: [3,4] };// CAT - this.rules[30].opcodes[3] = { type: 4, index: 31 };// RNM(escape) - this.rules[30].opcodes[4] = { type: 1, children: [5,6,7,8,9,10,11,12,13] };// ALT - this.rules[30].opcodes[5] = { type: 6, string: [34] };// TBS - this.rules[30].opcodes[6] = { type: 6, string: [92] };// TBS - this.rules[30].opcodes[7] = { type: 6, string: [47] };// TBS - this.rules[30].opcodes[8] = { type: 6, string: [98] };// TBS - this.rules[30].opcodes[9] = { type: 6, string: [102] };// TBS - this.rules[30].opcodes[10] = { type: 6, string: [110] };// TBS - this.rules[30].opcodes[11] = { type: 6, string: [114] };// TBS - this.rules[30].opcodes[12] = { type: 6, string: [116] };// TBS - this.rules[30].opcodes[13] = { type: 2, children: [14,15] };// CAT - this.rules[30].opcodes[14] = { type: 6, string: [117] };// TBS - this.rules[30].opcodes[15] = { type: 3, min: 4, max: 4 };// REP - this.rules[30].opcodes[16] = { type: 4, index: 33 };// RNM(HEXDIG) + this.rules[30].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP + this.rules[30].opcodes[1] = { type: 4, index: 31 };// RNM(tchar) - /* escape */ + /* tchar */ this.rules[31].opcodes = []; - this.rules[31].opcodes[0] = { type: 6, string: [92] };// TBS + this.rules[31].opcodes[0] = { type: 1, children: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17] };// ALT + this.rules[31].opcodes[1] = { type: 7, string: [33] };// TLS + this.rules[31].opcodes[2] = { type: 7, string: [35] };// TLS + this.rules[31].opcodes[3] = { type: 7, string: [36] };// TLS + this.rules[31].opcodes[4] = { type: 7, string: [37] };// TLS + this.rules[31].opcodes[5] = { type: 7, string: [38] };// TLS + this.rules[31].opcodes[6] = { type: 7, string: [39] };// TLS + this.rules[31].opcodes[7] = { type: 7, string: [42] };// TLS + this.rules[31].opcodes[8] = { type: 7, string: [43] };// TLS + this.rules[31].opcodes[9] = { type: 7, string: [45] };// TLS + this.rules[31].opcodes[10] = { type: 7, string: [46] };// TLS + this.rules[31].opcodes[11] = { type: 7, string: [94] };// TLS + this.rules[31].opcodes[12] = { type: 7, string: [95] };// TLS + this.rules[31].opcodes[13] = { type: 7, string: [96] };// TLS + this.rules[31].opcodes[14] = { type: 7, string: [124] };// TLS + this.rules[31].opcodes[15] = { type: 7, string: [126] };// TLS + this.rules[31].opcodes[16] = { type: 4, index: 38 };// RNM(DIGIT) + this.rules[31].opcodes[17] = { type: 4, index: 39 };// RNM(ALPHA) - /* unescape */ + /* CHAR */ this.rules[32].opcodes = []; - this.rules[32].opcodes[0] = { type: 1, children: [1,2,3,4,5] };// ALT - this.rules[32].opcodes[1] = { type: 5, min: 32, max: 33 };// TRG - this.rules[32].opcodes[2] = { type: 5, min: 35, max: 91 };// TRG - this.rules[32].opcodes[3] = { type: 5, min: 93, max: 122 };// TRG - this.rules[32].opcodes[4] = { type: 6, string: [124] };// TBS - this.rules[32].opcodes[5] = { type: 5, min: 126, max: 1114111 };// TRG + this.rules[32].opcodes[0] = { type: 1, children: [1,2] };// ALT + this.rules[32].opcodes[1] = { type: 4, index: 34 };// RNM(unescape) + this.rules[32].opcodes[2] = { type: 2, children: [3,4] };// CAT + this.rules[32].opcodes[3] = { type: 4, index: 33 };// RNM(escape) + this.rules[32].opcodes[4] = { type: 1, children: [5,6,7,8,9,10,11,12,13] };// ALT + this.rules[32].opcodes[5] = { type: 6, string: [34] };// TBS + this.rules[32].opcodes[6] = { type: 6, string: [92] };// TBS + this.rules[32].opcodes[7] = { type: 6, string: [47] };// TBS + this.rules[32].opcodes[8] = { type: 6, string: [98] };// TBS + this.rules[32].opcodes[9] = { type: 6, string: [102] };// TBS + this.rules[32].opcodes[10] = { type: 6, string: [110] };// TBS + this.rules[32].opcodes[11] = { type: 6, string: [114] };// TBS + this.rules[32].opcodes[12] = { type: 6, string: [116] };// TBS + this.rules[32].opcodes[13] = { type: 2, children: [14,15] };// CAT + this.rules[32].opcodes[14] = { type: 6, string: [117] };// TBS + this.rules[32].opcodes[15] = { type: 3, min: 4, max: 4 };// REP + this.rules[32].opcodes[16] = { type: 4, index: 37 };// RNM(HEXDIG) - /* HEXDIG */ + /* escape */ this.rules[33].opcodes = []; - this.rules[33].opcodes[0] = { type: 1, children: [1,2,3,4,5,6,7] };// ALT - this.rules[33].opcodes[1] = { type: 4, index: 34 };// RNM(DIGIT) - this.rules[33].opcodes[2] = { type: 7, string: [97] };// TLS - this.rules[33].opcodes[3] = { type: 7, string: [98] };// TLS - this.rules[33].opcodes[4] = { type: 7, string: [99] };// TLS - this.rules[33].opcodes[5] = { type: 7, string: [100] };// TLS - this.rules[33].opcodes[6] = { type: 7, string: [101] };// TLS - this.rules[33].opcodes[7] = { type: 7, string: [102] };// TLS + this.rules[33].opcodes[0] = { type: 6, string: [92] };// TBS - /* DIGIT */ + /* unescape */ this.rules[34].opcodes = []; - this.rules[34].opcodes[0] = { type: 5, min: 48, max: 57 };// TRG + this.rules[34].opcodes[0] = { type: 1, children: [1,2,3,4,5] };// ALT + this.rules[34].opcodes[1] = { type: 5, min: 32, max: 33 };// TRG + this.rules[34].opcodes[2] = { type: 5, min: 35, max: 91 };// TRG + this.rules[34].opcodes[3] = { type: 5, min: 93, max: 122 };// TRG + this.rules[34].opcodes[4] = { type: 6, string: [124] };// TBS + this.rules[34].opcodes[5] = { type: 5, min: 126, max: 1114111 };// TRG + + /* identifier */ + this.rules[35].opcodes = []; + this.rules[35].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP + this.rules[35].opcodes[1] = { type: 1, children: [2,3,4,5,6] };// ALT + this.rules[35].opcodes[2] = { type: 4, index: 39 };// RNM(ALPHA) + this.rules[35].opcodes[3] = { type: 4, index: 38 };// RNM(DIGIT) + this.rules[35].opcodes[4] = { type: 7, string: [46] };// TLS + this.rules[35].opcodes[5] = { type: 7, string: [45] };// TLS + this.rules[35].opcodes[6] = { type: 7, string: [95] };// TLS + + /* identifier-strict */ + this.rules[36].opcodes = []; + this.rules[36].opcodes[0] = { type: 3, min: 1, max: Infinity };// REP + this.rules[36].opcodes[1] = { type: 1, children: [2,3,4,5] };// ALT + this.rules[36].opcodes[2] = { type: 4, index: 39 };// RNM(ALPHA) + this.rules[36].opcodes[3] = { type: 4, index: 38 };// RNM(DIGIT) + this.rules[36].opcodes[4] = { type: 7, string: [95] };// TLS + this.rules[36].opcodes[5] = { type: 7, string: [45] };// TLS + + /* HEXDIG */ + this.rules[37].opcodes = []; + this.rules[37].opcodes[0] = { type: 1, children: [1,2,3,4,5,6,7] };// ALT + this.rules[37].opcodes[1] = { type: 4, index: 38 };// RNM(DIGIT) + this.rules[37].opcodes[2] = { type: 7, string: [97] };// TLS + this.rules[37].opcodes[3] = { type: 7, string: [98] };// TLS + this.rules[37].opcodes[4] = { type: 7, string: [99] };// TLS + this.rules[37].opcodes[5] = { type: 7, string: [100] };// TLS + this.rules[37].opcodes[6] = { type: 7, string: [101] };// TLS + this.rules[37].opcodes[7] = { type: 7, string: [102] };// TLS + + /* DIGIT */ + this.rules[38].opcodes = []; + this.rules[38].opcodes[0] = { type: 5, min: 48, max: 57 };// TRG /* ALPHA */ - this.rules[35].opcodes = []; - this.rules[35].opcodes[0] = { type: 1, children: [1,2] };// ALT - this.rules[35].opcodes[1] = { type: 5, min: 65, max: 90 };// TRG - this.rules[35].opcodes[2] = { type: 5, min: 97, max: 122 };// TRG + this.rules[39].opcodes = []; + this.rules[39].opcodes[0] = { type: 1, children: [1,2] };// ALT + this.rules[39].opcodes[1] = { type: 5, min: 65, max: 90 };// TRG + this.rules[39].opcodes[2] = { type: 5, min: 97, max: 122 };// TRG // The `toString()` function will display the original grammar file(s) that produced these opcodes. this.toString = function toString(){ let str = ""; str += "; Arazzo runtime expression ABNF syntax\n"; - str += "expression = ( \"$url\" / \"$method\" / \"$statusCode\" / \"$request.\" source / \"$response.\" source / \"$inputs.\" name / \"$outputs.\" name / \"$steps.\" name / \"$workflows.\" name / \"$sourceDescriptions.\" name / \"$components.\" name )\n"; - str += "source = ( header-reference / query-reference / path-reference / body-reference )\n"; - str += "header-reference = \"header.\" token\n"; - str += "query-reference = \"query.\" name\n"; - str += "path-reference = \"path.\" name\n"; - str += "body-reference = \"body\" [\"#\" json-pointer ]\n"; - str += "name = *( CHAR )\n"; + str += "expression = (\n"; + str += " \"$url\" /\n"; + str += " \"$method\" /\n"; + str += " \"$statusCode\" /\n"; + str += " \"$request.\" source /\n"; + str += " \"$response.\" source /\n"; + str += " \"$inputs.\" inputs-reference /\n"; + str += " \"$outputs.\" outputs-reference /\n"; + str += " \"$steps.\" steps-reference /\n"; + str += " \"$workflows.\" workflows-reference /\n"; + str += " \"$sourceDescriptions.\" source-reference /\n"; + str += " \"$components.\" components-reference /\n"; + str += " \"$self\"\n"; + str += " )\n"; + str += "; Request/Response sources\n"; + str += "source = ( header-reference / query-reference / path-reference / body-reference )\n"; + str += "header-reference = \"header.\" token\n"; + str += "query-reference = \"query.\" name\n"; + str += "path-reference = \"path.\" name\n"; + str += "body-reference = \"body\" [\"#\" json-pointer ]\n"; str += "\n"; - str += "; Grammar for parsing template strings with embedded expressions\n"; - str += "expression-string = *( literal-char / embedded-expression )\n"; - str += "embedded-expression = \"{\" expression \"}\"\n"; - str += "literal-char = %x00-7A / %x7C / %x7E-10FFFF ; anything except { (%x7B) and } (%x7D)\n"; + str += "; Input/Output references\n"; + str += "inputs-reference = input-name [\"#\" json-pointer]\n"; + str += "input-name = identifier\n"; + str += "outputs-reference = output-name [\"#\" json-pointer]\n"; + str += "output-name = identifier\n"; + str += "\n"; + str += "; Steps expressions\n"; + str += "steps-reference = step-id \".outputs.\" output-name [\"#\" json-pointer]\n"; + str += "step-id = identifier-strict\n"; str += "\n"; - str += "; Secondary grammar for parsing $steps name part\n"; - str += "; Format: {stepId}.{field}.{subField}[#/{jsonPointer}]\n"; - str += "steps-name = steps-id \".\" steps-field \".\" steps-sub-field [\"#\" json-pointer]\n"; - str += "steps-id = 1*(ALPHA / DIGIT / \"_\" / \"-\")\n"; - str += "steps-field = \"outputs\"\n"; - str += "steps-sub-field = 1*(ALPHA / DIGIT / \".\" / \"-\" / \"_\")\n"; + str += "; Workflows expressions\n"; + str += "workflows-reference = workflow-id \".\" workflow-field \".\" workflow-field-name [\"#\" json-pointer]\n"; + str += "workflow-id = identifier-strict\n"; + str += "workflow-field = \"inputs\" / \"outputs\"\n"; + str += "workflow-field-name = identifier\n"; str += "\n"; - str += "; Secondary grammar for parsing $workflows name part\n"; - str += "; Format: {workflowId}.{field}.{subField}[#/{jsonPointer}]\n"; - str += "workflows-name = workflows-id \".\" workflows-field \".\" workflows-sub-field [\"#\" json-pointer]\n"; - str += "workflows-id = 1*(ALPHA / DIGIT / \"_\" / \"-\")\n"; - str += "workflows-field = \"inputs\" / \"outputs\"\n"; - str += "workflows-sub-field = 1*(ALPHA / DIGIT / \".\" / \"-\" / \"_\")\n"; + str += "; Source descriptions expressions\n"; + str += "source-reference = source-name \".\" source-reference-id\n"; + str += "source-name = identifier-strict\n"; + str += "source-reference-id = 1*CHAR\n"; str += "\n"; - str += "; Secondary grammar for parsing $sourceDescriptions name part\n"; - str += "; Format: {sourceName}.{reference}\n"; - str += "; reference can be operationId (unconstrained) or workflowId (constrained)\n"; - str += "source-descriptions-name = source-descriptions-source-name \".\" source-descriptions-reference\n"; - str += "source-descriptions-source-name = 1*(ALPHA / DIGIT / \"_\" / \"-\")\n"; - str += "source-descriptions-reference = 1*CHAR\n"; + str += "; Components expressions\n"; + str += "components-reference = component-type \".\" component-name\n"; + str += "component-type = \"parameters\" / \"successActions\" / \"failureActions\"\n"; + str += "component-name = identifier\n"; str += "\n"; - str += "; Secondary grammar for parsing $components name part\n"; - str += "; Format: {field}.{subField}\n"; - str += "; Allowed fields: parameters, successActions, failureActions\n"; - str += "components-name = components-field \".\" components-sub-field\n"; - str += "components-field = \"parameters\" / \"successActions\" / \"failureActions\"\n"; - str += "components-sub-field = 1*(ALPHA / DIGIT / \".\" / \"-\" / \"_\")\n"; + str += "; Unconstrained name rule for query/path references\n"; + str += "name = *( CHAR )\n"; str += "\n"; - str += "; https://datatracker.ietf.org/doc/html/rfc6901#section-3\n"; + str += "; Grammar for parsing template strings with embedded expressions\n"; + str += "expression-string = *( literal-char / embedded-expression )\n"; + str += "embedded-expression = \"{\" expression \"}\"\n"; + str += "literal-char = %x00-7A / %x7C / %x7E-10FFFF ; anything except { (%x7B) and } (%x7D)\n"; + str += "\n"; + str += "; JSON Pointer (RFC 6901, adapted)\n"; + str += "; { (%x7B) and } (%x7D) are excluded from 'unescaped' for unambiguous embedded expression parsing\n"; str += "json-pointer = *( \"/\" reference-token )\n"; str += "reference-token = *( unescaped / escaped )\n"; - str += "unescaped = %x00-2E / %x30-7D / %x7F-10FFFF\n"; - str += " ; %x2F ('/') and %x7E ('~') are excluded from 'unescaped'\n"; + str += "unescaped = %x00-2E / %x30-7A / %x7C / %x7F-10FFFF\n"; + str += " ; %x2F ('/'), %x7E ('~'), %x7B ('{'), %x7D ('}') are excluded\n"; str += "escaped = \"~\" ( \"0\" / \"1\" )\n"; str += " ; representing '~' and '/', respectively\n"; str += "\n"; @@ -464,6 +487,12 @@ export default function grammar(){ str += "unescape = %x20-21 / %x23-5B / %x5D-7A / %x7C / %x7E-10FFFF\n"; str += " ; %x7B ('{') and %x7D ('}') are excluded from 'unescape'\n"; str += "\n"; + str += "; Identifier rules\n"; + str += "identifier = 1*(ALPHA / DIGIT / \".\" / \"-\" / \"_\")\n"; + str += " ; Alphanumeric with dots, hyphens, underscores\n"; + str += "identifier-strict = 1*(ALPHA / DIGIT / \"_\" / \"-\")\n"; + str += " ; Alphanumeric with hyphens, underscores (no dots)\n"; + str += "\n"; str += "; https://datatracker.ietf.org/doc/html/rfc5234#appendix-B.1\n"; str += "HEXDIG = DIGIT / \"A\" / \"B\" / \"C\" / \"D\" / \"E\" / \"F\"\n"; str += "DIGIT = %x30-39 ; 0-9\n"; diff --git a/src/parse/translators/ASTTranslator/transformers.js b/src/parse/translators/ASTTranslator/transformers.js index 9134796..879e052 100644 --- a/src/parse/translators/ASTTranslator/transformers.js +++ b/src/parse/translators/ASTTranslator/transformers.js @@ -1,50 +1,3 @@ -import { Parser } from 'apg-lite'; - -import Grammar from '../../../grammar.js'; -import CSTTranslator from '../CSTTranslator.js'; - -const grammar = new Grammar(); - -const parseStepsName = (stepsName) => { - const parser = new Parser(); - parser.ast = new CSTTranslator(); - const result = parser.parse(grammar, 'steps-name', stepsName); - if (!result.success) { - throw new Error(`Invalid steps expression name: ${stepsName}`); - } - return parser.ast.getTree(); -}; - -const parseWorkflowsName = (workflowsName) => { - const parser = new Parser(); - parser.ast = new CSTTranslator(); - const result = parser.parse(grammar, 'workflows-name', workflowsName); - if (!result.success) { - throw new Error(`Invalid workflows expression name: ${workflowsName}`); - } - return parser.ast.getTree(); -}; - -const parseSourceDescriptionsName = (sourceDescriptionsName) => { - const parser = new Parser(); - parser.ast = new CSTTranslator(); - const result = parser.parse(grammar, 'source-descriptions-name', sourceDescriptionsName); - if (!result.success) { - throw new Error(`Invalid source descriptions expression name: ${sourceDescriptionsName}`); - } - return parser.ast.getTree(); -}; - -const parseComponentsName = (componentsName) => { - const parser = new Parser(); - parser.ast = new CSTTranslator(); - const result = parser.parse(grammar, 'components-name', componentsName); - if (!result.success) { - throw new Error(`Invalid components expression name: ${componentsName}`); - } - return parser.ast.getTree(); -}; - export const transformCSTtoAST = (node, transformerMap) => { const transformer = transformerMap[node.type]; if (!transformer) { @@ -61,6 +14,7 @@ const transformers = { if (text === '$url') return { type: 'UrlExpression' }; if (text === '$method') return { type: 'MethodExpression' }; if (text === '$statusCode') return { type: 'StatusCodeExpression' }; + if (text === '$self') return { type: 'SelfExpression' }; // Source expressions (request/response) if (text.startsWith('$request.')) { @@ -78,29 +32,30 @@ const transformers = { }; } - // Named expressions (raw name) - const nameNode = node.children.find((c) => c.type === 'name'); + // Reference expressions if (text.startsWith('$inputs.')) { - return { type: 'InputsExpression', name: nameNode.text }; + const refNode = node.children.find((c) => c.type === 'inputs-reference'); + return transformCSTtoAST(refNode, transformers); } if (text.startsWith('$outputs.')) { - return { type: 'OutputsExpression', name: nameNode.text }; + const refNode = node.children.find((c) => c.type === 'outputs-reference'); + return transformCSTtoAST(refNode, transformers); } if (text.startsWith('$steps.')) { - const stepsNameCST = parseStepsName(nameNode.text); - return transformCSTtoAST(stepsNameCST, transformers); + const refNode = node.children.find((c) => c.type === 'steps-reference'); + return transformCSTtoAST(refNode, transformers); } if (text.startsWith('$workflows.')) { - const workflowsNameCST = parseWorkflowsName(nameNode.text); - return transformCSTtoAST(workflowsNameCST, transformers); + const refNode = node.children.find((c) => c.type === 'workflows-reference'); + return transformCSTtoAST(refNode, transformers); } if (text.startsWith('$sourceDescriptions.')) { - const sourceDescriptionsNameCST = parseSourceDescriptionsName(nameNode.text); - return transformCSTtoAST(sourceDescriptionsNameCST, transformers); + const refNode = node.children.find((c) => c.type === 'source-reference'); + return transformCSTtoAST(refNode, transformers); } if (text.startsWith('$components.')) { - const componentsNameCST = parseComponentsName(nameNode.text); - return transformCSTtoAST(componentsNameCST, transformers); + const refNode = node.children.find((c) => c.type === 'components-reference'); + return transformCSTtoAST(refNode, transformers); } }, @@ -153,18 +108,48 @@ const transformers = { return { type: 'ReferenceToken', value: node.text }; }, - // steps-name secondary grammar transformers - ['steps-name'](node) { - const stepIdNode = node.children.find((c) => c.type === 'steps-id'); - const fieldNode = node.children.find((c) => c.type === 'steps-field'); - const subFieldNode = node.children.find((c) => c.type === 'steps-sub-field'); + ['inputs-reference'](node) { + const inputNameNode = node.children.find((c) => c.type === 'input-name'); + const jsonPointerNode = node.children.find((c) => c.type === 'json-pointer'); + + const result = { + type: 'InputsExpression', + name: inputNameNode.text, + }; + + if (jsonPointerNode) { + result.jsonPointer = transformCSTtoAST(jsonPointerNode, transformers); + } + + return result; + }, + + ['outputs-reference'](node) { + const outputNameNode = node.children.find((c) => c.type === 'output-name'); + const jsonPointerNode = node.children.find((c) => c.type === 'json-pointer'); + + const result = { + type: 'OutputsExpression', + name: outputNameNode.text, + }; + + if (jsonPointerNode) { + result.jsonPointer = transformCSTtoAST(jsonPointerNode, transformers); + } + + return result; + }, + + ['steps-reference'](node) { + const stepIdNode = node.children.find((c) => c.type === 'step-id'); + const outputNameNode = node.children.find((c) => c.type === 'output-name'); const jsonPointerNode = node.children.find((c) => c.type === 'json-pointer'); const result = { type: 'StepsExpression', stepId: stepIdNode.text, - field: fieldNode.text, - outputName: subFieldNode.text, + field: 'outputs', + outputName: outputNameNode.text, }; if (jsonPointerNode) { @@ -174,18 +159,17 @@ const transformers = { return result; }, - // workflows-name secondary grammar transformers - ['workflows-name'](node) { - const workflowIdNode = node.children.find((c) => c.type === 'workflows-id'); - const fieldNode = node.children.find((c) => c.type === 'workflows-field'); - const subFieldNode = node.children.find((c) => c.type === 'workflows-sub-field'); + ['workflows-reference'](node) { + const workflowIdNode = node.children.find((c) => c.type === 'workflow-id'); + const fieldNode = node.children.find((c) => c.type === 'workflow-field'); + const fieldNameNode = node.children.find((c) => c.type === 'workflow-field-name'); const jsonPointerNode = node.children.find((c) => c.type === 'json-pointer'); const result = { type: 'WorkflowsExpression', workflowId: workflowIdNode.text, field: fieldNode.text, - subField: subFieldNode.text, + fieldName: fieldNameNode.text, }; if (jsonPointerNode) { @@ -195,10 +179,9 @@ const transformers = { return result; }, - // source-descriptions-name secondary grammar transformers - ['source-descriptions-name'](node) { - const sourceNameNode = node.children.find((c) => c.type === 'source-descriptions-source-name'); - const referenceNode = node.children.find((c) => c.type === 'source-descriptions-reference'); + ['source-reference'](node) { + const sourceNameNode = node.children.find((c) => c.type === 'source-name'); + const referenceNode = node.children.find((c) => c.type === 'source-reference-id'); return { type: 'SourceDescriptionsExpression', @@ -207,15 +190,14 @@ const transformers = { }; }, - // components-name secondary grammar transformers - ['components-name'](node) { - const fieldNode = node.children.find((c) => c.type === 'components-field'); - const subFieldNode = node.children.find((c) => c.type === 'components-sub-field'); + ['components-reference'](node) { + const typeNode = node.children.find((c) => c.type === 'component-type'); + const nameNode = node.children.find((c) => c.type === 'component-name'); return { type: 'ComponentsExpression', - field: fieldNode.text, - subField: subFieldNode.text, + componentType: typeNode.text, + componentName: nameNode.text, }; }, }; diff --git a/src/parse/translators/CSTTranslator.js b/src/parse/translators/CSTTranslator.js index 487a068..a2eda6f 100644 --- a/src/parse/translators/CSTTranslator.js +++ b/src/parse/translators/CSTTranslator.js @@ -19,26 +19,24 @@ class CSTTranslator extends AST { this.callbacks['body-reference'] = cstCallback('body-reference'); this.callbacks['json-pointer'] = cstCallback('json-pointer'); this.callbacks['reference-token'] = cstCallback('reference-token'); + this.callbacks['inputs-reference'] = cstCallback('inputs-reference'); + this.callbacks['input-name'] = cstCallback('input-name'); + this.callbacks['outputs-reference'] = cstCallback('outputs-reference'); + this.callbacks['output-name'] = cstCallback('output-name'); + this.callbacks['steps-reference'] = cstCallback('steps-reference'); + this.callbacks['workflows-reference'] = cstCallback('workflows-reference'); + this.callbacks['workflow-id'] = cstCallback('workflow-id'); + this.callbacks['workflow-field'] = cstCallback('workflow-field'); + this.callbacks['workflow-field-name'] = cstCallback('workflow-field-name'); + this.callbacks['source-reference'] = cstCallback('source-reference'); + this.callbacks['source-name'] = cstCallback('source-name'); + this.callbacks['source-reference-id'] = cstCallback('source-reference-id'); + this.callbacks['components-reference'] = cstCallback('components-reference'); + this.callbacks['component-type'] = cstCallback('component-type'); + this.callbacks['component-name'] = cstCallback('component-name'); this.callbacks['name'] = cstCallback('name'); this.callbacks['token'] = cstCallback('token'); - // steps-name secondary grammar rules - this.callbacks['steps-name'] = cstCallback('steps-name'); - this.callbacks['steps-id'] = cstCallback('steps-id'); - this.callbacks['steps-field'] = cstCallback('steps-field'); - this.callbacks['steps-sub-field'] = cstCallback('steps-sub-field'); - // workflows-name secondary grammar rules - this.callbacks['workflows-name'] = cstCallback('workflows-name'); - this.callbacks['workflows-id'] = cstCallback('workflows-id'); - this.callbacks['workflows-field'] = cstCallback('workflows-field'); - this.callbacks['workflows-sub-field'] = cstCallback('workflows-sub-field'); - // source-descriptions-name secondary grammar rules - this.callbacks['source-descriptions-name'] = cstCallback('source-descriptions-name'); - this.callbacks['source-descriptions-source-name'] = cstCallback('source-descriptions-source-name'); - this.callbacks['source-descriptions-reference'] = cstCallback('source-descriptions-reference'); - // components-name secondary grammar rules - this.callbacks['components-name'] = cstCallback('components-name'); - this.callbacks['components-field'] = cstCallback('components-field'); - this.callbacks['components-sub-field'] = cstCallback('components-sub-field'); + this.callbacks['step-id'] = cstCallback('step-id'); } getTree() { diff --git a/test/extract.js b/test/extract.js index 8ef9f5b..f5c67be 100644 --- a/test/extract.js +++ b/test/extract.js @@ -73,17 +73,13 @@ describe('extract', function () { * Workaround: Use parse() directly on the raw expression (without {} delimiters) * for $request.body and $response.body expressions containing JSON pointers. */ - it('should return empty array for body expressions with JSON pointers (known limitation)', function () { - // RFC 6901 allows } in JSON pointer paths, so extract() cannot determine - // where the expression ends for body-reference expressions - assert.deepEqual(extract('{$request.body#/url}'), []); - assert.deepEqual(extract('{$response.body#/status}'), []); - assert.deepEqual(extract('{$request.body#/user/uuid}'), []); + it('should handle body expressions with JSON pointers', function () { + assert.deepEqual(extract('{$request.body#/url}'), ['$request.body#/url']); + assert.deepEqual(extract('{$response.body#/status}'), ['$response.body#/status']); + assert.deepEqual(extract('{$request.body#/user/uuid}'), ['$request.body#/user/uuid']); }); it('should handle expressions with JSON pointers in name part', function () { - // $steps., $inputs., $outputs., etc. use the `name` rule which excludes } - // so they work correctly even with JSON pointer-like paths const result = extract('pet={$steps.someStepId.outputs.pets#/0/id}'); assert.deepEqual(result, ['$steps.someStepId.outputs.pets#/0/id']); }); diff --git a/test/fixtures/expressions-valid.js b/test/fixtures/expressions-valid.js index aaeda4e..19449d7 100644 --- a/test/fixtures/expressions-valid.js +++ b/test/fixtures/expressions-valid.js @@ -3,6 +3,7 @@ const validExpressions = [ '$url', '$method', '$statusCode', + '$self', // Request expressions - header '$request.header.accept', @@ -40,11 +41,24 @@ const validExpressions = [ '$inputs.username', '$inputs.password', '$inputs.apiKey', + '$inputs.a', + '$inputs.my-input', + '$inputs.my_input', + '$inputs.my.nested.name', + '$inputs.customer#/firstName', + '$inputs.customer#/address/city', // Outputs expression '$outputs.result', '$outputs.token', '$outputs.data', + '$outputs.a', + '$outputs.my-output', + '$outputs.my_output', + '$outputs.my.nested.name', + '$outputs.response#/name', + '$outputs.mappedResponse#/data/id', + '$outputs.result#/items/0/name', // Steps expression '$steps.stepId.outputs.value', diff --git a/test/parse/__snapshots__/ast-corpus.js.snap b/test/parse/__snapshots__/ast-corpus.js.snap index 6515d2c..543c219 100644 --- a/test/parse/__snapshots__/ast-corpus.js.snap +++ b/test/parse/__snapshots__/ast-corpus.js.snap @@ -3,32 +3,31 @@ exports[`parse ast-corpus $components.failureActions.logError 1`] = ` { "type": "ComponentsExpression", - "field": "failureActions", - "subField": "logError", + "componentType": "failureActions", + "componentName": "logError", } `; -exports[`parse ast-corpus $components.inputs.username 1`] = ` +exports[`parse ast-corpus $components.parameters.petId 1`] = ` { "type": "ComponentsExpression", - "field": "inputs", - "subField": "username", + "componentType": "parameters", + "componentName": "petId", } `; -exports[`parse ast-corpus $components.parameters.petId 1`] = ` +exports[`parse ast-corpus $components.successActions.notifyUser 1`] = ` { "type": "ComponentsExpression", - "field": "parameters", - "subField": "petId", + "componentType": "successActions", + "componentName": "notifyUser", } `; -exports[`parse ast-corpus $components.successActions.notifyUser 1`] = ` +exports[`parse ast-corpus $inputs.a 1`] = ` { - "type": "ComponentsExpression", - "field": "successActions", - "subField": "notifyUser", + "type": "InputsExpression", + "name": "a", } `; @@ -39,6 +38,65 @@ exports[`parse ast-corpus $inputs.apiKey 1`] = ` } `; +exports[`parse ast-corpus $inputs.customer#/address/city 1`] = ` +{ + "type": "InputsExpression", + "name": "customer", + "jsonPointer": { + "type": "JsonPointer", + "value": "/address/city", + "referenceTokens": [ + { + "type": "ReferenceToken", + "value": "address", + }, + { + "type": "ReferenceToken", + "value": "city", + }, + ], + }, +} +`; + +exports[`parse ast-corpus $inputs.customer#/firstName 1`] = ` +{ + "type": "InputsExpression", + "name": "customer", + "jsonPointer": { + "type": "JsonPointer", + "value": "/firstName", + "referenceTokens": [ + { + "type": "ReferenceToken", + "value": "firstName", + }, + ], + }, +} +`; + +exports[`parse ast-corpus $inputs.my.nested.name 1`] = ` +{ + "type": "InputsExpression", + "name": "my.nested.name", +} +`; + +exports[`parse ast-corpus $inputs.my_input 1`] = ` +{ + "type": "InputsExpression", + "name": "my_input", +} +`; + +exports[`parse ast-corpus $inputs.my-input 1`] = ` +{ + "type": "InputsExpression", + "name": "my-input", +} +`; + exports[`parse ast-corpus $inputs.password 1`] = ` { "type": "InputsExpression", @@ -59,6 +117,13 @@ exports[`parse ast-corpus $method 1`] = ` } `; +exports[`parse ast-corpus $outputs.a 1`] = ` +{ + "type": "OutputsExpression", + "name": "a", +} +`; + exports[`parse ast-corpus $outputs.data 1`] = ` { "type": "OutputsExpression", @@ -66,6 +131,65 @@ exports[`parse ast-corpus $outputs.data 1`] = ` } `; +exports[`parse ast-corpus $outputs.mappedResponse#/data/id 1`] = ` +{ + "type": "OutputsExpression", + "name": "mappedResponse", + "jsonPointer": { + "type": "JsonPointer", + "value": "/data/id", + "referenceTokens": [ + { + "type": "ReferenceToken", + "value": "data", + }, + { + "type": "ReferenceToken", + "value": "id", + }, + ], + }, +} +`; + +exports[`parse ast-corpus $outputs.my.nested.name 1`] = ` +{ + "type": "OutputsExpression", + "name": "my.nested.name", +} +`; + +exports[`parse ast-corpus $outputs.my_output 1`] = ` +{ + "type": "OutputsExpression", + "name": "my_output", +} +`; + +exports[`parse ast-corpus $outputs.my-output 1`] = ` +{ + "type": "OutputsExpression", + "name": "my-output", +} +`; + +exports[`parse ast-corpus $outputs.response#/name 1`] = ` +{ + "type": "OutputsExpression", + "name": "response", + "jsonPointer": { + "type": "JsonPointer", + "value": "/name", + "referenceTokens": [ + { + "type": "ReferenceToken", + "value": "name", + }, + ], + }, +} +`; + exports[`parse ast-corpus $outputs.result 1`] = ` { "type": "OutputsExpression", @@ -73,6 +197,31 @@ exports[`parse ast-corpus $outputs.result 1`] = ` } `; +exports[`parse ast-corpus $outputs.result#/items/0/name 1`] = ` +{ + "type": "OutputsExpression", + "name": "result", + "jsonPointer": { + "type": "JsonPointer", + "value": "/items/0/name", + "referenceTokens": [ + { + "type": "ReferenceToken", + "value": "items", + }, + { + "type": "ReferenceToken", + "value": "0", + }, + { + "type": "ReferenceToken", + "value": "name", + }, + ], + }, +} +`; + exports[`parse ast-corpus $outputs.token 1`] = ` { "type": "OutputsExpression", @@ -433,6 +582,12 @@ exports[`parse ast-corpus $response.header.content-type 1`] = ` } `; +exports[`parse ast-corpus $self 1`] = ` +{ + "type": "SelfExpression", +} +`; + exports[`parse ast-corpus $sourceDescriptions.externalService.url 1`] = ` { "type": "SourceDescriptionsExpression", @@ -543,7 +698,7 @@ exports[`parse ast-corpus $workflows.authWorkflow.outputs.token 1`] = ` "type": "WorkflowsExpression", "workflowId": "authWorkflow", "field": "outputs", - "subField": "token", + "fieldName": "token", } `; @@ -552,7 +707,7 @@ exports[`parse ast-corpus $workflows.authWorkflow.outputs.token#/accessToken 1`] "type": "WorkflowsExpression", "workflowId": "authWorkflow", "field": "outputs", - "subField": "token", + "fieldName": "token", "jsonPointer": { "type": "JsonPointer", "value": "/accessToken", @@ -571,7 +726,7 @@ exports[`parse ast-corpus $workflows.mainProcess.outputs.data 1`] = ` "type": "WorkflowsExpression", "workflowId": "mainProcess", "field": "outputs", - "subField": "data", + "fieldName": "data", } `; @@ -580,7 +735,7 @@ exports[`parse ast-corpus $workflows.myWorkflow.inputs.username 1`] = ` "type": "WorkflowsExpression", "workflowId": "myWorkflow", "field": "inputs", - "subField": "username", + "fieldName": "username", } `; @@ -589,7 +744,7 @@ exports[`parse ast-corpus $workflows.myWorkflow.outputs.result 1`] = ` "type": "WorkflowsExpression", "workflowId": "myWorkflow", "field": "outputs", - "subField": "result", + "fieldName": "result", } `; @@ -598,7 +753,7 @@ exports[`parse ast-corpus $workflows.myWorkflow.outputs.result#/value 1`] = ` "type": "WorkflowsExpression", "workflowId": "myWorkflow", "field": "outputs", - "subField": "result", + "fieldName": "result", "jsonPointer": { "type": "JsonPointer", "value": "/value", diff --git a/test/parse/__snapshots__/cst-corpus.js.snap b/test/parse/__snapshots__/cst-corpus.js.snap index 90e8a68..3dc72e7 100644 --- a/test/parse/__snapshots__/cst-corpus.js.snap +++ b/test/parse/__snapshots__/cst-corpus.js.snap @@ -8,29 +8,26 @@ exports[`parse cst-corpus $components.failureActions.logError 1`] = ` "length": 35, "children": [ { - "type": "name", + "type": "components-reference", "text": "failureActions.logError", "start": 12, "length": 23, - "children": [], - }, - ], -} -`; - -exports[`parse cst-corpus $components.inputs.username 1`] = ` -{ - "type": "expression", - "text": "$components.inputs.username", - "start": 0, - "length": 27, - "children": [ - { - "type": "name", - "text": "inputs.username", - "start": 12, - "length": 15, - "children": [], + "children": [ + { + "type": "component-type", + "text": "failureActions", + "start": 12, + "length": 14, + "children": [], + }, + { + "type": "component-name", + "text": "logError", + "start": 27, + "length": 8, + "children": [], + }, + ], }, ], } @@ -44,11 +41,26 @@ exports[`parse cst-corpus $components.parameters.petId 1`] = ` "length": 28, "children": [ { - "type": "name", + "type": "components-reference", "text": "parameters.petId", "start": 12, "length": 16, - "children": [], + "children": [ + { + "type": "component-type", + "text": "parameters", + "start": 12, + "length": 10, + "children": [], + }, + { + "type": "component-name", + "text": "petId", + "start": 23, + "length": 5, + "children": [], + }, + ], }, ], } @@ -62,11 +74,52 @@ exports[`parse cst-corpus $components.successActions.notifyUser 1`] = ` "length": 37, "children": [ { - "type": "name", + "type": "components-reference", "text": "successActions.notifyUser", "start": 12, "length": 25, - "children": [], + "children": [ + { + "type": "component-type", + "text": "successActions", + "start": 12, + "length": 14, + "children": [], + }, + { + "type": "component-name", + "text": "notifyUser", + "start": 27, + "length": 10, + "children": [], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $inputs.a 1`] = ` +{ + "type": "expression", + "text": "$inputs.a", + "start": 0, + "length": 9, + "children": [ + { + "type": "inputs-reference", + "text": "a", + "start": 8, + "length": 1, + "children": [ + { + "type": "input-name", + "text": "a", + "start": 8, + "length": 1, + "children": [], + }, + ], }, ], } @@ -80,11 +133,186 @@ exports[`parse cst-corpus $inputs.apiKey 1`] = ` "length": 14, "children": [ { - "type": "name", + "type": "inputs-reference", "text": "apiKey", "start": 8, "length": 6, - "children": [], + "children": [ + { + "type": "input-name", + "text": "apiKey", + "start": 8, + "length": 6, + "children": [], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $inputs.customer#/address/city 1`] = ` +{ + "type": "expression", + "text": "$inputs.customer#/address/city", + "start": 0, + "length": 30, + "children": [ + { + "type": "inputs-reference", + "text": "customer#/address/city", + "start": 8, + "length": 22, + "children": [ + { + "type": "input-name", + "text": "customer", + "start": 8, + "length": 8, + "children": [], + }, + { + "type": "json-pointer", + "text": "/address/city", + "start": 17, + "length": 13, + "children": [ + { + "type": "reference-token", + "text": "address", + "start": 18, + "length": 7, + "children": [], + }, + { + "type": "reference-token", + "text": "city", + "start": 26, + "length": 4, + "children": [], + }, + ], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $inputs.customer#/firstName 1`] = ` +{ + "type": "expression", + "text": "$inputs.customer#/firstName", + "start": 0, + "length": 27, + "children": [ + { + "type": "inputs-reference", + "text": "customer#/firstName", + "start": 8, + "length": 19, + "children": [ + { + "type": "input-name", + "text": "customer", + "start": 8, + "length": 8, + "children": [], + }, + { + "type": "json-pointer", + "text": "/firstName", + "start": 17, + "length": 10, + "children": [ + { + "type": "reference-token", + "text": "firstName", + "start": 18, + "length": 9, + "children": [], + }, + ], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $inputs.my.nested.name 1`] = ` +{ + "type": "expression", + "text": "$inputs.my.nested.name", + "start": 0, + "length": 22, + "children": [ + { + "type": "inputs-reference", + "text": "my.nested.name", + "start": 8, + "length": 14, + "children": [ + { + "type": "input-name", + "text": "my.nested.name", + "start": 8, + "length": 14, + "children": [], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $inputs.my_input 1`] = ` +{ + "type": "expression", + "text": "$inputs.my_input", + "start": 0, + "length": 16, + "children": [ + { + "type": "inputs-reference", + "text": "my_input", + "start": 8, + "length": 8, + "children": [ + { + "type": "input-name", + "text": "my_input", + "start": 8, + "length": 8, + "children": [], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $inputs.my-input 1`] = ` +{ + "type": "expression", + "text": "$inputs.my-input", + "start": 0, + "length": 16, + "children": [ + { + "type": "inputs-reference", + "text": "my-input", + "start": 8, + "length": 8, + "children": [ + { + "type": "input-name", + "text": "my-input", + "start": 8, + "length": 8, + "children": [], + }, + ], }, ], } @@ -98,11 +326,19 @@ exports[`parse cst-corpus $inputs.password 1`] = ` "length": 16, "children": [ { - "type": "name", + "type": "inputs-reference", "text": "password", "start": 8, "length": 8, - "children": [], + "children": [ + { + "type": "input-name", + "text": "password", + "start": 8, + "length": 8, + "children": [], + }, + ], }, ], } @@ -116,57 +352,329 @@ exports[`parse cst-corpus $inputs.username 1`] = ` "length": 16, "children": [ { - "type": "name", + "type": "inputs-reference", "text": "username", "start": 8, "length": 8, - "children": [], + "children": [ + { + "type": "input-name", + "text": "username", + "start": 8, + "length": 8, + "children": [], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $method 1`] = ` +{ + "type": "expression", + "text": "$method", + "start": 0, + "length": 7, + "children": [], +} +`; + +exports[`parse cst-corpus $outputs.a 1`] = ` +{ + "type": "expression", + "text": "$outputs.a", + "start": 0, + "length": 10, + "children": [ + { + "type": "outputs-reference", + "text": "a", + "start": 9, + "length": 1, + "children": [ + { + "type": "output-name", + "text": "a", + "start": 9, + "length": 1, + "children": [], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $outputs.data 1`] = ` +{ + "type": "expression", + "text": "$outputs.data", + "start": 0, + "length": 13, + "children": [ + { + "type": "outputs-reference", + "text": "data", + "start": 9, + "length": 4, + "children": [ + { + "type": "output-name", + "text": "data", + "start": 9, + "length": 4, + "children": [], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $outputs.mappedResponse#/data/id 1`] = ` +{ + "type": "expression", + "text": "$outputs.mappedResponse#/data/id", + "start": 0, + "length": 32, + "children": [ + { + "type": "outputs-reference", + "text": "mappedResponse#/data/id", + "start": 9, + "length": 23, + "children": [ + { + "type": "output-name", + "text": "mappedResponse", + "start": 9, + "length": 14, + "children": [], + }, + { + "type": "json-pointer", + "text": "/data/id", + "start": 24, + "length": 8, + "children": [ + { + "type": "reference-token", + "text": "data", + "start": 25, + "length": 4, + "children": [], + }, + { + "type": "reference-token", + "text": "id", + "start": 30, + "length": 2, + "children": [], + }, + ], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $outputs.my.nested.name 1`] = ` +{ + "type": "expression", + "text": "$outputs.my.nested.name", + "start": 0, + "length": 23, + "children": [ + { + "type": "outputs-reference", + "text": "my.nested.name", + "start": 9, + "length": 14, + "children": [ + { + "type": "output-name", + "text": "my.nested.name", + "start": 9, + "length": 14, + "children": [], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $outputs.my_output 1`] = ` +{ + "type": "expression", + "text": "$outputs.my_output", + "start": 0, + "length": 18, + "children": [ + { + "type": "outputs-reference", + "text": "my_output", + "start": 9, + "length": 9, + "children": [ + { + "type": "output-name", + "text": "my_output", + "start": 9, + "length": 9, + "children": [], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $outputs.my-output 1`] = ` +{ + "type": "expression", + "text": "$outputs.my-output", + "start": 0, + "length": 18, + "children": [ + { + "type": "outputs-reference", + "text": "my-output", + "start": 9, + "length": 9, + "children": [ + { + "type": "output-name", + "text": "my-output", + "start": 9, + "length": 9, + "children": [], + }, + ], + }, + ], +} +`; + +exports[`parse cst-corpus $outputs.response#/name 1`] = ` +{ + "type": "expression", + "text": "$outputs.response#/name", + "start": 0, + "length": 23, + "children": [ + { + "type": "outputs-reference", + "text": "response#/name", + "start": 9, + "length": 14, + "children": [ + { + "type": "output-name", + "text": "response", + "start": 9, + "length": 8, + "children": [], + }, + { + "type": "json-pointer", + "text": "/name", + "start": 18, + "length": 5, + "children": [ + { + "type": "reference-token", + "text": "name", + "start": 19, + "length": 4, + "children": [], + }, + ], + }, + ], }, ], } `; -exports[`parse cst-corpus $method 1`] = ` -{ - "type": "expression", - "text": "$method", - "start": 0, - "length": 7, - "children": [], -} -`; - -exports[`parse cst-corpus $outputs.data 1`] = ` +exports[`parse cst-corpus $outputs.result 1`] = ` { "type": "expression", - "text": "$outputs.data", + "text": "$outputs.result", "start": 0, - "length": 13, + "length": 15, "children": [ { - "type": "name", - "text": "data", + "type": "outputs-reference", + "text": "result", "start": 9, - "length": 4, - "children": [], + "length": 6, + "children": [ + { + "type": "output-name", + "text": "result", + "start": 9, + "length": 6, + "children": [], + }, + ], }, ], } `; -exports[`parse cst-corpus $outputs.result 1`] = ` +exports[`parse cst-corpus $outputs.result#/items/0/name 1`] = ` { "type": "expression", - "text": "$outputs.result", + "text": "$outputs.result#/items/0/name", "start": 0, - "length": 15, + "length": 29, "children": [ { - "type": "name", - "text": "result", + "type": "outputs-reference", + "text": "result#/items/0/name", "start": 9, - "length": 6, - "children": [], + "length": 20, + "children": [ + { + "type": "output-name", + "text": "result", + "start": 9, + "length": 6, + "children": [], + }, + { + "type": "json-pointer", + "text": "/items/0/name", + "start": 16, + "length": 13, + "children": [ + { + "type": "reference-token", + "text": "items", + "start": 17, + "length": 5, + "children": [], + }, + { + "type": "reference-token", + "text": "0", + "start": 23, + "length": 1, + "children": [], + }, + { + "type": "reference-token", + "text": "name", + "start": 25, + "length": 4, + "children": [], + }, + ], + }, + ], }, ], } @@ -180,11 +688,19 @@ exports[`parse cst-corpus $outputs.token 1`] = ` "length": 14, "children": [ { - "type": "name", + "type": "outputs-reference", "text": "token", "start": 9, "length": 5, - "children": [], + "children": [ + { + "type": "output-name", + "text": "token", + "start": 9, + "length": 5, + "children": [], + }, + ], }, ], } @@ -966,6 +1482,16 @@ exports[`parse cst-corpus $response.header.content-type 1`] = ` } `; +exports[`parse cst-corpus $self 1`] = ` +{ + "type": "expression", + "text": "$self", + "start": 0, + "length": 5, + "children": [], +} +`; + exports[`parse cst-corpus $sourceDescriptions.externalService.url 1`] = ` { "type": "expression", @@ -974,11 +1500,26 @@ exports[`parse cst-corpus $sourceDescriptions.externalService.url 1`] = ` "length": 39, "children": [ { - "type": "name", + "type": "source-reference", "text": "externalService.url", "start": 20, "length": 19, - "children": [], + "children": [ + { + "type": "source-name", + "text": "externalService", + "start": 20, + "length": 15, + "children": [], + }, + { + "type": "source-reference-id", + "text": "url", + "start": 36, + "length": 3, + "children": [], + }, + ], }, ], } @@ -992,11 +1533,26 @@ exports[`parse cst-corpus $sourceDescriptions.mainApi.createUser 1`] = ` "length": 38, "children": [ { - "type": "name", + "type": "source-reference", "text": "mainApi.createUser", "start": 20, "length": 18, - "children": [], + "children": [ + { + "type": "source-name", + "text": "mainApi", + "start": 20, + "length": 7, + "children": [], + }, + { + "type": "source-reference-id", + "text": "createUser", + "start": 28, + "length": 10, + "children": [], + }, + ], }, ], } @@ -1010,11 +1566,26 @@ exports[`parse cst-corpus $sourceDescriptions.petStore.getPets 1`] = ` "length": 36, "children": [ { - "type": "name", + "type": "source-reference", "text": "petStore.getPets", "start": 20, "length": 16, - "children": [], + "children": [ + { + "type": "source-name", + "text": "petStore", + "start": 20, + "length": 8, + "children": [], + }, + { + "type": "source-reference-id", + "text": "getPets", + "start": 29, + "length": 7, + "children": [], + }, + ], }, ], } @@ -1038,11 +1609,26 @@ exports[`parse cst-corpus $steps.createUser.outputs.userId 1`] = ` "length": 32, "children": [ { - "type": "name", + "type": "steps-reference", "text": "createUser.outputs.userId", "start": 7, "length": 25, - "children": [], + "children": [ + { + "type": "step-id", + "text": "createUser", + "start": 7, + "length": 10, + "children": [], + }, + { + "type": "output-name", + "text": "userId", + "start": 26, + "length": 6, + "children": [], + }, + ], }, ], } @@ -1056,11 +1642,26 @@ exports[`parse cst-corpus $steps.loginStep.outputs.sessionToken 1`] = ` "length": 37, "children": [ { - "type": "name", + "type": "steps-reference", "text": "loginStep.outputs.sessionToken", "start": 7, "length": 30, - "children": [], + "children": [ + { + "type": "step-id", + "text": "loginStep", + "start": 7, + "length": 9, + "children": [], + }, + { + "type": "output-name", + "text": "sessionToken", + "start": 25, + "length": 12, + "children": [], + }, + ], }, ], } @@ -1074,11 +1675,41 @@ exports[`parse cst-corpus $steps.loginStep.outputs.user#/name 1`] = ` "length": 35, "children": [ { - "type": "name", + "type": "steps-reference", "text": "loginStep.outputs.user#/name", "start": 7, "length": 28, - "children": [], + "children": [ + { + "type": "step-id", + "text": "loginStep", + "start": 7, + "length": 9, + "children": [], + }, + { + "type": "output-name", + "text": "user", + "start": 25, + "length": 4, + "children": [], + }, + { + "type": "json-pointer", + "text": "/name", + "start": 30, + "length": 5, + "children": [ + { + "type": "reference-token", + "text": "name", + "start": 31, + "length": 4, + "children": [], + }, + ], + }, + ], }, ], } @@ -1092,11 +1723,48 @@ exports[`parse cst-corpus $steps.someStepId.outputs.pets#/0/id 1`] = ` "length": 36, "children": [ { - "type": "name", + "type": "steps-reference", "text": "someStepId.outputs.pets#/0/id", "start": 7, "length": 29, - "children": [], + "children": [ + { + "type": "step-id", + "text": "someStepId", + "start": 7, + "length": 10, + "children": [], + }, + { + "type": "output-name", + "text": "pets", + "start": 26, + "length": 4, + "children": [], + }, + { + "type": "json-pointer", + "text": "/0/id", + "start": 31, + "length": 5, + "children": [ + { + "type": "reference-token", + "text": "0", + "start": 32, + "length": 1, + "children": [], + }, + { + "type": "reference-token", + "text": "id", + "start": 34, + "length": 2, + "children": [], + }, + ], + }, + ], }, ], } @@ -1110,11 +1778,26 @@ exports[`parse cst-corpus $steps.stepId.outputs.value 1`] = ` "length": 27, "children": [ { - "type": "name", + "type": "steps-reference", "text": "stepId.outputs.value", "start": 7, "length": 20, - "children": [], + "children": [ + { + "type": "step-id", + "text": "stepId", + "start": 7, + "length": 6, + "children": [], + }, + { + "type": "output-name", + "text": "value", + "start": 22, + "length": 5, + "children": [], + }, + ], }, ], } @@ -1138,11 +1821,33 @@ exports[`parse cst-corpus $workflows.authWorkflow.outputs.token 1`] = ` "length": 37, "children": [ { - "type": "name", + "type": "workflows-reference", "text": "authWorkflow.outputs.token", "start": 11, "length": 26, - "children": [], + "children": [ + { + "type": "workflow-id", + "text": "authWorkflow", + "start": 11, + "length": 12, + "children": [], + }, + { + "type": "workflow-field", + "text": "outputs", + "start": 24, + "length": 7, + "children": [], + }, + { + "type": "workflow-field-name", + "text": "token", + "start": 32, + "length": 5, + "children": [], + }, + ], }, ], } @@ -1156,11 +1861,48 @@ exports[`parse cst-corpus $workflows.authWorkflow.outputs.token#/accessToken 1`] "length": 50, "children": [ { - "type": "name", + "type": "workflows-reference", "text": "authWorkflow.outputs.token#/accessToken", "start": 11, "length": 39, - "children": [], + "children": [ + { + "type": "workflow-id", + "text": "authWorkflow", + "start": 11, + "length": 12, + "children": [], + }, + { + "type": "workflow-field", + "text": "outputs", + "start": 24, + "length": 7, + "children": [], + }, + { + "type": "workflow-field-name", + "text": "token", + "start": 32, + "length": 5, + "children": [], + }, + { + "type": "json-pointer", + "text": "/accessToken", + "start": 38, + "length": 12, + "children": [ + { + "type": "reference-token", + "text": "accessToken", + "start": 39, + "length": 11, + "children": [], + }, + ], + }, + ], }, ], } @@ -1174,11 +1916,33 @@ exports[`parse cst-corpus $workflows.mainProcess.outputs.data 1`] = ` "length": 35, "children": [ { - "type": "name", + "type": "workflows-reference", "text": "mainProcess.outputs.data", "start": 11, "length": 24, - "children": [], + "children": [ + { + "type": "workflow-id", + "text": "mainProcess", + "start": 11, + "length": 11, + "children": [], + }, + { + "type": "workflow-field", + "text": "outputs", + "start": 23, + "length": 7, + "children": [], + }, + { + "type": "workflow-field-name", + "text": "data", + "start": 31, + "length": 4, + "children": [], + }, + ], }, ], } @@ -1192,11 +1956,33 @@ exports[`parse cst-corpus $workflows.myWorkflow.inputs.username 1`] = ` "length": 37, "children": [ { - "type": "name", + "type": "workflows-reference", "text": "myWorkflow.inputs.username", "start": 11, "length": 26, - "children": [], + "children": [ + { + "type": "workflow-id", + "text": "myWorkflow", + "start": 11, + "length": 10, + "children": [], + }, + { + "type": "workflow-field", + "text": "inputs", + "start": 22, + "length": 6, + "children": [], + }, + { + "type": "workflow-field-name", + "text": "username", + "start": 29, + "length": 8, + "children": [], + }, + ], }, ], } @@ -1210,11 +1996,33 @@ exports[`parse cst-corpus $workflows.myWorkflow.outputs.result 1`] = ` "length": 36, "children": [ { - "type": "name", + "type": "workflows-reference", "text": "myWorkflow.outputs.result", "start": 11, "length": 25, - "children": [], + "children": [ + { + "type": "workflow-id", + "text": "myWorkflow", + "start": 11, + "length": 10, + "children": [], + }, + { + "type": "workflow-field", + "text": "outputs", + "start": 22, + "length": 7, + "children": [], + }, + { + "type": "workflow-field-name", + "text": "result", + "start": 30, + "length": 6, + "children": [], + }, + ], }, ], } @@ -1228,11 +2036,48 @@ exports[`parse cst-corpus $workflows.myWorkflow.outputs.result#/value 1`] = ` "length": 43, "children": [ { - "type": "name", + "type": "workflows-reference", "text": "myWorkflow.outputs.result#/value", "start": 11, "length": 32, - "children": [], + "children": [ + { + "type": "workflow-id", + "text": "myWorkflow", + "start": 11, + "length": 10, + "children": [], + }, + { + "type": "workflow-field", + "text": "outputs", + "start": 22, + "length": 7, + "children": [], + }, + { + "type": "workflow-field-name", + "text": "result", + "start": 30, + "length": 6, + "children": [], + }, + { + "type": "json-pointer", + "text": "/value", + "start": 37, + "length": 6, + "children": [ + { + "type": "reference-token", + "text": "value", + "start": 38, + "length": 5, + "children": [], + }, + ], + }, + ], }, ], } diff --git a/test/parse/translators/__snapshots__/CSTTranslator.js.snap b/test/parse/translators/__snapshots__/CSTTranslator.js.snap index 6220854..654dbed 100644 --- a/test/parse/translators/__snapshots__/CSTTranslator.js.snap +++ b/test/parse/translators/__snapshots__/CSTTranslator.js.snap @@ -57,11 +57,26 @@ exports[`parse translators CSTTranslator should translate $steps.login.outputs.t "length": 26, "children": [ { - "type": "name", + "type": "steps-reference", "text": "login.outputs.token", "start": 7, "length": 19, - "children": [], + "children": [ + { + "type": "step-id", + "text": "login", + "start": 7, + "length": 5, + "children": [], + }, + { + "type": "output-name", + "text": "token", + "start": 21, + "length": 5, + "children": [], + }, + ], }, ], } diff --git a/test/test.js b/test/test.js index 437914b..37220c6 100644 --- a/test/test.js +++ b/test/test.js @@ -50,11 +50,17 @@ describe('test', function () { assert.isFalse(test('$inputs.{foo}')); }); - it('should accept { and } in JSON pointer paths (RFC 6901 compliant)', function () { - // RFC 6901 allows { and } in JSON pointer paths (unescaped rule) - // This is standards-compliant behavior - assert.isTrue(test('$request.body#/foo}')); - assert.isTrue(test('$request.body#/foo{')); - assert.isTrue(test('$request.body#/{foo}')); + it('should accept empty name in query and path references', function () { + // name = *( CHAR ) allows zero characters, matching the OpenAPI spec + assert.isTrue(test('$request.query.')); + assert.isTrue(test('$request.path.')); + }); + + it('should reject { and } in JSON pointer paths', function () { + // { and } are excluded from json-pointer grammar to allow unambiguous + // parsing of embedded expressions like {$request.body#/status} + assert.isFalse(test('$request.body#/foo}')); + assert.isFalse(test('$request.body#/foo{')); + assert.isFalse(test('$request.body#/{foo}')); }); }); diff --git a/types/index.d.ts b/types/index.d.ts index 37599f3..062cc0b 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -63,6 +63,14 @@ export interface StatusCodeExpression { readonly type: 'StatusCodeExpression'; } +/** + * $self - The canonical URI of the current Arazzo Description + * https://spec.openapis.org/arazzo/latest.html#runtime-expressions + */ +export interface SelfExpression { + readonly type: 'SelfExpression'; +} + /** * JSON Pointer reference token * https://datatracker.ietf.org/doc/html/rfc6901#section-3 @@ -153,6 +161,7 @@ export interface ResponseExpression { export interface InputsExpression { readonly type: 'InputsExpression'; readonly name: string; + readonly jsonPointer?: JsonPointer; } /** @@ -162,6 +171,7 @@ export interface InputsExpression { export interface OutputsExpression { readonly type: 'OutputsExpression'; readonly name: string; + readonly jsonPointer?: JsonPointer; } /** @@ -177,14 +187,14 @@ export interface StepsExpression { } /** - * $workflows.{workflowId}.{field}.{subField} - Workflows expression + * $workflows.{workflowId}.{field}.{fieldName} - Workflows expression * https://spec.openapis.org/arazzo/latest.html#runtime-expressions */ export interface WorkflowsExpression { readonly type: 'WorkflowsExpression'; readonly workflowId: string; readonly field: 'inputs' | 'outputs'; - readonly subField: string; + readonly fieldName: string; readonly jsonPointer?: JsonPointer; } @@ -199,13 +209,13 @@ export interface SourceDescriptionsExpression { } /** - * $components.{field}.{subField} - Components expression + * $components.{componentType}.{componentName} - Components expression * https://spec.openapis.org/arazzo/latest.html#runtime-expressions */ export interface ComponentsExpression { readonly type: 'ComponentsExpression'; - readonly field: 'parameters' | 'successActions' | 'failureActions'; - readonly subField: string; + readonly componentType: 'parameters' | 'successActions' | 'failureActions'; + readonly componentName: string; } /** @@ -215,6 +225,7 @@ export type ASTNode = | UrlExpression | MethodExpression | StatusCodeExpression + | SelfExpression | RequestExpression | ResponseExpression | InputsExpression