From 48134edaef1b40eb37757e2d41dde07f1369bce6 Mon Sep 17 00:00:00 2001 From: ddwightx Date: Sun, 17 Apr 2022 02:02:19 -0400 Subject: [PATCH] Added Pattern Fragment matching. Removed pattern merging. Added option to ignore whitespace in EBNF conversion. --- docs/Matcher/Language-Matcher-Schema.md | 629 ++++- .../LanguageMatcherDefinition.schema.json | 386 ++- src/Matcher.CLI/Matcher.CLI.csproj | 2 +- .../CharacterClassMatchEngine.cs | 750 ++--- src/Matcher.Interop.Ebnf/EbnfConverter.cs | 5 +- src/Matcher.Interop.Ebnf/EbnfMatchEngine.cs | 2491 ++++++++++------- .../Matcher.Interop.Ebnf.csproj | 2 +- .../DefinitionConverter.cs | 14 +- .../Matcher.Interop.Model.csproj | 2 +- .../MatcherActionDefinition.cs | 5 +- .../PatternMatcherDefinition.cs | 2 - src/Matcher/AbstractLanguageMatchEngine.cs | 1 - src/Matcher/Data/Extensions.cs | 2 +- src/Matcher/Data/StringMatchData.cs | 4 - .../Input/Actions/AssertMatcherAction.cs | 22 +- .../Actions/CreateVariableMatcherAction.cs | 15 +- src/Matcher/Input/Actions/MatcherAction.cs | 2 +- .../Actions/UpdateVariableMatcherAction.cs | 2 +- .../Input/Actions/VariableValueSource.cs | 2 +- src/Matcher/Input/FallThroughMode.cs | 8 + src/Matcher/Input/Patterns/PatternMatcher.cs | 2 - src/Matcher/LanguageMatchEngine.cs | 549 ++-- src/Matcher/Matcher.csproj | 2 +- src/Shared/Shared.csproj | 2 +- .../Files/CharacterClassDefinition.json | 1 + .../Files/EagerIndexTestDefinition.json | 92 +- .../MatcherTests/Files/EbnfDefinition.json | 36 +- .../Shared/LanguageMatchEngineFactory.cs | 2 +- .../Tests/Actions/MatcherActionTests.cs | 4 +- .../MatcherTests/Tests/Engine/EngineTests.cs | 541 +++- .../Tests/Interop/DefinitionConverterTests.cs | 15 +- .../Tests/Interop/Ebnf/ConversionTests.cs | 11 +- .../Interop/MatcherActionDefintionComparer.cs | 2 +- .../PatternMatcherDefinitionComparer.cs | 2 - .../Shared/Matcher/LanguageMatcherProvider.cs | 2 +- .../Files/StaxeTestComplexLangDefinition.json | 97 +- .../TestComplexLang/Tests/ScriptTests.cs | 4 +- 37 files changed, 3437 insertions(+), 2273 deletions(-) diff --git a/docs/Matcher/Language-Matcher-Schema.md b/docs/Matcher/Language-Matcher-Schema.md index e17cc71..4824dd0 100644 --- a/docs/Matcher/Language-Matcher-Schema.md +++ b/docs/Matcher/Language-Matcher-Schema.md @@ -1,6 +1,10 @@ # Language Matcher Schema -Define a language grammar +``` +#/languageMatcher +``` + +Language parser grammar | Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Defined In | | ------------------- | ---------- | ------------ | ------------ | ----------------- | --------------------- | ------------------------------------------------------------------------------ | @@ -10,13 +14,435 @@ Define a language grammar | Property | Type | Required | Nullable | Default | Defined by | | ------------------------------------- | ---------- | ------------ | -------- | -------- | ------------------------------ | +| [actions](#actions) | `array` | Optional | No | | Language Matcher (this schema) | | [fragments](#fragments) | `object[]` | **Required** | No | | Language Matcher (this schema) | | [indexingMode](#indexingmode) | `enum` | Optional | No | `"Lazy"` | Language Matcher (this schema) | | [logMatches](#logmatches) | `boolean` | **Required** | No | `false` | Language Matcher (this schema) | | [name](#name) | `string` | **Required** | No | | Language Matcher (this schema) | -| [patterns](#patterns) | `object[]` | **Required** | No | | Language Matcher (this schema) | +| [patterns](#patterns) | `array` | **Required** | No | | Language Matcher (this schema) | | [startingFragment](#startingfragment) | `string` | **Required** | No | | Language Matcher (this schema) | +## actions + +`actions` + +- is optional +- type: `array` +- defined in this schema + +### actions Type + +Array type: `array` + +All items must be of the type: + +**One** of the following _conditions_ need to be fulfilled. + +#### Condition 1 + +`object` with following properties: + +| Property | Type | Required | +| ------------------- | -------------- | ------------ | +| `action` | string | **Required** | +| `firstVariableName` | string | **Required** | +| `name` | string | **Required** | +| `source` | string | **Required** | +| `value` | integer,string | Optional | + +#### action + +Action type + +`action` + +- is **required** +- type: `enum` + +The value of this property **must** be equal to one of the [known values below](#-known-values). + +##### action Known Values + +| Value | Description | +| ---------------- | ----------- | +| `CreateVariable` | | + +#### firstVariableName + +Name of variable to create + +`firstVariableName` + +- is **required** +- type: `string` + +##### firstVariableName Type + +`string` + +All instances must conform to this regular expression + +```regex +^(\w+)$ +``` + +- test example: [Var1]() + +##### firstVariableName Example + +```json +Var1 +``` + +#### name + +Action reference name + +`name` + +- is **required** +- type: `string` + +##### name Type + +`string` + +All instances must conform to this regular expression + +```regex +^(\w+)$ +``` + +- test example: [CreateVar]() + +##### name Example + +```json +CreateVar +``` + +#### source + +Value to set in the variable + +`source` + +- is **required** +- type: `enum` + +The value of this property **must** be equal to one of the [known values below](#-known-values). + +##### source Known Values + +| Value | Description | +| ------------------- | ---------------------------------------------------- | +| `Value` | Use the value field | +| `PartsCount` | Count of fragment parts | +| `PartsXml` | XML representation of the fragment parts | +| `PartsLength` | Length of all fragment parts together | +| `StringPartsText` | Count of fragment string based parts | +| `StringPartsLength` | Concatenated text of all fragment string based parts | + +#### value + +Value to set in the variable if this is set as the source + +`value` + +- is optional +- type: multiple + +##### value Type + +Unknown type `integer,string`. + +```json +{ + "$id": "#/properties/actions/items/properties/createVariable/value", + "type": ["integer", "string"], + "description": "Value to set in the variable if this is set as the source", + "examples": ["print", 20], + "simpletype": "multiple" +} +``` + +##### value Examples + +```json +print +``` + +```json +20 +``` + +#### Condition 2 + +`object` with following properties: + +| Property | Type | Required | +| -------------------- | ------ | ------------ | +| `action` | string | **Required** | +| `change` | string | **Required** | +| `firstVariableName` | string | **Required** | +| `name` | string | **Required** | +| `secondVariableName` | string | **Required** | + +#### action + +Action type + +`action` + +- is **required** +- type: `enum` + +The value of this property **must** be equal to one of the [known values below](#-known-values). + +##### action Known Values + +| Value | Description | +| ---------------- | ----------- | +| `UpdateVariable` | | + +#### change + +Operation to perform + +`change` + +- is **required** +- type: `enum` + +The value of this property **must** be equal to one of the [known values below](#-known-values). + +##### change Known Values + +| Value | Description | +| ---------- | ----------- | +| `Add` | | +| `Subtract` | | +| `Concat` | | +| `Remove` | | +| `Set` | | + +#### firstVariableName + +Name of variable to update and perform a operation against + +`firstVariableName` + +- is **required** +- type: `string` + +##### firstVariableName Type + +`string` + +All instances must conform to this regular expression + +```regex +^(\w+)$ +``` + +- test example: [Var1]() + +##### firstVariableName Example + +```json +Var1 +``` + +#### name + +Action reference name + +`name` + +- is **required** +- type: `string` + +##### name Type + +`string` + +All instances must conform to this regular expression + +```regex +^(\w+)$ +``` + +- test example: [SetVar]() + +##### name Example + +```json +SetVar +``` + +#### secondVariableName + +Name of variable to use on the other end of the operation + +`secondVariableName` + +- is **required** +- type: `string` + +##### secondVariableName Type + +`string` + +All instances must conform to this regular expression + +```regex +^(\w+)$ +``` + +- test example: [Var2]() + +##### secondVariableName Example + +```json +Var2 +``` + +#### Condition 3 + +`object` with following properties: + +| Property | Type | Required | +| -------------------- | ------ | ------------ | +| `action` | string | **Required** | +| `assert` | string | **Required** | +| `firstVariableName` | string | **Required** | +| `name` | string | **Required** | +| `secondVariableName` | string | **Required** | + +#### action + +Action type + +`action` + +- is **required** +- type: `enum` + +The value of this property **must** be equal to one of the [known values below](#-known-values). + +##### action Known Values + +| Value | Description | +| -------- | ----------- | +| `Assert` | | + +#### assert + +The condition to assert between the two variables + +`assert` + +- is **required** +- type: `enum` + +The value of this property **must** be equal to one of the [known values below](#-known-values). + +##### assert Known Values + +| Value | Description | +| --------------------- | ----------------------------------- | +| `MatchesPattern` | Matches a pattern string (e.g. \w+) | +| `Equals` | | +| `NotEquals` | | +| `GreaterThan` | | +| `GreaterThanOrEquals` | | +| `LessThan` | | +| `LessThanOrEquals` | | +| `Contains` | | +| `StartsWith` | | +| `EndsWith` | | + +#### firstVariableName + +Name of the first variable in the condition + +`firstVariableName` + +- is **required** +- type: `string` + +##### firstVariableName Type + +`string` + +All instances must conform to this regular expression + +```regex +^(\w+)$ +``` + +- test example: [Var1]() + +##### firstVariableName Example + +```json +Var1 +``` + +#### name + +Action reference name + +`name` + +- is **required** +- type: `string` + +##### name Type + +`string` + +All instances must conform to this regular expression + +```regex +^(\w+)$ +``` + +- test example: [CheckVar]() + +##### name Example + +```json +CheckVar +``` + +#### secondVariableName + +Name of the second variable in the condition + +`secondVariableName` + +- is **required** +- type: `string` + +##### secondVariableName Type + +`string` + +All instances must conform to this regular expression + +```regex +^(\w+)$ +``` + +- test example: [Var2]() + +##### secondVariableName Example + +```json +Var2 +``` + ## fragments Text parsing rules @@ -35,6 +461,7 @@ All items must be of the type: `object` with following properties: | Property | Type | Required | Default | | ------------------------ | ------- | ------------ | ------------ | +| `actions` | array | Optional | | | `boundsAsParts` | boolean | Optional | `false` | | `cacheable` | boolean | Optional | `false` | | `clearCache` | boolean | Optional | `false` | @@ -54,6 +481,31 @@ All items must be of the type: `object` with following properties: | `partsPadding` | string | Optional | | | `start` | string | Optional | | +#### actions + +`actions` + +- is optional +- type: `string[]` + +##### actions Type + +Array type: `string[]` + +All items must be of the type: `string` + +All instances must conform to this regular expression + +```regex +^(\w+)$ +``` + +- test example: [SetLength]() +- test example: [SetExpected]() +- test example: [AssertLength]() + +Name of actions to run if the fragment matched successfully + #### boundsAsParts Add the matched start and end patterns as parts @@ -139,7 +591,7 @@ CloseBracket #### expressionMode -Experssion parser mode +Expression parser mode `expressionMode` @@ -159,7 +611,7 @@ The value of this property **must** be equal to one of the [known values below]( #### expressionOrder -Expression precendence within parent (lower number is higher precendence) +Expression precedence within parent (lower number is higher precedence) `expressionOrder` @@ -170,23 +622,9 @@ Expression precendence within parent (lower number is higher precendence) `integer` -#### fallThrough - -Add the children of this fragment instead of this fragment to the AST - -`fallThrough` - -- is optional -- type: `boolean` -- default: `false` - -##### fallThrough Type - -`boolean` - #### fallThroughMode -Add the children of this fragment instead of this fragment to the AST, and discard this fragment. +Add the children of this fragment instead of this fragment to the AST, and discard this fragment `fallThroughMode` @@ -198,12 +636,12 @@ The value of this property **must** be equal to one of the [known values below]( ##### fallThroughMode Known Values -| Value | Description | -| ------- | --------------------------------------------- | -| `None` | No fall through | -| `Empty` | Fall through if there are no children | -| `One` | Fall through if there is one or less children | -| `All` | Always fall through | +| Value | Description | +| ------- | ------------------------------------------ | +| `None` | No fall through | +| `Empty` | Fall through if there are no children | +| `One` | Fall through if there is one or less child | +| `All` | Always fall through | #### isNoise @@ -296,7 +734,7 @@ All instances must conform to this regular expression [StringLiteral]() - test example: [[Script]]() -Names of nested fragments (surrounded by '[' and ']' and/or patterns +Names of nested fragments (surrounded by '[' and ']') and/or patterns #### partsDelimiter @@ -341,7 +779,7 @@ Require that parts are separated by the delimiter #### partsMatchMode -Parts treatment +Repetition constraint `partsMatchMode` @@ -417,7 +855,7 @@ Text parsing rule ## indexingMode -Lexer configuration +Token indexing configuration (lexing) `indexingMode` @@ -490,47 +928,53 @@ All instances must conform to this regular expression `patterns` - is **required** -- type: `object[]` +- type: `array` - defined in this schema ### patterns Type -Array type: `object[]` +Array type: `array` -All items must be of the type: `object` with following properties: +All items must be of the type: -| Property | Type | Required | Default | -| ---------- | ------- | ------------ | ------- | -| `isNoise` | boolean | Optional | `false` | -| `mergable` | boolean | Optional | `false` | -| `name` | string | **Required** | | -| `pattern` | string | **Required** | | +**One** of the following _conditions_ need to be fulfilled. -#### isNoise +#### Condition 1 -Text to be ignored while parsing +`object` with following properties: -`isNoise` +| Property | Type | Required | Default | +| ------------- | ------- | ------------ | ------- | +| `isAuxiliary` | boolean | Optional | `false` | +| `isNoise` | boolean | Optional | `false` | +| `name` | string | **Required** | | +| `pattern` | string | **Required** | | + +#### isAuxiliary + +Pattern won't be used in pre-parse lexing but may be referenced by a fragment + +`isAuxiliary` - is optional - type: `boolean` - default: `false` -##### isNoise Type +##### isAuxiliary Type `boolean` -#### mergable +#### isNoise -Tokens surrounding ignored noise can be removed +Text to be ignored while parsing -`mergable` +`isNoise` - is optional - type: `boolean` - default: `false` -##### mergable Type +##### isNoise Type `boolean` @@ -588,7 +1032,98 @@ All instances must conform to this regular expression \s+ ``` -Patterns for generating tokens +#### Condition 2 + +`object` with following properties: + +| Property | Type | Required | Default | +| ------------- | ------- | ------------ | ------- | +| `isAuxiliary` | boolean | Optional | `false` | +| `isNoise` | boolean | Optional | `false` | +| `name` | string | **Required** | | +| `pattern` | string | Optional | | + +#### isAuxiliary + +Pattern won't be used in pre-parse lexing but may be referenced by a fragment + +`isAuxiliary` + +- is optional +- type: `boolean` +- default: `false` + +##### isAuxiliary Type + +`boolean` + +#### isNoise + +Text to be ignored while parsing + +`isNoise` + +- is optional +- type: `boolean` +- default: `false` + +##### isNoise Type + +`boolean` + +#### name + +Pattern reference name + +`name` + +- is **required** +- type: `string` + +##### name Type + +`string` + +All instances must conform to this regular expression + +```regex +^(\w+)$ +``` + +- test example: [StringLiteral]() + +##### name Example + +```json +StringLiteral +``` + +#### pattern + +Fragment to run during pre-parse lexing + +`pattern` + +- is optional +- type: `string` + +##### pattern Type + +`string` + +All instances must conform to this regular expression + +```regex +^(\w+)$ +``` + +- test example: [SpecialSection]() + +##### pattern Example + +```json +SpecialSection +``` ## startingFragment @@ -617,5 +1152,3 @@ All instances must conform to this regular expression ```json "Script" ``` - -Generated using [jsonschema2md](https://github.com/adobe/jsonschema2md) diff --git a/docs/Matcher/LanguageMatcherDefinition.schema.json b/docs/Matcher/LanguageMatcherDefinition.schema.json index ac81d60..4f34b0f 100644 --- a/docs/Matcher/LanguageMatcherDefinition.schema.json +++ b/docs/Matcher/LanguageMatcherDefinition.schema.json @@ -3,6 +3,7 @@ "type": "object", "title": "Language Matcher", "description": "Language parser grammar", + "$id": "#/languageMatcher", "required": [ "name", "startingFragment", @@ -33,12 +34,17 @@ "indexingMode": { "$id": "#/properties/indexingMode", "type": "string", - "description": "Lexer configuration: \n\nNone - Disable lexing; \nLazy - Generate tokens on demand; \nEager - Generate tokens before parsing;", + "description": "Token indexing configuration (lexing)", "enum": [ "None", "Lazy", "Eager" ], + "meta:enum": { + "None": "Disable lexing", + "Lazy": "Generate tokens on demand", + "Eager": "Generate tokens before parsing" + }, "default": "Lazy", "examples": [ "Eager" @@ -54,47 +60,310 @@ "$id": "#/properties/patterns", "type": "array", "items": { - "$id": "#/properties/patterns/items", - "type": "object", - "title": "Pattern", - "description": "Patterns for generating tokens", - "required": [ - "name", - "pattern" - ], - "additionalProperties": false, - "properties": { - "name": { - "$id": "#/properties/patterns/items/properties/name", - "type": "string", - "description": "Pattern reference name", - "examples": [ - "StringLiteral" + "oneOf": [ + { + "$id": "#/properties/patterns/items/properties/pattern", + "type": "object", + "title": "Pattern", + "description": "Patterns for generating tokens", + "required": [ + "name", + "pattern" ], - "pattern": "^(\\w+)$" + "additionalProperties": false, + "properties": { + "name": { + "$id": "#/properties/patterns/items/properties/pattern/name", + "type": "string", + "description": "Pattern reference name", + "examples": [ + "StringLiteral" + ], + "pattern": "^(\\w+)$" + }, + "pattern": { + "$id": "#/properties/patterns/items/properties/pattern/pattern", + "type": "string", + "description": "Pattern used to parse tokens from text", + "examples": [ + "\\s+" + ], + "pattern": "^(.*)$" + }, + "isNoise": { + "$id": "#/properties/patterns/items/properties/pattern/isNoise", + "type": "boolean", + "description": "Text to be ignored while parsing", + "default": false + }, + "isAuxiliary": { + "$id": "#/properties/patterns/items/properties/pattern/isAuxiliary", + "type": "boolean", + "description": "Pattern won't be used in pre-parse lexing but may be referenced by a fragment", + "default": false + } + } }, - "pattern": { - "$id": "#/properties/patterns/items/properties/pattern", - "type": "string", - "description": "Pattern used to parse tokens from text", - "examples": [ - "\\s+" + { + "$id": "#/properties/patterns/items/properties/fragmentPattern", + "type": "object", + "title": "Fragment Pattern", + "description": "Generate tokens using a fragment in pre-parse lexing", + "required": [ + "name", + "fragment" + ], + "additionalProperties": false, + "properties": { + "name": { + "$id": "#/properties/patterns/items/properties/fragmentPattern/name", + "type": "string", + "description": "Pattern reference name", + "examples": [ + "StringLiteral" + ], + "pattern": "^(\\w+)$" + }, + "pattern": { + "$id": "#/properties/patterns/items/properties/fragmentPattern/fragment", + "type": "string", + "description": "Fragment to run during pre-parse lexing", + "examples": [ + "SpecialSection" + ], + "pattern": "^(\\w+)$" + }, + "isNoise": { + "$id": "#/properties/patterns/items/properties/fragmentPattern/isNoise", + "type": "boolean", + "description": "Text to be ignored while parsing", + "default": false + }, + "isAuxiliary": { + "$id": "#/properties/patterns/items/properties/fragmentPattern/isAuxiliary", + "type": "boolean", + "description": "Pattern won't be used in pre-parse lexing but may be referenced by a fragment", + "default": false + } + } + } + ] + } + }, + "actions": { + "$id": "#/properties/actions", + "type": "array", + "items": { + "oneOf": [ + { + "$id": "#/properties/actions/items/properties/createVariable", + "type": "object", + "title": "Create Variable", + "description": "Create a variable", + "required": [ + "name", + "action", + "firstVariableName", + "source" ], - "pattern": "^(.*)$" + "additionalProperties": false, + "properties": { + "name": { + "$id": "#/properties/actions/items/properties/createVariable/name", + "type": "string", + "description": "Action reference name", + "examples": [ + "CreateVar" + ], + "pattern": "^(\\w+)$" + }, + "action": { + "$id": "#/properties/actions/items/properties/createVariable/action", + "type": "string", + "description": "Action type", + "enum": [ + "CreateVariable" + ] + }, + "firstVariableName": { + "$id": "#/properties/actions/items/properties/createVariable/firstVariableName", + "type": "string", + "description": "Name of variable to create", + "examples": [ + "Var1" + ], + "pattern": "^(\\w+)$" + }, + "source": { + "$id": "#/properties/actions/items/properties/createVariable/source", + "type": "string", + "description": "Value to set in the variable", + "enum": [ + "Value", + "PartsCount", + "PartsXml", + "PartsLength", + "StringPartsText", + "StringPartsLength" + ], + "meta:enum": { + "Value": "Use the value field", + "PartsCount": "Count of fragment parts", + "PartsXml": "XML representation of the fragment parts", + "PartsLength": "Length of all fragment parts together", + "StringPartsText": "Count of fragment string based parts", + "StringPartsLength": "Concatenated text of all fragment string based parts" + } + }, + "value": { + "$id": "#/properties/actions/items/properties/createVariable/value", + "type": [ + "integer", + "string" + ], + "description": "Value to set in the variable if this is set as the source", + "examples": [ + "print", + 20 + ] + } + } }, - "isNoise": { - "$id": "#/properties/patterns/items/properties/isNoise", - "type": "boolean", - "description": "Text to be ignored while parsing", - "default": false + { + "$id": "#/properties/actions/items/properties/updateVariable", + "type": "object", + "title": "Update Variable", + "description": "Update and perform an operation on a variable", + "required": [ + "name", + "action", + "firstVariableName", + "secondVariableName", + "change" + ], + "additionalProperties": false, + "properties": { + "name": { + "$id": "#/properties/actions/items/properties/updateVariable/name", + "type": "string", + "description": "Action reference name", + "examples": [ + "SetVar" + ], + "pattern": "^(\\w+)$" + }, + "action": { + "$id": "#/properties/actions/items/properties/updateVariable/action", + "type": "string", + "description": "Action type", + "enum": [ + "UpdateVariable" + ] + }, + "firstVariableName": { + "$id": "#/properties/actions/items/properties/updateVariable/firstVariableName", + "type": "string", + "description": "Name of variable to update and perform a operation against", + "examples": [ + "Var1" + ], + "pattern": "^(\\w+)$" + }, + "secondVariableName": { + "$id": "#/properties/actions/items/properties/updateVariable/secondVariableName", + "type": "string", + "description": "Name of variable to use on the other end of the operation", + "examples": [ + "Var2" + ], + "pattern": "^(\\w+)$" + }, + "change": { + "$id": "#/properties/actions/items/properties/updateVariable/change", + "type": "string", + "description": "Operation to perform", + "enum": [ + "Add", + "Subtract", + "Concat", + "Remove", + "Set" + ] + } + } }, - "mergable": { - "$id": "#/properties/patterns/items/properties/mergable", - "type": "boolean", - "description": "Tokens surrounding ignored noise can be removed", - "default": false + { + "$id": "#/properties/actions/items/properties/assert", + "type": "object", + "title": "Assert", + "description": "Assert an condition between two variables. If unsuccessful, the fragment is marked as failed", + "required": [ + "name", + "action", + "firstVariableName", + "secondVariableName", + "assert" + ], + "additionalProperties": false, + "properties": { + "name": { + "$id": "#/properties/actions/items/properties/assert/name", + "type": "string", + "description": "Action reference name", + "examples": [ + "CheckVar" + ], + "pattern": "^(\\w+)$" + }, + "action": { + "$id": "#/properties/actions/items/properties/assert/action", + "type": "string", + "description": "Action type", + "enum": [ + "Assert" + ] + }, + "firstVariableName": { + "$id": "#/properties/actions/items/properties/assert/firstVariableName", + "type": "string", + "description": "Name of the first variable in the condition", + "examples": [ + "Var1" + ], + "pattern": "^(\\w+)$" + }, + "secondVariableName": { + "$id": "#/properties/actions/items/properties/assert/secondVariableName", + "type": "string", + "description": "Name of the second variable in the condition", + "examples": [ + "Var2" + ], + "pattern": "^(\\w+)$" + }, + "assert": { + "$id": "#/properties/actions/items/properties/assert/assert", + "type": "string", + "description": "The condition to assert between the two variables", + "enum": [ + "Equals", + "NotEquals", + "GreaterThan", + "GreaterThanOrEquals", + "LessThan", + "LessThanOrEquals", + "Contains", + "StartsWith", + "EndsWith", + "MatchesPattern" + ], + "meta:enum": { + "MatchesPattern": "Matches a pattern string (e.g. \\w+)" + } + } + } } - } + ] } }, "fragments": { @@ -127,7 +396,7 @@ "items": { "$id": "#/properties/fragments/items/properties/parts/items", "type": "string", - "description": "Names of nested fragments (surrounded by '[' and ']' and/or patterns", + "description": "Names of nested fragments (surrounded by '[' and ']') and/or patterns", "examples": [ "StringLiteral", "[Script]" @@ -135,6 +404,21 @@ "pattern": "^(\\w+|\\[\\w+\\])$" } }, + "actions": { + "$id": "#/properties/fragments/items/properties/actions", + "type": "array", + "items": { + "$id": "#/properties/fragments/items/properties/parts/items", + "type": "string", + "description": "Name of actions to run if the fragment matched successfully", + "examples": [ + "SetLength", + "SetExpected", + "AssertLength" + ], + "pattern": "^(\\w+)$" + } + }, "partsDelimiter": { "$id": "#/properties/fragments/items/properties/partsDelimiter", "type": "string", @@ -153,13 +437,18 @@ "partsMatchMode": { "$id": "#/properties/fragments/items/properties/partsMatchMode", "type": "string", - "description": "Parts treatment: \n\nMultiple - Match any of the parts (one or more); \nOrdered - Match the parts in the order given; \nOne - Match one of the parts;", "default": "Multiple", + "description": "Repetition constraint", "enum": [ "Multiple", "Ordered", "One" - ] + ], + "meta:enum": { + "Multiple": "Match any of the parts (one or more)", + "Ordered": "Match the parts in the order given", + "One": "Match one of the parts" + } }, "partsPadding": { "$id": "#/properties/fragments/items/properties/partsPadding", @@ -202,14 +491,20 @@ "fallThroughMode": { "$id": "#/properties/fragments/items/properties/fallThroughMode", "type": "string", - "description": "Add the children of this fragment instead of this fragment to the AST, and discard this fragment. Fall through mode: \n\nNone - No fall through; \nEmpty - Fall through if there are no children; \nOne - Fall through if there is one or less child; \nAll - Always fall through;", + "description": "Add the children of this fragment instead of this fragment to the AST, and discard this fragment", "default": "None", "enum": [ "None", "Empty", "One", "All" - ] + ], + "meta:enum": { + "None": "No fall through", + "Empty": "Fall through if there are no children", + "One": "Fall through if there is one or less child", + "All": "Always fall through" + } }, "cacheable": { "$id": "#/properties/fragments/items/properties/cacheable", @@ -226,13 +521,18 @@ "expressionMode": { "$id": "#/properties/fragments/items/properties/expressionMode", "type": "string", - "description": "Expression parser mode: \n\nNone - No expression parsing; \nBinaryTree - Expression tree to have a max of two child parts per node; \nLikeNameTree - Expression tree combines parts that are the same type;", + "description": "Expression parser mode", "default": "None", "enum": [ "None", "BinaryTree", "LikeNameTree" - ] + ], + "meta:enum": { + "None": "No expression parsing", + "BinaryTree": "Expression tree to have a max of two child parts per node", + "LikeNameTree": "Expression tree combines parts that are the same type" + } }, "expressionOrder": { "$id": "#/properties/fragments/items/properties/expressionOrder", diff --git a/src/Matcher.CLI/Matcher.CLI.csproj b/src/Matcher.CLI/Matcher.CLI.csproj index 37a1f1f..888fa6b 100644 --- a/src/Matcher.CLI/Matcher.CLI.csproj +++ b/src/Matcher.CLI/Matcher.CLI.csproj @@ -2,7 +2,7 @@ Exe - net5.0 + net6.0 matchercli Synfron.Staxe.Matcher.CLI Daquanne Dwight diff --git a/src/Matcher.Interop.Ebnf/CharacterClassMatchEngine.cs b/src/Matcher.Interop.Ebnf/CharacterClassMatchEngine.cs index 3d91c86..2f76735 100644 --- a/src/Matcher.Interop.Ebnf/CharacterClassMatchEngine.cs +++ b/src/Matcher.Interop.Ebnf/CharacterClassMatchEngine.cs @@ -1,41 +1,42 @@ -/* Generated by Synfron.Staxe.Matcher v0.8.0.0*/ +/* Generated by Synfron.Staxe.Matcher v3.0.0.0*/ using Synfron.Staxe.Matcher.Data; +using Synfron.Staxe.Matcher.Input; using System; using System.Collections.Generic; using System.Linq; -namespace Synfron.Staxe.Matcher.Interop.Bnf +namespace Synfron.Staxe.Matcher { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "")] public class CharacterClassMatchEngine : AbstractLanguageMatchEngine { public override MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true) { - FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; - Span checkFlags = stackalloc int[7]; + FragmentMatchData parentMatchData = new FragmentMatchData{StartIndex = 0}; State state = new State() - { Code = code, DistinctStringMatches = new List(2000) }; + {Code = code}; bool success = false; switch (fragmentMatcher) { + case "CharacterClass": + success = MatchFragmentCharacterClass(ref state, parentMatchData); + break; case "NotCharacterClassBody": - success = MatchFragmentNotCharacterClassBody(ref state, matchData); + success = MatchFragmentNotCharacterClassBody(ref state, parentMatchData); break; case "CharacterClassBody": - success = MatchFragmentCharacterClassBody(ref state, matchData); + success = MatchFragmentCharacterClassBody(ref state, parentMatchData); break; case "CharacterRange": - success = MatchFragmentCharacterRange(ref state, matchData); + success = MatchFragmentCharacterRange(ref state, parentMatchData); break; case "HexRange": - success = MatchFragmentHexRange(ref state, matchData); + success = MatchFragmentHexRange(ref state, parentMatchData); break; } - IMatchData resultMatchData = matchData.Parts.FirstOrDefault(); + IMatchData resultMatchData = parentMatchData.Parts.FirstOrDefault(); int? failureIndex = success ? null : state.FailureIndex; - if (success && matchFullText && state.CurrentIndex != state.Code.Length) + if (success && matchFullText && state.CurrentIndex < state.Code.Length) { success = false; failureIndex = state.CurrentIndex; @@ -46,17 +47,16 @@ public override MatcherResult Match(string code, string fragmentMatcher, bool ma public override MatcherResult Match(string code, bool matchFullText = true) { - FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; - Span checkFlags = stackalloc int[7]; + FragmentMatchData parentMatchData = new FragmentMatchData{StartIndex = 0}; State state = new State() - { Code = code, DistinctStringMatches = new List(2000) }; - bool success = MatchFragmentCharacterClass(ref state, matchData); - IMatchData resultMatchData = matchData?.Parts.FirstOrDefault(); + {Code = code}; + bool success = MatchFragmentCharacterClass(ref state, parentMatchData); + IMatchData resultMatchData = parentMatchData?.Parts.FirstOrDefault(); int? failureIndex = success ? null : state.FailureIndex; - if (success && matchFullText && state.CurrentIndex != state.Code.Length) + if (success && matchFullText && state.CurrentIndex < state.Code.Length) { success = false; - failureIndex = state.CurrentIndex; + failureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); @@ -64,7 +64,7 @@ public override MatcherResult Match(string code, bool matchFullText = true) private (bool success, int offset) MatchLiteral0(string text, int startOffset) { - string literal = "^"; + string literal = "["; int length = literal.Length; if (startOffset + length > text.Length) { @@ -82,82 +82,94 @@ public override MatcherResult Match(string code, bool matchFullText = true) return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternCaret(ref State state, bool required, bool readOnly = false) + private bool MatchPatternOpenBracket(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) + int startOffset = state.CurrentIndex; + int length; + (success, length) = MatchLiteral0(state.Code, state.CurrentIndex); + matchData = default; + if (success) { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral0(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) + matchData = new StringMatchData{Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 1}; + if (!readOnly) { - stringMatchData = new StringMatchData { Name = "Caret", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.MaxDistinctIndex++; - if (!readOnly) + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + fragmentMatchData.StartIndex = startOffset; } } - else if (!required) - { - success = true; - } + } + else if (!required) + { + success = true; + } + + return success; + } - return (success, stringMatchData); + private bool MatchFragmentBoundsOpenBracket(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) + { + bool success = MatchPatternOpenBracket(ref state, parentMatchData, out matchData, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } - else + + return success; + } + + private (bool success, int offset) MatchLiteral1(string text, int startOffset) + { + string literal = "^"; + int length = literal.Length; + if (startOffset + length > text.Length) { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 3; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } + return (false, 0); + } - success = stringMatchData.Id == 3; - } - else + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral0(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Caret", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } + return (false, 0); } + } - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } + return (true, length); + } - if (!required) + private bool MatchPatternCaret(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) + { + bool success = false; + int startOffset = state.CurrentIndex; + int length; + (success, length) = MatchLiteral1(state.Code, state.CurrentIndex); + matchData = default; + if (success) + { + matchData = new StringMatchData{Name = "Caret", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 3}; + if (!readOnly) { - success = true; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } - - return (success, stringMatchData); } + else if (!required) + { + success = true; + } + + return success; } - private bool MatchFragmentBoundsCaret(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsCaret(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternCaret(ref state, true, readOnly); + bool success = MatchPatternCaret(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -267,90 +279,46 @@ private bool MatchFragmentBoundsCaret(ref State state, bool readOnly, out String return (true, offset - startOffset); } - private (bool success, StringMatchData matchData) MatchPatternHexChar(ref State state, bool required, bool readOnly = false) + private bool MatchPatternHexChar(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) + int startOffset = state.CurrentIndex; + int length; + (success, length) = MatchGroup0(state.Code, state.CurrentIndex); + matchData = default; + if (success) { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup0(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) + matchData = new StringMatchData{Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 4}; + if (!readOnly) { - stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.MaxDistinctIndex++; - if (!readOnly) + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + fragmentMatchData.StartIndex = startOffset; } } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); } - else + else if (!required) { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 4; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 4; - } - else - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup0(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); + success = true; } + + return success; } - private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternHexChar(ref state, true, false); + StringMatchData partMatchData; + bool success = MatchPatternHexChar(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); } return success; } - private (bool success, int offset) MatchLiteral1(string text, int startOffset) + private (bool success, int offset) MatchLiteral2(string text, int startOffset) { string literal = "-"; int length = literal.Length; @@ -370,79 +338,34 @@ private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData ma return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternDash(ref State state, bool required, bool readOnly = false) + private bool MatchPatternDash(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) + int startOffset = state.CurrentIndex; + int length; + (success, length) = MatchLiteral2(state.Code, state.CurrentIndex); + matchData = default; + if (success) { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral1(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) + matchData = new StringMatchData{Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 5}; + if (!readOnly) { - stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 5 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.MaxDistinctIndex++; - if (!readOnly) + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + fragmentMatchData.StartIndex = startOffset; } } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); } - else + else if (!required) { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 5; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 5; - } - else - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral1(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 5 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); + success = true; } + + return success; } - private bool MatchFragmentPartsOrderedModeHexRange(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOrderedModeHexRange(ref State state, FragmentMatchData parentMatchData) { bool success = true; bool partSuccess; @@ -450,7 +373,7 @@ private bool MatchFragmentPartsOrderedModeHexRange(ref State state, FragmentMatc StringMatchData stringMatchData = null; int distinctIndex = state.DistinctIndex; ; - success = MatchPartByTextMatcherHexChar(ref state, matchData); + success = MatchPartByTextMatcherHexChar(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -467,14 +390,14 @@ private bool MatchFragmentPartsOrderedModeHexRange(ref State state, FragmentMatc } distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternDash(ref state, true, false); + partSuccess = MatchPatternDash(ref state, parentMatchData, out stringMatchData, true, false); success = partSuccess; if (!success) { goto Break; } - success = MatchPartByTextMatcherHexChar(ref state, matchData); + success = MatchPartByTextMatcherHexChar(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -490,8 +413,8 @@ private bool MatchFragmentPartsOrderedModeHexRange(ref State state, FragmentMatc matchCount++; } - Break: - success = success || 2 <= matchCount; + Break: + success = 2 <= matchCount; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -500,18 +423,18 @@ private bool MatchFragmentPartsOrderedModeHexRange(ref State state, FragmentMatc return success; } - private bool MatchFragmentHexRange(ref State state, FragmentMatchData matchData) + private bool MatchFragmentHexRange(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "HexRange", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOrderedModeHexRange(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "HexRange", StartIndex = -1}; + success = (MatchFragmentPartsOrderedModeHexRange(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -521,13 +444,18 @@ private bool MatchFragmentHexRange(ref State state, FragmentMatchData matchData) if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } return success; } - private (bool success, int offset) MatchLiteral2(string text, int startOffset) + private (bool success, int offset) MatchLiteral3(string text, int startOffset) { string literal = "]"; int length = literal.Length; @@ -549,10 +477,10 @@ private bool MatchFragmentHexRange(ref State state, FragmentMatchData matchData) private (bool success, int offset) MatchNot0(string text, int startOffset) { - return text.Length > startOffset && !MatchLiteral2(text, startOffset).success ? (true, 0) : (false, 0); + return text.Length > startOffset && !MatchLiteral3(text, startOffset).success ? (true, 0) : (false, 0); } - private (bool success, int offset) MatchLiteral3(string text, int startOffset) + private (bool success, int offset) MatchLiteral4(string text, int startOffset) { string literal = "\\"; int length = literal.Length; @@ -577,7 +505,7 @@ private bool MatchFragmentHexRange(ref State state, FragmentMatchData matchData) int offset = startOffset; bool success; int subOffset; - (success, subOffset) = MatchLiteral3(text, offset); + (success, subOffset) = MatchLiteral4(text, offset); if (!success) { return (false, 0); @@ -635,90 +563,46 @@ private bool MatchFragmentHexRange(ref State state, FragmentMatchData matchData) return (true, offset - startOffset); } - private (bool success, StringMatchData matchData) MatchPatternAnyChar(ref State state, bool required, bool readOnly = false) + private bool MatchPatternAnyChar(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) + int startOffset = state.CurrentIndex; + int length; + (success, length) = MatchGroup2(state.Code, state.CurrentIndex); + matchData = default; + if (success) { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup2(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) + matchData = new StringMatchData{Name = "AnyChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 6}; + if (!readOnly) { - stringMatchData = new StringMatchData { Name = "AnyChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.MaxDistinctIndex++; - if (!readOnly) + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + fragmentMatchData.StartIndex = startOffset; } } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); } - else + else if (!required) { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 6; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 6; - } - else - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup2(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "AnyChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); + success = true; } + + return success; } - private bool MatchPartByTextMatcherAnyChar(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherAnyChar(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternAnyChar(ref state, true, false); + StringMatchData partMatchData; + bool success = MatchPatternAnyChar(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); } return success; } - private bool MatchFragmentPartsOrderedModeCharacterRange(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOrderedModeCharacterRange(ref State state, FragmentMatchData parentMatchData) { bool success = true; bool partSuccess; @@ -726,7 +610,7 @@ private bool MatchFragmentPartsOrderedModeCharacterRange(ref State state, Fragme StringMatchData stringMatchData = null; int distinctIndex = state.DistinctIndex; ; - success = MatchPartByTextMatcherAnyChar(ref state, matchData); + success = MatchPartByTextMatcherAnyChar(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -743,14 +627,14 @@ private bool MatchFragmentPartsOrderedModeCharacterRange(ref State state, Fragme } distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternDash(ref state, true, false); + partSuccess = MatchPatternDash(ref state, parentMatchData, out stringMatchData, true, false); success = partSuccess; if (!success) { goto Break; } - success = MatchPartByTextMatcherAnyChar(ref state, matchData); + success = MatchPartByTextMatcherAnyChar(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -766,8 +650,8 @@ private bool MatchFragmentPartsOrderedModeCharacterRange(ref State state, Fragme matchCount++; } - Break: - success = success || 2 <= matchCount; + Break: + success = 2 <= matchCount; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -776,18 +660,18 @@ private bool MatchFragmentPartsOrderedModeCharacterRange(ref State state, Fragme return success; } - private bool MatchFragmentCharacterRange(ref State state, FragmentMatchData matchData) + private bool MatchFragmentCharacterRange(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "CharacterRange", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOrderedModeCharacterRange(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "CharacterRange", StartIndex = -1}; + success = (MatchFragmentPartsOrderedModeCharacterRange(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -797,13 +681,18 @@ private bool MatchFragmentCharacterRange(ref State state, FragmentMatchData matc if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } return success; } - private bool MatchFragmentPartsMultipleModeCharacterClassBody(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsMultipleModeCharacterClassBody(ref State state, FragmentMatchData parentMatchData) { bool overallSuccess = false; bool subSuccess = false; @@ -816,48 +705,48 @@ private bool MatchFragmentPartsMultipleModeCharacterClassBody(ref State state, F { subSuccess = false; bool individualSuccess; - individualSuccess = MatchFragmentHexRange(ref state, matchData); + individualSuccess = MatchFragmentHexRange(ref state, parentMatchData); subSuccess |= individualSuccess; if (individualSuccess) { matchCount++; distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); + delimiterSuccess = true; goto Break; } - individualSuccess = MatchPartByTextMatcherHexChar(ref state, matchData); + individualSuccess = MatchPartByTextMatcherHexChar(ref state, parentMatchData); subSuccess |= individualSuccess; if (individualSuccess) { matchCount++; distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); + delimiterSuccess = true; goto Break; } - individualSuccess = MatchFragmentCharacterRange(ref state, matchData); + individualSuccess = MatchFragmentCharacterRange(ref state, parentMatchData); subSuccess |= individualSuccess; if (individualSuccess) { matchCount++; distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); + delimiterSuccess = true; goto Break; } - individualSuccess = MatchPartByTextMatcherAnyChar(ref state, matchData); + individualSuccess = MatchPartByTextMatcherAnyChar(ref state, parentMatchData); subSuccess |= individualSuccess; if (individualSuccess) { matchCount++; distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); + delimiterSuccess = true; goto Break; } - Break: - overallSuccess |= subSuccess; + Break: + overallSuccess |= subSuccess; } while (subSuccess && delimiterSuccess); if (delimiterSuccess && range != null) @@ -866,8 +755,7 @@ private bool MatchFragmentPartsMultipleModeCharacterClassBody(ref State state, F state.DistinctIndex = distinctIndex; } - bool thresholdSuccess = 1 <= matchCount; - bool success = overallSuccess || thresholdSuccess; + bool success = 1 <= matchCount; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -876,18 +764,18 @@ private bool MatchFragmentPartsMultipleModeCharacterClassBody(ref State state, F return success; } - private bool MatchFragmentCharacterClassBody(ref State state, FragmentMatchData matchData) + private bool MatchFragmentCharacterClassBody(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "CharacterClassBody", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsMultipleModeCharacterClassBody(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "CharacterClassBody", StartIndex = -1}; + success = (MatchFragmentPartsMultipleModeCharacterClassBody(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -897,23 +785,28 @@ private bool MatchFragmentCharacterClassBody(ref State state, FragmentMatchData if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } return success; } - private bool MatchFragmentPartsOneModeNotCharacterClassBody(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOneModeNotCharacterClassBody(ref State state, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - success = MatchFragmentCharacterClassBody(ref state, matchData); + success = MatchFragmentCharacterClassBody(ref state, parentMatchData); if (success) { matchCount++; } - success = false || matchCount > 0; + success = matchCount > 0; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -922,18 +815,19 @@ private bool MatchFragmentPartsOneModeNotCharacterClassBody(ref State state, Fra return success; } - private bool MatchFragmentNotCharacterClassBody(ref State state, FragmentMatchData matchData) + private bool MatchFragmentNotCharacterClassBody(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "NotCharacterClassBody", StartIndex = state.CurrentIndex }; - success = (MatchFragmentBoundsCaret(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeNotCharacterClassBody(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "NotCharacterClassBody", StartIndex = -1}; + StringMatchData startMatchData; + success = (MatchFragmentBoundsCaret(ref state, partMatchData, false, out startMatchData) && MatchFragmentPartsOneModeNotCharacterClassBody(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -943,127 +837,28 @@ private bool MatchFragmentNotCharacterClassBody(ref State state, FragmentMatchDa if (success) { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral4(string text, int startOffset) - { - string literal = "["; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternOpenBracket(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral4(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.MaxDistinctIndex++; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 1; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 1; - } - else + if (parentMatchData.StartIndex < 0) { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral4(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; + parentMatchData.StartIndex = partMatchData.StartIndex; } - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsOpenBracket(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternOpenBracket(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + parentMatchData.Parts.Add(partMatchData); } return success; } - private bool MatchFragmentPartsOneModeCharacterClass(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOneModeCharacterClass(ref State state, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - success = MatchFragmentNotCharacterClassBody(ref state, matchData) || MatchFragmentCharacterClassBody(ref state, matchData); + success = MatchFragmentNotCharacterClassBody(ref state, parentMatchData) || MatchFragmentCharacterClassBody(ref state, parentMatchData); if (success) { matchCount++; } - success = false || matchCount > 0; + success = matchCount > 0; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -1072,82 +867,36 @@ private bool MatchFragmentPartsOneModeCharacterClass(ref State state, FragmentMa return success; } - private (bool success, StringMatchData matchData) MatchPatternCloseBracket(ref State state, bool required, bool readOnly = false) + private bool MatchPatternCloseBracket(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) + int startOffset = state.CurrentIndex; + int length; + (success, length) = MatchLiteral3(state.Code, state.CurrentIndex); + matchData = default; + if (success) { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral2(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) + matchData = new StringMatchData{Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 2}; + if (!readOnly) { - stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.MaxDistinctIndex++; - if (!readOnly) + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + fragmentMatchData.StartIndex = startOffset; } } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); } - else + else if (!required) { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 2; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 2; - } - else - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral2(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); + success = true; } + + return success; } - private bool MatchFragmentBoundsCloseBracket(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsCloseBracket(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternCloseBracket(ref state, true, readOnly); + bool success = MatchPatternCloseBracket(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -1156,18 +905,20 @@ private bool MatchFragmentBoundsCloseBracket(ref State state, bool readOnly, out return success; } - private bool MatchFragmentCharacterClass(ref State state, FragmentMatchData matchData) + private bool MatchFragmentCharacterClass(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "CharacterClass", StartIndex = state.CurrentIndex }; - success = (MatchFragmentBoundsOpenBracket(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeCharacterClass(ref state, partMatcherData) && MatchFragmentBoundsCloseBracket(ref state, false, out StringMatchData endMatchData)); + partMatchData = new FragmentMatchData{Name = "CharacterClass", StartIndex = -1}; + StringMatchData startMatchData; + StringMatchData endMatchData = null; + success = (MatchFragmentBoundsOpenBracket(ref state, partMatchData, false, out startMatchData) && MatchFragmentPartsOneModeCharacterClass(ref state, partMatchData) && MatchFragmentBoundsCloseBracket(ref state, partMatchData, false, out endMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -1177,7 +928,12 @@ private bool MatchFragmentCharacterClass(ref State state, FragmentMatchData matc if (success) { - matchData.Parts.AddRange(partMatcherData.Parts); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.AddRange(partMatchData.Parts); } return success; diff --git a/src/Matcher.Interop.Ebnf/EbnfConverter.cs b/src/Matcher.Interop.Ebnf/EbnfConverter.cs index 84884b3..2ebe97a 100644 --- a/src/Matcher.Interop.Ebnf/EbnfConverter.cs +++ b/src/Matcher.Interop.Ebnf/EbnfConverter.cs @@ -65,7 +65,10 @@ public LanguageMatcher Convert(string name, string ebnfText) AddRules((FragmentMatchData)matcherResult.MatchData); - _languageMatcher.IndexingMode = IndexingMode.Eager; + if (IgnoreWhitespace) + { + _languageMatcher.IndexingMode = IndexingMode.Eager; + } _languageMatcher.StartingFragment = _languageMatcher.Fragments.FirstOrDefault(); return _languageMatcher; diff --git a/src/Matcher.Interop.Ebnf/EbnfMatchEngine.cs b/src/Matcher.Interop.Ebnf/EbnfMatchEngine.cs index ea6cdac..3433a89 100644 --- a/src/Matcher.Interop.Ebnf/EbnfMatchEngine.cs +++ b/src/Matcher.Interop.Ebnf/EbnfMatchEngine.cs @@ -1,79 +1,89 @@ -/* Generated by Synfron.Staxe.Matcher v0.8.0.0*/ +/* Generated by Synfron.Staxe.Matcher v3.0.0.0*/ using Synfron.Staxe.Matcher.Data; using Synfron.Staxe.Matcher.Input; using System; using System.Collections.Generic; using System.Linq; +using System.Text; -namespace Synfron.Staxe.Matcher.Interop.Bnf +namespace Synfron.Staxe.Matcher { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "")] public class EbnfMatchEngine : AbstractLanguageMatchEngine { public override MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true) { - FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; + FragmentMatchData parentMatchData = new FragmentMatchData{StartIndex = 0}; State state = new State() - { Code = code, DistinctStringMatches = new List(2000), MatchCache = new Dictionary, FragmentMatchData>() }; - PreMatchPatterns(ref state); + {Code = code, DistinctStringMatches = new List(2000), MatchCache = new Dictionary, FragmentMatchData>(), MatchLogBuilder = new StringBuilder()}; + state.PreMatchSuccess = PreMatchPatterns(ref state); bool success = false; switch (fragmentMatcher) { case "Rules": - success = MatchFragmentRules(ref state, matchData); + success = MatchFragmentRules(ref state, parentMatchData); + break; + case "RulePrefix": + success = MatchFragmentRulePrefix(ref state, parentMatchData); break; case "NotRulePrefix": - success = MatchFragmentNotRulePrefix(ref state, matchData); + success = MatchFragmentNotRulePrefix(ref state, parentMatchData); break; case "RuleName": - success = MatchFragmentRuleName(ref state, matchData); + success = MatchFragmentRuleName(ref state, parentMatchData); break; case "Rule": - success = MatchFragmentRule(ref state, matchData); + success = MatchFragmentRule(ref state, parentMatchData); break; case "Expression": - success = MatchFragmentExpression(ref state, matchData); + success = MatchFragmentExpression(ref state, parentMatchData); + break; + case "ExpressionSuffix": + success = MatchFragmentExpressionSuffix(ref state, parentMatchData); break; case "RepetitionSuffix": - success = MatchFragmentRepetitionSuffix(ref state, matchData); + success = MatchFragmentRepetitionSuffix(ref state, parentMatchData); break; case "ExceptSuffix": - success = MatchFragmentExceptSuffix(ref state, matchData); + success = MatchFragmentExceptSuffix(ref state, parentMatchData); break; case "CommaSuffix": - success = MatchFragmentCommaSuffix(ref state, matchData); + success = MatchFragmentCommaSuffix(ref state, parentMatchData); break; case "OrSuffix": - success = MatchFragmentOrSuffix(ref state, matchData); + success = MatchFragmentOrSuffix(ref state, parentMatchData); break; case "OptionalComma": - success = MatchFragmentOptionalComma(ref state, matchData); + success = MatchFragmentOptionalComma(ref state, parentMatchData); + break; + case "ExpressionValue": + success = MatchFragmentExpressionValue(ref state, parentMatchData); + break; + case "ExpressionGroup": + success = MatchFragmentExpressionGroup(ref state, parentMatchData); break; case "RepetitionGroup": - success = MatchFragmentRepetitionGroup(ref state, matchData); + success = MatchFragmentRepetitionGroup(ref state, parentMatchData); break; case "OptionalGroup": - success = MatchFragmentOptionalGroup(ref state, matchData); + success = MatchFragmentOptionalGroup(ref state, parentMatchData); break; case "ZeroOrOneItem": - success = MatchFragmentZeroOrOneItem(ref state, matchData); + success = MatchFragmentZeroOrOneItem(ref state, parentMatchData); break; case "OneOrMoreItem": - success = MatchFragmentOneOrMoreItem(ref state, matchData); + success = MatchFragmentOneOrMoreItem(ref state, parentMatchData); break; case "ZeroOrMoreItem": - success = MatchFragmentZeroOrMoreItem(ref state, matchData); + success = MatchFragmentZeroOrMoreItem(ref state, parentMatchData); break; case "Item": - success = MatchFragmentItem(ref state, matchData); + success = MatchFragmentItem(ref state, parentMatchData); break; } - IMatchData resultMatchData = matchData.Parts.FirstOrDefault(); + IMatchData resultMatchData = parentMatchData.Parts.FirstOrDefault(); int? failureIndex = success ? null : state.FailureIndex; - if (success && matchFullText && state.CurrentIndex != state.Code.Length) + if (success && matchFullText && state.CurrentIndex < (state.PreMatchSuccess ? state.DistinctStringMatches.LastOrDefault().GetEndIndex() : state.Code.Length)) { success = false; failureIndex = state.CurrentIndex; @@ -84,17 +94,17 @@ public override MatcherResult Match(string code, string fragmentMatcher, bool ma public override MatcherResult Match(string code, bool matchFullText = true) { - FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; + FragmentMatchData parentMatchData = new FragmentMatchData{StartIndex = 0}; State state = new State() - { Code = code, DistinctStringMatches = new List(2000), MatchCache = new Dictionary, FragmentMatchData>() }; - PreMatchPatterns(ref state); - bool success = MatchFragmentRules(ref state, matchData); - IMatchData resultMatchData = matchData?.Parts.FirstOrDefault(); + {Code = code, DistinctStringMatches = new List(2000), MatchCache = new Dictionary, FragmentMatchData>(), MatchLogBuilder = new StringBuilder()}; + state.PreMatchSuccess = PreMatchPatterns(ref state); + bool success = MatchFragmentRules(ref state, parentMatchData); + IMatchData resultMatchData = parentMatchData?.Parts.FirstOrDefault(); int? failureIndex = success ? null : state.FailureIndex; - if (success && matchFullText && state.CurrentIndex != state.Code.Length) + if (success && matchFullText && state.CurrentIndex < (state.PreMatchSuccess ? state.DistinctStringMatches.LastOrDefault().GetEndIndex() : state.Code.Length)) { success = false; - failureIndex = state.CurrentIndex; + failureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); @@ -104,13 +114,14 @@ private bool PreMatchPatterns(ref State state) { int codeLength = state.Code.Length; bool success = true; - while (state.CurrentIndex < codeLength) + StringMatchData matchData = null; + int currentIndex = 0; + while (success && (currentIndex = state.CurrentIndex) < codeLength) { - success = false; - success = MatchPatternNumber(ref state, true, false).success || MatchPatternDoubleQuoteLiteral(ref state, true, false).success || MatchPatternSingleQuoteLiteral(ref state, true, false).success || MatchPatternSpecialGroup(ref state, true, false).success || MatchPatternComment(ref state, true, false).success || MatchPatternIs(ref state, true, false).success || MatchPatternRuleName(ref state, true, false).success || MatchPatternOr(ref state, true, false).success || MatchPatternAsterisk(ref state, true, false).success || MatchPatternPlus(ref state, true, false).success || MatchPatternQuestionMark(ref state, true, false).success || MatchPatternSemicolon(ref state, true, false).success || MatchPatternPeriod(ref state, true, false).success || MatchPatternOpenBrace(ref state, true, false).success || MatchPatternCloseBrace(ref state, true, false).success || MatchPatternOpenBracket(ref state, true, false).success || MatchPatternCloseBracket(ref state, true, false).success || MatchPatternOpenParens(ref state, true, false).success || MatchPatternCloseParens(ref state, true, false).success || MatchPatternWhitespace(ref state, true, false).success || MatchPatternComma(ref state, true, false).success || MatchPatternHexChar(ref state, true, false).success || MatchPatternDash(ref state, true, false).success; - if (!success) + success = MatchPatternNumber(ref state, null, out matchData, true, false) || MatchPatternDoubleQuoteLiteral(ref state, null, out matchData, true, false) || MatchPatternSingleQuoteLiteral(ref state, null, out matchData, true, false) || MatchPatternOptionalGroup(ref state, null, out matchData, true, false) || MatchPatternSpecialGroup(ref state, null, out matchData, true, false) || MatchPatternComment(ref state, null, out matchData, true, false) || MatchPatternIs(ref state, null, out matchData, true, false) || MatchPatternRuleName(ref state, null, out matchData, true, false) || MatchPatternOr(ref state, null, out matchData, true, false) || MatchPatternAsterisk(ref state, null, out matchData, true, false) || MatchPatternPlus(ref state, null, out matchData, true, false) || MatchPatternQuestionMark(ref state, null, out matchData, true, false) || MatchPatternSemicolon(ref state, null, out matchData, true, false) || MatchPatternPeriod(ref state, null, out matchData, true, false) || MatchPatternOpenBrace(ref state, null, out matchData, true, false) || MatchPatternCloseBrace(ref state, null, out matchData, true, false) || MatchPatternOpenParens(ref state, null, out matchData, true, false) || MatchPatternCloseParens(ref state, null, out matchData, true, false) || MatchPatternWhitespace(ref state, null, out matchData, true, false) || MatchPatternComma(ref state, null, out matchData, true, false) || MatchPatternHexChar(ref state, null, out matchData, true, false) || MatchPatternDash(ref state, null, out matchData, true, false); + if (matchData != null) { - break; + state.MatchLogBuilder.AppendLine($"{currentIndex}. Prematched {matchData.Name}: {matchData.Text}"); } } @@ -135,26 +146,29 @@ private bool PreMatchPatterns(ref State state) return success ? (true, offset - startOffset) : (false, 0); } - private (bool success, StringMatchData matchData) MatchPatternWhitespace(ref State state, bool required, bool readOnly = false) + private bool MatchPatternWhitespace(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; (success, length) = MatchOneOrMore2(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "Whitespace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 20 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "Whitespace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 21}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -162,39 +176,34 @@ private bool PreMatchPatterns(ref State state) success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 20; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 20; + success = matchData.Id == 21; } else { - int length; - int startOffset = state.CurrentIndex; (success, length) = MatchOneOrMore2(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "Whitespace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 20 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "Whitespace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 21}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -202,11 +211,11 @@ private bool PreMatchPatterns(ref State state) success = true; } - return (success, stringMatchData); + return success; } } - private (bool success, int offset) MatchLiteral28(string text, int startOffset) + private (bool success, int offset) MatchLiteral30(string text, int startOffset) { string literal = "<"; int length = literal.Length; @@ -226,7 +235,7 @@ private bool PreMatchPatterns(ref State state) return (true, length); } - private (bool success, int offset) MatchLiteral29(string text, int startOffset) + private (bool success, int offset) MatchLiteral31(string text, int startOffset) { string literal = "-"; int length = literal.Length; @@ -246,7 +255,7 @@ private bool PreMatchPatterns(ref State state) return (true, length); } - private (bool success, int offset) MatchLiteral30(string text, int startOffset) + private (bool success, int offset) MatchLiteral32(string text, int startOffset) { string literal = "_"; int length = literal.Length; @@ -276,13 +285,13 @@ private bool PreMatchPatterns(ref State state) return (true, subOffset); } - (success, subOffset) = MatchLiteral29(text, startOffset); + (success, subOffset) = MatchLiteral31(text, startOffset); if (success) { return (true, subOffset); } - (success, subOffset) = MatchLiteral30(text, startOffset); + (success, subOffset) = MatchLiteral32(text, startOffset); if (success) { return (true, subOffset); @@ -291,7 +300,7 @@ private bool PreMatchPatterns(ref State state) return (false, 0); } - private (bool success, int offset) MatchWildcard4(string text, int startOffset) + private (bool success, int offset) MatchWildcard5(string text, int startOffset) { int offset = startOffset; bool success = true; @@ -305,7 +314,7 @@ private bool PreMatchPatterns(ref State state) return (true, offset - startOffset); } - private (bool success, int offset) MatchLiteral31(string text, int startOffset) + private (bool success, int offset) MatchLiteral33(string text, int startOffset) { string literal = ">"; int length = literal.Length; @@ -325,12 +334,12 @@ private bool PreMatchPatterns(ref State state) return (true, length); } - private (bool success, int offset) MatchGroup12(string text, int startOffset) + private (bool success, int offset) MatchGroup13(string text, int startOffset) { int offset = startOffset; bool success; int subOffset; - (success, subOffset) = MatchLiteral28(text, offset); + (success, subOffset) = MatchLiteral30(text, offset); if (!success) { return (false, 0); @@ -344,14 +353,14 @@ private bool PreMatchPatterns(ref State state) } offset += subOffset; - (success, subOffset) = MatchWildcard4(text, offset); + (success, subOffset) = MatchWildcard5(text, offset); if (!success) { return (false, 0); } offset += subOffset; - (success, subOffset) = MatchLiteral31(text, offset); + (success, subOffset) = MatchLiteral33(text, offset); if (!success) { return (false, 0); @@ -361,7 +370,7 @@ private bool PreMatchPatterns(ref State state) return (true, offset - startOffset); } - private (bool success, int offset) MatchGroup13(string text, int startOffset) + private (bool success, int offset) MatchGroup14(string text, int startOffset) { int offset = startOffset; bool success; @@ -373,7 +382,7 @@ private bool PreMatchPatterns(ref State state) } offset += subOffset; - (success, subOffset) = MatchWildcard4(text, offset); + (success, subOffset) = MatchWildcard5(text, offset); if (!success) { return (false, 0); @@ -387,13 +396,13 @@ private bool PreMatchPatterns(ref State state) { bool success; int subOffset; - (success, subOffset) = MatchGroup12(text, startOffset); + (success, subOffset) = MatchGroup13(text, startOffset); if (success) { return (true, subOffset); } - (success, subOffset) = MatchGroup13(text, startOffset); + (success, subOffset) = MatchGroup14(text, startOffset); if (success) { return (true, subOffset); @@ -402,26 +411,29 @@ private bool PreMatchPatterns(ref State state) return (false, 0); } - private (bool success, StringMatchData matchData) MatchPatternRuleName(ref State state, bool required, bool readOnly = false) + private bool MatchPatternRuleName(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; (success, length) = MatchOr6(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "RuleName", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 7 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "RuleName", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 10}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -429,39 +441,34 @@ private bool PreMatchPatterns(ref State state) success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 7; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 7; + success = matchData.Id == 10; } else { - int length; - int startOffset = state.CurrentIndex; (success, length) = MatchOr6(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "RuleName", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 7 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "RuleName", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 10}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -469,32 +476,39 @@ private bool PreMatchPatterns(ref State state) success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchPartByTextMatcherRuleName(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherRuleName(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternRuleName(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: ((\u003c\\l(\\w|-|_)*\u003e)|(\\l(\\w|-|_)*))"); + StringMatchData partMatchData; + bool success = MatchPatternRuleName(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: ((\u003c\\l(\\w|-|_)*\u003e)|(\\l(\\w|-|_)*))"); + state.Id--; return success; } - private bool MatchFragmentPartsOneModeRuleName(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOneModeRuleName(ref State state, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - success = MatchPartByTextMatcherRuleName(ref state, matchData); + success = MatchPartByTextMatcherRuleName(ref state, parentMatchData); if (success) { matchCount++; } - success = false || matchCount > 0; + success = matchCount > 0; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -503,20 +517,23 @@ private bool MatchFragmentPartsOneModeRuleName(ref State state, FragmentMatchDat return success; } - private bool MatchFragmentRuleName(ref State state, FragmentMatchData matchData) + private bool MatchFragmentRuleName(ref State state, FragmentMatchData parentMatchData) { bool success = false; - if (!state.MatchCache.TryGetValue(new ValueTuple("RuleName", state.CurrentIndex), out FragmentMatchData partMatcherData)) + int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: RuleName"); + if (!state.MatchCache.TryGetValue(new ValueTuple("RuleName", state.CurrentIndex), out partMatchData)) { - int startIndex = state.CurrentIndex; int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "RuleName", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeRuleName(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "RuleName", StartIndex = -1}; + success = (MatchFragmentPartsOneModeRuleName(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - state.MatchCache[new ValueTuple("RuleName", startIndex)] = partMatcherData; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; + state.MatchCache[new ValueTuple("RuleName", startIndex)] = partMatchData; } else { @@ -525,26 +542,33 @@ private bool MatchFragmentRuleName(ref State state, FragmentMatchData matchData) state.DistinctIndex = distinctIndex; } } - else if (success = partMatcherData != null) + else if (success = partMatchData != null) { - state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; - state.DistinctIndex = partMatcherData.EndDistinctIndex; + state.CurrentIndex = partMatchData.StartIndex + partMatchData.Length; + state.DistinctIndex = partMatchData.EndDistinctIndex; } if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: RuleName"); + state.Id--; return success; } - private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - MatchPatternWhitespace(ref state, false, false); - success = MatchFragmentRuleName(ref state, matchData); + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); + success = MatchFragmentRuleName(ref state, parentMatchData); if (success) { matchCount++; @@ -552,10 +576,10 @@ private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchD if (success) { - MatchPatternWhitespace(ref state, false, false); + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); } - success = false || matchCount > 0; + success = matchCount > 0; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -564,7 +588,7 @@ private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchD return success; } - private (bool success, int offset) MatchLiteral24(string text, int startOffset) + private (bool success, int offset) MatchLiteral26(string text, int startOffset) { string literal = "::="; int length = literal.Length; @@ -584,7 +608,7 @@ private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchD return (true, length); } - private (bool success, int offset) MatchLiteral25(string text, int startOffset) + private (bool success, int offset) MatchLiteral27(string text, int startOffset) { string literal = ":="; int length = literal.Length; @@ -604,7 +628,7 @@ private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchD return (true, length); } - private (bool success, int offset) MatchLiteral26(string text, int startOffset) + private (bool success, int offset) MatchLiteral28(string text, int startOffset) { string literal = ":"; int length = literal.Length; @@ -624,7 +648,7 @@ private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchD return (true, length); } - private (bool success, int offset) MatchLiteral27(string text, int startOffset) + private (bool success, int offset) MatchLiteral29(string text, int startOffset) { string literal = "="; int length = literal.Length; @@ -648,25 +672,25 @@ private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchD { bool success; int subOffset; - (success, subOffset) = MatchLiteral24(text, startOffset); + (success, subOffset) = MatchLiteral26(text, startOffset); if (success) { return (true, subOffset); } - (success, subOffset) = MatchLiteral25(text, startOffset); + (success, subOffset) = MatchLiteral27(text, startOffset); if (success) { return (true, subOffset); } - (success, subOffset) = MatchLiteral26(text, startOffset); + (success, subOffset) = MatchLiteral28(text, startOffset); if (success) { return (true, subOffset); } - (success, subOffset) = MatchLiteral27(text, startOffset); + (success, subOffset) = MatchLiteral29(text, startOffset); if (success) { return (true, subOffset); @@ -675,26 +699,29 @@ private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchD return (false, 0); } - private (bool success, StringMatchData matchData) MatchPatternIs(ref State state, bool required, bool readOnly = false) + private bool MatchPatternIs(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; (success, length) = MatchOr3(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "Is", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "Is", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 9}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -702,39 +729,34 @@ private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchD success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 6; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 6; + success = matchData.Id == 9; } else { - int length; - int startOffset = state.CurrentIndex; (success, length) = MatchOr3(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "Is", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "Is", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 9}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -742,36 +764,41 @@ private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchD success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchFragmentBoundsIs(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsIs(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternIs(ref state, true, readOnly); + int startIndex = state.CurrentIndex; + bool success = MatchPatternIs(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}. {(success ? "Passed" : "Failed")} Bounds: {"(::=|:=|:|=)"}"); return success; } - private bool MatchFragmentRulePrefix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentRulePrefix(ref State state, FragmentMatchData parentMatchData) { bool success = false; - if (!state.MatchCache.TryGetValue(new ValueTuple("RulePrefix", state.CurrentIndex), out FragmentMatchData partMatcherData)) + int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: RulePrefix"); + if (!state.MatchCache.TryGetValue(new ValueTuple("RulePrefix", state.CurrentIndex), out partMatchData)) { - int startIndex = state.CurrentIndex; int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "RulePrefix", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeRulePrefix(ref state, partMatcherData) && MatchFragmentBoundsIs(ref state, false, out StringMatchData endMatchData)); + partMatchData = new FragmentMatchData{Name = "RulePrefix", StartIndex = -1}; + StringMatchData endMatchData = null; + success = (MatchFragmentPartsOneModeRulePrefix(ref state, partMatchData) && MatchFragmentBoundsIs(ref state, partMatchData, false, out endMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - state.MatchCache[new ValueTuple("RulePrefix", startIndex)] = partMatcherData; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; + state.MatchCache[new ValueTuple("RulePrefix", startIndex)] = partMatchData; } else { @@ -780,17 +807,24 @@ private bool MatchFragmentRulePrefix(ref State state, FragmentMatchData matchDat state.DistinctIndex = distinctIndex; } } - else if (success = partMatcherData != null) + else if (success = partMatchData != null) { - state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; - state.DistinctIndex = partMatcherData.EndDistinctIndex; + state.CurrentIndex = partMatchData.StartIndex + partMatchData.Length; + state.DistinctIndex = partMatchData.EndDistinctIndex; } if (success) { - matchData.Parts.AddRange(partMatcherData.Parts); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.AddRange(partMatchData.Parts); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: RulePrefix"); + state.Id--; return success; } @@ -884,26 +918,29 @@ private bool MatchFragmentRulePrefix(ref State state, FragmentMatchData matchDat return (true, offset - startOffset); } - private (bool success, StringMatchData matchData) MatchPatternDoubleQuoteLiteral(ref State state, bool required, bool readOnly = false) + private bool MatchPatternDoubleQuoteLiteral(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; (success, length) = MatchGroup1(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "DoubleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "DoubleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 2}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -911,39 +948,34 @@ private bool MatchFragmentRulePrefix(ref State state, FragmentMatchData matchDat success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 2; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 2; + success = matchData.Id == 2; } else { - int length; - int startOffset = state.CurrentIndex; (success, length) = MatchGroup1(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "DoubleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "DoubleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 2}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -951,18 +983,25 @@ private bool MatchFragmentRulePrefix(ref State state, FragmentMatchData matchDat success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchPartByTextMatcherDoubleQuoteLiteral(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherDoubleQuoteLiteral(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternDoubleQuoteLiteral(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: (\"(\"!.)*\")"); + StringMatchData partMatchData; + bool success = MatchPatternDoubleQuoteLiteral(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: (\"(\"!.)*\")"); + state.Id--; return success; } @@ -1056,26 +1095,29 @@ private bool MatchPartByTextMatcherDoubleQuoteLiteral(ref State state, FragmentM return (true, offset - startOffset); } - private (bool success, StringMatchData matchData) MatchPatternSingleQuoteLiteral(ref State state, bool required, bool readOnly = false) + private bool MatchPatternSingleQuoteLiteral(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; (success, length) = MatchGroup3(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "SingleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "SingleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 3}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -1083,39 +1125,34 @@ private bool MatchPartByTextMatcherDoubleQuoteLiteral(ref State state, FragmentM success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 3; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 3; + success = matchData.Id == 3; } else { - int length; - int startOffset = state.CurrentIndex; (success, length) = MatchGroup3(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "SingleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "SingleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 3}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -1123,18 +1160,25 @@ private bool MatchPartByTextMatcherDoubleQuoteLiteral(ref State state, FragmentM success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchPartByTextMatcherSingleQuoteLiteral(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherSingleQuoteLiteral(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternSingleQuoteLiteral(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: (\u0027(\u0027!.)*\u0027)"); + StringMatchData partMatchData; + bool success = MatchPatternSingleQuoteLiteral(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: (\u0027(\u0027!.)*\u0027)"); + state.Id--; return success; } @@ -1158,26 +1202,29 @@ private bool MatchPartByTextMatcherSingleQuoteLiteral(ref State state, FragmentM return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternOpenParens(ref State state, bool required, bool readOnly = false) + private bool MatchPatternOpenParens(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; (success, length) = MatchLiteral44(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "OpenParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 18 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "OpenParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 19}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -1185,39 +1232,34 @@ private bool MatchPartByTextMatcherSingleQuoteLiteral(ref State state, FragmentM success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 18; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 18; + success = matchData.Id == 19; } else { - int length; - int startOffset = state.CurrentIndex; (success, length) = MatchLiteral44(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "OpenParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 18 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "OpenParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 19}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -1225,28 +1267,29 @@ private bool MatchPartByTextMatcherSingleQuoteLiteral(ref State state, FragmentM success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchFragmentBoundsOpenParens(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsOpenParens(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternOpenParens(ref state, true, readOnly); + int startIndex = state.CurrentIndex; + bool success = MatchPatternOpenParens(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}. {(success ? "Passed" : "Failed")} Bounds: {"\\("}"); return success; } - private bool MatchFragmentPartsOneModeExpressionGroup(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOneModeExpressionGroup(ref State state, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - MatchPatternWhitespace(ref state, false, false); - success = MatchFragmentExpression(ref state, matchData); + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); + success = MatchFragmentExpression(ref state, parentMatchData); if (success) { matchCount++; @@ -1254,10 +1297,10 @@ private bool MatchFragmentPartsOneModeExpressionGroup(ref State state, FragmentM if (success) { - MatchPatternWhitespace(ref state, false, false); + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); } - success = false || matchCount > 0; + success = matchCount > 0; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -1286,26 +1329,29 @@ private bool MatchFragmentPartsOneModeExpressionGroup(ref State state, FragmentM return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternCloseParens(ref State state, bool required, bool readOnly = false) + private bool MatchPatternCloseParens(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; (success, length) = MatchLiteral45(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "CloseParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 19 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "CloseParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 20}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -1313,39 +1359,34 @@ private bool MatchFragmentPartsOneModeExpressionGroup(ref State state, FragmentM success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 19; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 19; + success = matchData.Id == 20; } else { - int length; - int startOffset = state.CurrentIndex; (success, length) = MatchLiteral45(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "CloseParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 19 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "CloseParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 20}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -1353,34 +1394,39 @@ private bool MatchFragmentPartsOneModeExpressionGroup(ref State state, FragmentM success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchFragmentBoundsCloseParens(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsCloseParens(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternCloseParens(ref state, true, readOnly); + int startIndex = state.CurrentIndex; + bool success = MatchPatternCloseParens(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}. {(success ? "Passed" : "Failed")} Bounds: {"\\)"}"); return success; } - private bool MatchFragmentExpressionGroup(ref State state, FragmentMatchData matchData) + private bool MatchFragmentExpressionGroup(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: ExpressionGroup"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ExpressionGroup", StartIndex = state.CurrentIndex }; - success = (MatchFragmentBoundsOpenParens(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatcherData) && MatchFragmentBoundsCloseParens(ref state, false, out StringMatchData endMatchData)); + partMatchData = new FragmentMatchData{Name = "ExpressionGroup", StartIndex = -1}; + StringMatchData startMatchData; + StringMatchData endMatchData = null; + success = (MatchFragmentBoundsOpenParens(ref state, partMatchData, false, out startMatchData) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatchData) && MatchFragmentBoundsCloseParens(ref state, partMatchData, false, out endMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -1390,13 +1436,20 @@ private bool MatchFragmentExpressionGroup(ref State state, FragmentMatchData mat if (success) { - matchData.Parts.AddRange(partMatcherData.Parts); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.AddRange(partMatchData.Parts); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: ExpressionGroup"); + state.Id--; return success; } - private (bool success, int offset) MatchLiteral40(string text, int startOffset) + private (bool success, int offset) MatchLiteral42(string text, int startOffset) { string literal = "{"; int length = literal.Length; @@ -1416,26 +1469,29 @@ private bool MatchFragmentExpressionGroup(ref State state, FragmentMatchData mat return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternOpenBrace(ref State state, bool required, bool readOnly = false) + private bool MatchPatternOpenBrace(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral40(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchLiteral42(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "OpenBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 14 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "OpenBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 17}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -1443,39 +1499,34 @@ private bool MatchFragmentExpressionGroup(ref State state, FragmentMatchData mat success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 14; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 14; + success = matchData.Id == 17; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral40(state.Code, state.CurrentIndex); + (success, length) = MatchLiteral42(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "OpenBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 14 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "OpenBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 17}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -1483,23 +1534,24 @@ private bool MatchFragmentExpressionGroup(ref State state, FragmentMatchData mat success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchFragmentBoundsOpenBrace(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsOpenBrace(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternOpenBrace(ref state, true, readOnly); + int startIndex = state.CurrentIndex; + bool success = MatchPatternOpenBrace(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}. {(success ? "Passed" : "Failed")} Bounds: {"\\{"}"); return success; } - private (bool success, int offset) MatchLiteral41(string text, int startOffset) + private (bool success, int offset) MatchLiteral43(string text, int startOffset) { string literal = "}"; int length = literal.Length; @@ -1519,26 +1571,29 @@ private bool MatchFragmentBoundsOpenBrace(ref State state, bool readOnly, out St return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternCloseBrace(ref State state, bool required, bool readOnly = false) + private bool MatchPatternCloseBrace(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral41(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchLiteral43(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "CloseBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 15 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "CloseBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 18}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -1546,39 +1601,34 @@ private bool MatchFragmentBoundsOpenBrace(ref State state, bool readOnly, out St success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 15; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 15; + success = matchData.Id == 18; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral41(state.Code, state.CurrentIndex); + (success, length) = MatchLiteral43(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "CloseBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 15 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "CloseBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 18}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -1586,34 +1636,39 @@ private bool MatchFragmentBoundsOpenBrace(ref State state, bool readOnly, out St success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchFragmentBoundsCloseBrace(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsCloseBrace(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternCloseBrace(ref state, true, readOnly); + int startIndex = state.CurrentIndex; + bool success = MatchPatternCloseBrace(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}. {(success ? "Passed" : "Failed")} Bounds: {"\\}"}"); return success; } - private bool MatchFragmentRepetitionGroup(ref State state, FragmentMatchData matchData) + private bool MatchFragmentRepetitionGroup(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: RepetitionGroup"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "RepetitionGroup", StartIndex = state.CurrentIndex }; - success = (MatchFragmentBoundsOpenBrace(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatcherData) && MatchFragmentBoundsCloseBrace(ref state, false, out StringMatchData endMatchData)); + partMatchData = new FragmentMatchData{Name = "RepetitionGroup", StartIndex = -1}; + StringMatchData startMatchData; + StringMatchData endMatchData = null; + success = (MatchFragmentBoundsOpenBrace(ref state, partMatchData, false, out startMatchData) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatchData) && MatchFragmentBoundsCloseBrace(ref state, partMatchData, false, out endMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -1623,13 +1678,20 @@ private bool MatchFragmentRepetitionGroup(ref State state, FragmentMatchData mat if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: RepetitionGroup"); + state.Id--; return success; } - private (bool success, int offset) MatchLiteral42(string text, int startOffset) + private (bool success, int offset) MatchLiteral6(string text, int startOffset) { string literal = "["; int length = literal.Length; @@ -1649,26 +1711,29 @@ private bool MatchFragmentRepetitionGroup(ref State state, FragmentMatchData mat return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternOpenBracket(ref State state, bool required, bool readOnly = false) + private bool MatchPatternOpenBracket(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral42(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchLiteral6(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 16 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 5}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -1676,39 +1741,34 @@ private bool MatchFragmentRepetitionGroup(ref State state, FragmentMatchData mat success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 16; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 16; + success = matchData.Id == 5; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral42(state.Code, state.CurrentIndex); + (success, length) = MatchLiteral6(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 16 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 5}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -1716,23 +1776,24 @@ private bool MatchFragmentRepetitionGroup(ref State state, FragmentMatchData mat success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchFragmentBoundsOpenBracket(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsOpenBracket(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternOpenBracket(ref state, true, readOnly); + int startIndex = state.CurrentIndex; + bool success = MatchPatternOpenBracket(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}. {(success ? "Passed" : "Failed")} Bounds: {"\\["}"); return success; } - private (bool success, int offset) MatchLiteral43(string text, int startOffset) + private (bool success, int offset) MatchLiteral7(string text, int startOffset) { string literal = "]"; int length = literal.Length; @@ -1752,26 +1813,29 @@ private bool MatchFragmentBoundsOpenBracket(ref State state, bool readOnly, out return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternCloseBracket(ref State state, bool required, bool readOnly = false) + private bool MatchPatternCloseBracket(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral43(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchLiteral7(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 17 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 6}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -1779,39 +1843,34 @@ private bool MatchFragmentBoundsOpenBracket(ref State state, bool readOnly, out success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 17; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 17; + success = matchData.Id == 6; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral43(state.Code, state.CurrentIndex); + (success, length) = MatchLiteral7(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 17 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 6}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -1819,57 +1878,79 @@ private bool MatchFragmentBoundsOpenBracket(ref State state, bool readOnly, out success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchFragmentBoundsCloseBracket(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsCloseBracket(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternCloseBracket(ref state, true, readOnly); + int startIndex = state.CurrentIndex; + bool success = MatchPatternCloseBracket(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}. {(success ? "Passed" : "Failed")} Bounds: {"\\]"}"); return success; } - private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData matchData) + private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "OptionalGroup", StartIndex = state.CurrentIndex }; - success = (MatchFragmentBoundsOpenBracket(ref state, false, out _) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatcherData) && MatchFragmentBoundsCloseBracket(ref state, false, out _)); - if (success) + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: OptionalGroup"); + if (!state.MatchCache.TryGetValue(new ValueTuple("OptionalGroup", state.CurrentIndex), out partMatchData)) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + int distinctIndex = state.DistinctIndex; + partMatchData = new FragmentMatchData{Name = "OptionalGroup", StartIndex = -1}; + StringMatchData startMatchData; + StringMatchData endMatchData = null; + success = (MatchFragmentBoundsOpenBracket(ref state, partMatchData, false, out startMatchData) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatchData) && MatchFragmentBoundsCloseBracket(ref state, partMatchData, false, out endMatchData)); + if (success) + { + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; + state.MatchCache[new ValueTuple("OptionalGroup", startIndex)] = partMatchData; + } + else + { + state.MatchCache[new ValueTuple("OptionalGroup", startIndex)] = null; + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } } - else + else if (success = partMatchData != null) { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; + state.CurrentIndex = partMatchData.StartIndex + partMatchData.Length; + state.DistinctIndex = partMatchData.EndDistinctIndex; } if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: OptionalGroup"); + state.Id--; return success; } private (bool success, int offset) MatchNot2(string text, int startOffset) { - return text.Length > startOffset && !MatchLiteral42(text, startOffset).success ? (true, 0) : (false, 0); + return text.Length > startOffset && !MatchLiteral6(text, startOffset).success ? (true, 0) : (false, 0); } private (bool success, int offset) MatchNot3(string text, int startOffset) { - return text.Length > startOffset && !MatchLiteral43(text, startOffset).success ? (true, 0) : (false, 0); + return text.Length > startOffset && !MatchLiteral7(text, startOffset).success ? (true, 0) : (false, 0); } private (bool success, int offset) MatchGroup4(string text, int startOffset) @@ -1901,7 +1982,7 @@ private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData match return (true, offset - startOffset); } - private (bool success, int offset) MatchLiteral9(string text, int startOffset) + private (bool success, int offset) MatchLiteral11(string text, int startOffset) { string literal = "\\"; int length = literal.Length; @@ -1926,14 +2007,14 @@ private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData match int offset = startOffset; bool success; int subOffset; - (success, subOffset) = MatchLiteral9(text, offset); + (success, subOffset) = MatchLiteral11(text, offset); if (!success) { return (false, 0); } offset += subOffset; - (success, subOffset) = MatchLiteral42(text, offset); + (success, subOffset) = MatchLiteral6(text, offset); if (!success) { return (false, 0); @@ -1948,14 +2029,14 @@ private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData match int offset = startOffset; bool success; int subOffset; - (success, subOffset) = MatchLiteral9(text, offset); + (success, subOffset) = MatchLiteral11(text, offset); if (!success) { return (false, 0); } offset += subOffset; - (success, subOffset) = MatchLiteral43(text, offset); + (success, subOffset) = MatchLiteral7(text, offset); if (!success) { return (false, 0); @@ -1976,7 +2057,12 @@ private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData match } (success, subOffset) = MatchGroup6(text, startOffset); - return success ? (true, subOffset) : (false, 0); + if (success) + { + return (true, subOffset); + } + + return (false, 0); } private (bool success, int offset) MatchOr1(string text, int startOffset) @@ -2019,7 +2105,7 @@ private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData match int offset = startOffset; bool success; int subOffset; - (success, subOffset) = MatchLiteral42(text, offset); + (success, subOffset) = MatchLiteral6(text, offset); if (!success) { return (false, 0); @@ -2033,7 +2119,7 @@ private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData match } offset += subOffset; - (success, subOffset) = MatchLiteral43(text, offset); + (success, subOffset) = MatchLiteral7(text, offset); if (!success) { return (false, 0); @@ -2043,26 +2129,29 @@ private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData match return (true, offset - startOffset); } - private (bool success, StringMatchData matchData) MatchPatternSpecialGroup(ref State state, bool required, bool readOnly = false) + private bool MatchPatternSpecialGroup(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; (success, length) = MatchGroup7(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "SpecialGroup", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "SpecialGroup", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 7}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -2070,39 +2159,34 @@ private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData match success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 4; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 4; + success = matchData.Id == 7; } else { - int length; - int startOffset = state.CurrentIndex; (success, length) = MatchGroup7(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "SpecialGroup", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "SpecialGroup", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 7}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -2110,18 +2194,25 @@ private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData match success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchPartByTextMatcherSpecialGroup(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherSpecialGroup(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternSpecialGroup(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: (\\[((\\[!\\]!.)|((\\\\\\[)|(\\\\\\])))+\\])"); + StringMatchData partMatchData; + bool success = MatchPatternSpecialGroup(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: (\\[((\\[!\\]!.)|((\\\\\\[)|(\\\\\\])))+\\])"); + state.Id--; return success; } @@ -2204,7 +2295,7 @@ private bool MatchPartByTextMatcherSpecialGroup(ref State state, FragmentMatchDa return success ? (true, offset - startOffset) : (false, 0); } - private (bool success, int offset) MatchGroup14(string text, int startOffset) + private (bool success, int offset) MatchGroup15(string text, int startOffset) { int offset = startOffset; bool success; @@ -2226,26 +2317,29 @@ private bool MatchPartByTextMatcherSpecialGroup(ref State state, FragmentMatchDa return (true, offset - startOffset); } - private (bool success, StringMatchData matchData) MatchPatternHexChar(ref State state, bool required, bool readOnly = false) + private bool MatchPatternHexChar(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup14(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchGroup15(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 22 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 23}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -2253,39 +2347,34 @@ private bool MatchPartByTextMatcherSpecialGroup(ref State state, FragmentMatchDa success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 22; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 22; + success = matchData.Id == 23; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup14(state.Code, state.CurrentIndex); + (success, length) = MatchGroup15(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 22 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 23}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -2293,18 +2382,25 @@ private bool MatchPartByTextMatcherSpecialGroup(ref State state, FragmentMatchDa success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternHexChar(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: ~#x([a-f]|[A-F]|[0-9]){1, 4}"); + StringMatchData partMatchData; + bool success = MatchPatternHexChar(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: ~#x([a-f]|[A-F]|[0-9]){1, 4}"); + state.Id--; return success; } @@ -2324,26 +2420,29 @@ private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData ma return success ? (true, offset - startOffset) : (false, 0); } - private (bool success, StringMatchData matchData) MatchPatternNumber(ref State state, bool required, bool readOnly = false) + private bool MatchPatternNumber(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; (success, length) = MatchOneOrMore0(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "Number", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "Number", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 1}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -2351,39 +2450,34 @@ private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData ma success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 1; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 1; + success = matchData.Id == 1; } else { - int length; - int startOffset = state.CurrentIndex; (success, length) = MatchOneOrMore0(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "Number", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "Number", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 1}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -2391,32 +2485,39 @@ private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData ma success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchPartByTextMatcherNumber(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherNumber(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternNumber(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: \\d+"); + StringMatchData partMatchData; + bool success = MatchPatternNumber(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: \\d+"); + state.Id--; return success; } - private bool MatchFragmentPartsOneModeItem(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOneModeItem(ref State state, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - success = MatchPartByTextMatcherDoubleQuoteLiteral(ref state, matchData) || MatchPartByTextMatcherSingleQuoteLiteral(ref state, matchData) || MatchFragmentExpressionGroup(ref state, matchData) || MatchFragmentRepetitionGroup(ref state, matchData) || MatchFragmentOptionalGroup(ref state, matchData) || MatchPartByTextMatcherSpecialGroup(ref state, matchData) || MatchFragmentRuleName(ref state, matchData) || MatchPartByTextMatcherHexChar(ref state, matchData) || MatchPartByTextMatcherNumber(ref state, matchData); + success = MatchPartByTextMatcherDoubleQuoteLiteral(ref state, parentMatchData) || MatchPartByTextMatcherSingleQuoteLiteral(ref state, parentMatchData) || MatchFragmentExpressionGroup(ref state, parentMatchData) || MatchFragmentRepetitionGroup(ref state, parentMatchData) || MatchFragmentOptionalGroup(ref state, parentMatchData) || MatchPartByTextMatcherSpecialGroup(ref state, parentMatchData) || MatchFragmentRuleName(ref state, parentMatchData) || MatchPartByTextMatcherHexChar(ref state, parentMatchData) || MatchPartByTextMatcherNumber(ref state, parentMatchData); if (success) { matchCount++; } - success = false || matchCount > 0; + success = matchCount > 0; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -2425,20 +2526,23 @@ private bool MatchFragmentPartsOneModeItem(ref State state, FragmentMatchData ma return success; } - private bool MatchFragmentItem(ref State state, FragmentMatchData matchData) + private bool MatchFragmentItem(ref State state, FragmentMatchData parentMatchData) { bool success = false; - if (!state.MatchCache.TryGetValue(new ValueTuple("Item", state.CurrentIndex), out FragmentMatchData partMatcherData)) + int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: Item"); + if (!state.MatchCache.TryGetValue(new ValueTuple("Item", state.CurrentIndex), out partMatchData)) { - int startIndex = state.CurrentIndex; int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "Item", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeItem(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "Item", StartIndex = -1}; + success = (MatchFragmentPartsOneModeItem(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - state.MatchCache[new ValueTuple("Item", startIndex)] = partMatcherData; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; + state.MatchCache[new ValueTuple("Item", startIndex)] = partMatchData; } else { @@ -2447,31 +2551,38 @@ private bool MatchFragmentItem(ref State state, FragmentMatchData matchData) state.DistinctIndex = distinctIndex; } } - else if (success = partMatcherData != null) + else if (success = partMatchData != null) { - state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; - state.DistinctIndex = partMatcherData.EndDistinctIndex; + state.CurrentIndex = partMatchData.StartIndex + partMatchData.Length; + state.DistinctIndex = partMatchData.EndDistinctIndex; } if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: Item"); + state.Id--; return success; } - private bool MatchFragmentPartsOneModeZeroOrOneItem(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOneModeZeroOrOneItem(ref State state, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - success = MatchFragmentItem(ref state, matchData); + success = MatchFragmentItem(ref state, parentMatchData); if (success) { matchCount++; } - success = false || matchCount > 0; + success = matchCount > 0; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -2480,7 +2591,7 @@ private bool MatchFragmentPartsOneModeZeroOrOneItem(ref State state, FragmentMat return success; } - private (bool success, int offset) MatchLiteral37(string text, int startOffset) + private (bool success, int offset) MatchLiteral39(string text, int startOffset) { string literal = "?"; int length = literal.Length; @@ -2500,26 +2611,29 @@ private bool MatchFragmentPartsOneModeZeroOrOneItem(ref State state, FragmentMat return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternQuestionMark(ref State state, bool required, bool readOnly = false) + private bool MatchPatternQuestionMark(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral37(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchLiteral39(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "QuestionMark", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 11 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "QuestionMark", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 14}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -2527,39 +2641,34 @@ private bool MatchFragmentPartsOneModeZeroOrOneItem(ref State state, FragmentMat success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 11; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 11; + success = matchData.Id == 14; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral37(state.Code, state.CurrentIndex); + (success, length) = MatchLiteral39(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "QuestionMark", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 11 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "QuestionMark", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 14}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -2567,34 +2676,38 @@ private bool MatchFragmentPartsOneModeZeroOrOneItem(ref State state, FragmentMat success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchFragmentBoundsQuestionMark(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsQuestionMark(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternQuestionMark(ref state, true, readOnly); + int startIndex = state.CurrentIndex; + bool success = MatchPatternQuestionMark(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}. {(success ? "Passed" : "Failed")} Bounds: {"\\?"}"); return success; } - private bool MatchFragmentZeroOrOneItem(ref State state, FragmentMatchData matchData) + private bool MatchFragmentZeroOrOneItem(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: ZeroOrOneItem"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ZeroOrOneItem", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatcherData) && MatchFragmentBoundsQuestionMark(ref state, false, out StringMatchData endMatchData)); + partMatchData = new FragmentMatchData{Name = "ZeroOrOneItem", StartIndex = -1}; + StringMatchData endMatchData = null; + success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatchData) && MatchFragmentBoundsQuestionMark(ref state, partMatchData, false, out endMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -2604,13 +2717,20 @@ private bool MatchFragmentZeroOrOneItem(ref State state, FragmentMatchData match if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: ZeroOrOneItem"); + state.Id--; return success; } - private (bool success, int offset) MatchLiteral36(string text, int startOffset) + private (bool success, int offset) MatchLiteral38(string text, int startOffset) { string literal = "+"; int length = literal.Length; @@ -2630,26 +2750,29 @@ private bool MatchFragmentZeroOrOneItem(ref State state, FragmentMatchData match return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternPlus(ref State state, bool required, bool readOnly = false) + private bool MatchPatternPlus(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral36(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchLiteral38(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "Plus", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 10 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "Plus", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 13}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -2657,39 +2780,34 @@ private bool MatchFragmentZeroOrOneItem(ref State state, FragmentMatchData match success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 10; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 10; + success = matchData.Id == 13; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral36(state.Code, state.CurrentIndex); + (success, length) = MatchLiteral38(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "Plus", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 10 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "Plus", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 13}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -2697,34 +2815,38 @@ private bool MatchFragmentZeroOrOneItem(ref State state, FragmentMatchData match success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchFragmentBoundsPlus(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsPlus(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternPlus(ref state, true, readOnly); + int startIndex = state.CurrentIndex; + bool success = MatchPatternPlus(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}. {(success ? "Passed" : "Failed")} Bounds: {"\\+"}"); return success; } - private bool MatchFragmentOneOrMoreItem(ref State state, FragmentMatchData matchData) + private bool MatchFragmentOneOrMoreItem(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: OneOrMoreItem"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "OneOrMoreItem", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatcherData) && MatchFragmentBoundsPlus(ref state, false, out StringMatchData endMatchData)); + partMatchData = new FragmentMatchData{Name = "OneOrMoreItem", StartIndex = -1}; + StringMatchData endMatchData = null; + success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatchData) && MatchFragmentBoundsPlus(ref state, partMatchData, false, out endMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -2734,13 +2856,20 @@ private bool MatchFragmentOneOrMoreItem(ref State state, FragmentMatchData match if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: OneOrMoreItem"); + state.Id--; return success; } - private (bool success, int offset) MatchLiteral35(string text, int startOffset) + private (bool success, int offset) MatchLiteral37(string text, int startOffset) { string literal = "*"; int length = literal.Length; @@ -2760,26 +2889,29 @@ private bool MatchFragmentOneOrMoreItem(ref State state, FragmentMatchData match return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternAsterisk(ref State state, bool required, bool readOnly = false) + private bool MatchPatternAsterisk(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral35(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchLiteral37(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "Asterisk", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 9 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "Asterisk", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 12}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -2787,39 +2919,34 @@ private bool MatchFragmentOneOrMoreItem(ref State state, FragmentMatchData match success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 9; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 9; + success = matchData.Id == 12; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral35(state.Code, state.CurrentIndex); + (success, length) = MatchLiteral37(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "Asterisk", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 9 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "Asterisk", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 12}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -2827,34 +2954,38 @@ private bool MatchFragmentOneOrMoreItem(ref State state, FragmentMatchData match success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchFragmentBoundsAsterisk(ref State state, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBoundsAsterisk(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) { - bool success; - (success, matchData) = MatchPatternAsterisk(ref state, true, readOnly); + int startIndex = state.CurrentIndex; + bool success = MatchPatternAsterisk(ref state, parentMatchData, out matchData, true, readOnly); if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}. {(success ? "Passed" : "Failed")} Bounds: {"\\*"}"); return success; } - private bool MatchFragmentZeroOrMoreItem(ref State state, FragmentMatchData matchData) + private bool MatchFragmentZeroOrMoreItem(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: ZeroOrMoreItem"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ZeroOrMoreItem", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatcherData) && MatchFragmentBoundsAsterisk(ref state, false, out StringMatchData endMatchData)); + partMatchData = new FragmentMatchData{Name = "ZeroOrMoreItem", StartIndex = -1}; + StringMatchData endMatchData = null; + success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatchData) && MatchFragmentBoundsAsterisk(ref state, partMatchData, false, out endMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -2864,18 +2995,25 @@ private bool MatchFragmentZeroOrMoreItem(ref State state, FragmentMatchData matc if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: ZeroOrMoreItem"); + state.Id--; return success; } - private bool MatchFragmentPartsOneModeExpressionValue(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOneModeExpressionValue(ref State state, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - MatchPatternWhitespace(ref state, false, false); - success = MatchFragmentZeroOrOneItem(ref state, matchData) || MatchFragmentOneOrMoreItem(ref state, matchData) || MatchFragmentZeroOrMoreItem(ref state, matchData) || MatchFragmentItem(ref state, matchData); + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); + success = MatchFragmentZeroOrOneItem(ref state, parentMatchData) || MatchFragmentOneOrMoreItem(ref state, parentMatchData) || MatchFragmentZeroOrMoreItem(ref state, parentMatchData) || MatchFragmentItem(ref state, parentMatchData); if (success) { matchCount++; @@ -2883,10 +3021,10 @@ private bool MatchFragmentPartsOneModeExpressionValue(ref State state, FragmentM if (success) { - MatchPatternWhitespace(ref state, false, false); + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); } - success = false || matchCount > 0; + success = matchCount > 0; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -2895,18 +3033,20 @@ private bool MatchFragmentPartsOneModeExpressionValue(ref State state, FragmentM return success; } - private bool MatchFragmentExpressionValue(ref State state, FragmentMatchData matchData) + private bool MatchFragmentExpressionValue(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: ExpressionValue"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ExpressionValue", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeExpressionValue(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "ExpressionValue", StartIndex = -1}; + success = (MatchFragmentPartsOneModeExpressionValue(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -2916,13 +3056,20 @@ private bool MatchFragmentExpressionValue(ref State state, FragmentMatchData mat if (success) { - matchData.Parts.AddRange(partMatcherData.Parts); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.AddRange(partMatchData.Parts); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: ExpressionValue"); + state.Id--; return success; } - private (bool success, int offset) MatchLiteral34(string text, int startOffset) + private (bool success, int offset) MatchLiteral36(string text, int startOffset) { string literal = "|"; int length = literal.Length; @@ -2942,26 +3089,29 @@ private bool MatchFragmentExpressionValue(ref State state, FragmentMatchData mat return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternOr(ref State state, bool required, bool readOnly = false) + private bool MatchPatternOr(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral34(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchLiteral36(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "Or", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 8 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "Or", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 11}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -2969,39 +3119,34 @@ private bool MatchFragmentExpressionValue(ref State state, FragmentMatchData mat success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 8; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 8; + success = matchData.Id == 11; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral34(state.Code, state.CurrentIndex); + (success, length) = MatchLiteral36(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "Or", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 8 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "Or", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 11}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -3009,30 +3154,37 @@ private bool MatchFragmentExpressionValue(ref State state, FragmentMatchData mat success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchPartByTextMatcherOr(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherOr(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternOr(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: \\|"); + StringMatchData partMatchData; + bool success = MatchPatternOr(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: \\|"); + state.Id--; return success; } - private bool MatchFragmentPartsOrderedModeOrSuffix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOrderedModeOrSuffix(ref State state, FragmentMatchData parentMatchData) { bool success = true; bool partSuccess; int matchCount = 0; StringMatchData stringMatchData = null; int distinctIndex = state.DistinctIndex; - MatchPatternWhitespace(ref state, false, false); - success = MatchPartByTextMatcherOr(ref state, matchData); + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); + success = MatchPartByTextMatcherOr(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -3049,14 +3201,14 @@ private bool MatchFragmentPartsOrderedModeOrSuffix(ref State state, FragmentMatc } distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + partSuccess = MatchPatternWhitespace(ref state, parentMatchData, out stringMatchData, false, false); success = partSuccess; if (!success) { goto Break; } - success = MatchFragmentExpressionValue(ref state, matchData); + success = MatchFragmentExpressionValue(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -3072,13 +3224,13 @@ private bool MatchFragmentPartsOrderedModeOrSuffix(ref State state, FragmentMatc matchCount++; } - Break: - if (success) - { - MatchPatternWhitespace(ref state, false, false); - } + Break: + if (success) + { + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); + } - success = success || 2 <= matchCount; + success = 2 <= matchCount; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -3087,18 +3239,20 @@ private bool MatchFragmentPartsOrderedModeOrSuffix(ref State state, FragmentMatc return success; } - private bool MatchFragmentOrSuffix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentOrSuffix(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: OrSuffix"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "OrSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 4 }; - success = (MatchFragmentPartsOrderedModeOrSuffix(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "OrSuffix", StartIndex = -1, ExpressionOrder = 4}; + success = (MatchFragmentPartsOrderedModeOrSuffix(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -3108,24 +3262,38 @@ private bool MatchFragmentOrSuffix(ref State state, FragmentMatchData matchData) if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: OrSuffix"); + state.Id--; return success; } - private bool MatchPartByTextMatcherAsterisk(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherAsterisk(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternAsterisk(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: \\*"); + StringMatchData partMatchData; + bool success = MatchPatternAsterisk(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: \\*"); + state.Id--; return success; } - private bool MatchFragmentPartsOrderedModeRepetitionSuffix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOrderedModeRepetitionSuffix(ref State state, FragmentMatchData parentMatchData) { bool success = true; bool partSuccess; @@ -3133,7 +3301,7 @@ private bool MatchFragmentPartsOrderedModeRepetitionSuffix(ref State state, Frag StringMatchData stringMatchData = null; int distinctIndex = state.DistinctIndex; ; - success = MatchPartByTextMatcherAsterisk(ref state, matchData); + success = MatchPartByTextMatcherAsterisk(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -3150,14 +3318,14 @@ private bool MatchFragmentPartsOrderedModeRepetitionSuffix(ref State state, Frag } distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + partSuccess = MatchPatternWhitespace(ref state, parentMatchData, out stringMatchData, false, false); success = partSuccess; if (!success) { goto Break; } - success = MatchFragmentExpressionValue(ref state, matchData); + success = MatchFragmentExpressionValue(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -3173,8 +3341,8 @@ private bool MatchFragmentPartsOrderedModeRepetitionSuffix(ref State state, Frag matchCount++; } - Break: - success = success || 2 <= matchCount; + Break: + success = 2 <= matchCount; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -3183,18 +3351,20 @@ private bool MatchFragmentPartsOrderedModeRepetitionSuffix(ref State state, Frag return success; } - private bool MatchFragmentRepetitionSuffix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentRepetitionSuffix(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: RepetitionSuffix"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "RepetitionSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 1 }; - success = (MatchFragmentPartsOrderedModeRepetitionSuffix(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "RepetitionSuffix", StartIndex = -1, ExpressionOrder = 1}; + success = (MatchFragmentPartsOrderedModeRepetitionSuffix(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -3204,32 +3374,42 @@ private bool MatchFragmentRepetitionSuffix(ref State state, FragmentMatchData ma if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: RepetitionSuffix"); + state.Id--; return success; } - private (bool success, StringMatchData matchData) MatchPatternDash(ref State state, bool required, bool readOnly = false) + private bool MatchPatternDash(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral29(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchLiteral31(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 23 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 24}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -3237,39 +3417,34 @@ private bool MatchFragmentRepetitionSuffix(ref State state, FragmentMatchData ma success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 23; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 23; + success = matchData.Id == 24; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral29(state.Code, state.CurrentIndex); + (success, length) = MatchLiteral31(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 23 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 24}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -3277,22 +3452,29 @@ private bool MatchFragmentRepetitionSuffix(ref State state, FragmentMatchData ma success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchPartByTextMatcherDash(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherDash(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternDash(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: -"); + StringMatchData partMatchData; + bool success = MatchPatternDash(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: -"); + state.Id--; return success; } - private bool MatchFragmentPartsOrderedModeExceptSuffix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOrderedModeExceptSuffix(ref State state, FragmentMatchData parentMatchData) { bool success = true; bool partSuccess; @@ -3300,7 +3482,7 @@ private bool MatchFragmentPartsOrderedModeExceptSuffix(ref State state, Fragment StringMatchData stringMatchData = null; int distinctIndex = state.DistinctIndex; ; - success = MatchPartByTextMatcherDash(ref state, matchData); + success = MatchPartByTextMatcherDash(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -3317,14 +3499,14 @@ private bool MatchFragmentPartsOrderedModeExceptSuffix(ref State state, Fragment } distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + partSuccess = MatchPatternWhitespace(ref state, parentMatchData, out stringMatchData, false, false); success = partSuccess; if (!success) { goto Break; } - success = MatchFragmentExpressionValue(ref state, matchData); + success = MatchFragmentExpressionValue(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -3340,8 +3522,8 @@ private bool MatchFragmentPartsOrderedModeExceptSuffix(ref State state, Fragment matchCount++; } - Break: - success = success || 2 <= matchCount; + Break: + success = 2 <= matchCount; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -3350,18 +3532,20 @@ private bool MatchFragmentPartsOrderedModeExceptSuffix(ref State state, Fragment return success; } - private bool MatchFragmentExceptSuffix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentExceptSuffix(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: ExceptSuffix"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ExceptSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 2 }; - success = (MatchFragmentPartsOrderedModeExceptSuffix(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "ExceptSuffix", StartIndex = -1, ExpressionOrder = 2}; + success = (MatchFragmentPartsOrderedModeExceptSuffix(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -3371,9 +3555,16 @@ private bool MatchFragmentExceptSuffix(ref State state, FragmentMatchData matchD if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: ExceptSuffix"); + state.Id--; return success; } @@ -3397,26 +3588,29 @@ private bool MatchFragmentExceptSuffix(ref State state, FragmentMatchData matchD return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternComma(ref State state, bool required, bool readOnly = false) + private bool MatchPatternComma(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; (success, length) = MatchLiteral46(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "Comma", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 21 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "Comma", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 22}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -3424,39 +3618,34 @@ private bool MatchFragmentExceptSuffix(ref State state, FragmentMatchData matchD success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 21; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 21; + success = matchData.Id == 22; } else { - int length; - int startOffset = state.CurrentIndex; (success, length) = MatchLiteral46(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "Comma", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 21 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "Comma", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 22}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -3464,32 +3653,39 @@ private bool MatchFragmentExceptSuffix(ref State state, FragmentMatchData matchD success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchPartByTextMatcherComma(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherComma(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternComma(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: ,"); + StringMatchData partMatchData; + bool success = MatchPatternComma(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: ,"); + state.Id--; return success; } - private bool MatchFragmentPartsOneModeOptionalComma(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOneModeOptionalComma(ref State state, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - success = MatchPartByTextMatcherComma(ref state, matchData); + success = MatchPartByTextMatcherComma(ref state, parentMatchData); if (success) { matchCount++; } - success = true || matchCount > 0; + success = true; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -3498,18 +3694,20 @@ private bool MatchFragmentPartsOneModeOptionalComma(ref State state, FragmentMat return success; } - private bool MatchFragmentOptionalComma(ref State state, FragmentMatchData matchData) + private bool MatchFragmentOptionalComma(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: OptionalComma"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "OptionalComma", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeOptionalComma(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "OptionalComma", StartIndex = -1}; + success = (MatchFragmentPartsOneModeOptionalComma(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -3519,23 +3717,30 @@ private bool MatchFragmentOptionalComma(ref State state, FragmentMatchData match if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: OptionalComma"); + state.Id--; return success; } - private bool MatchFragmentPartsOneModeNotRulePrefix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOneModeNotRulePrefix(ref State state, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - success = MatchFragmentRulePrefix(ref state, matchData); + success = MatchFragmentRulePrefix(ref state, parentMatchData); if (success) { matchCount++; } - success = false || matchCount > 0; + success = matchCount > 0; if (success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -3544,27 +3749,28 @@ private bool MatchFragmentPartsOneModeNotRulePrefix(ref State state, FragmentMat return success; } - private bool MatchFragmentNotRulePrefix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentNotRulePrefix(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: NotRulePrefix"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "NotRulePrefix", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeNotRulePrefix(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "NotRulePrefix", StartIndex = -1}; + success = (MatchFragmentPartsOneModeNotRulePrefix(ref state, partMatchData)); if (success) { } - else - { - } state.CurrentIndex = startIndex; state.DistinctIndex = distinctIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(!success ? "Passed" : "Failed")}: NotRulePrefix"); + state.Id--; return !success; } - private bool MatchFragmentPartsOrderedModeCommaSuffix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOrderedModeCommaSuffix(ref State state, FragmentMatchData parentMatchData) { bool success = true; bool partSuccess; @@ -3572,7 +3778,7 @@ private bool MatchFragmentPartsOrderedModeCommaSuffix(ref State state, FragmentM StringMatchData stringMatchData = null; int distinctIndex = state.DistinctIndex; ; - success = MatchFragmentOptionalComma(ref state, matchData); + success = MatchFragmentOptionalComma(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -3589,14 +3795,14 @@ private bool MatchFragmentPartsOrderedModeCommaSuffix(ref State state, FragmentM } distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + partSuccess = MatchPatternWhitespace(ref state, parentMatchData, out stringMatchData, false, false); success = partSuccess; if (!success) { goto Break; } - success = MatchFragmentNotRulePrefix(ref state, matchData); + success = MatchFragmentNotRulePrefix(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -3613,14 +3819,14 @@ private bool MatchFragmentPartsOrderedModeCommaSuffix(ref State state, FragmentM } distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + partSuccess = MatchPatternWhitespace(ref state, parentMatchData, out stringMatchData, false, false); success = partSuccess; if (!success) { goto Break; } - success = MatchFragmentExpressionValue(ref state, matchData); + success = MatchFragmentExpressionValue(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -3636,8 +3842,8 @@ private bool MatchFragmentPartsOrderedModeCommaSuffix(ref State state, FragmentM matchCount++; } - Break: - success = success || 3 <= matchCount; + Break: + success = 3 <= matchCount; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -3646,18 +3852,20 @@ private bool MatchFragmentPartsOrderedModeCommaSuffix(ref State state, FragmentM return success; } - private bool MatchFragmentCommaSuffix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentCommaSuffix(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: CommaSuffix"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "CommaSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 3 }; - success = (MatchFragmentPartsOrderedModeCommaSuffix(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "CommaSuffix", StartIndex = -1, ExpressionOrder = 3}; + success = (MatchFragmentPartsOrderedModeCommaSuffix(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -3667,13 +3875,20 @@ private bool MatchFragmentCommaSuffix(ref State state, FragmentMatchData matchDa if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: CommaSuffix"); + state.Id--; return success; } - private bool MatchFragmentPartsMultipleModeExpressionSuffix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsMultipleModeExpressionSuffix(ref State state, FragmentMatchData parentMatchData) { bool overallSuccess = false; bool subSuccess = false; @@ -3686,48 +3901,48 @@ private bool MatchFragmentPartsMultipleModeExpressionSuffix(ref State state, Fra { subSuccess = false; bool individualSuccess; - individualSuccess = MatchFragmentOrSuffix(ref state, matchData); + individualSuccess = MatchFragmentOrSuffix(ref state, parentMatchData); subSuccess |= individualSuccess; if (individualSuccess) { matchCount++; distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); + delimiterSuccess = true; goto Break; } - individualSuccess = MatchFragmentRepetitionSuffix(ref state, matchData); + individualSuccess = MatchFragmentRepetitionSuffix(ref state, parentMatchData); subSuccess |= individualSuccess; if (individualSuccess) { matchCount++; distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); + delimiterSuccess = true; goto Break; } - individualSuccess = MatchFragmentExceptSuffix(ref state, matchData); + individualSuccess = MatchFragmentExceptSuffix(ref state, parentMatchData); subSuccess |= individualSuccess; if (individualSuccess) { matchCount++; distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); + delimiterSuccess = true; goto Break; } - individualSuccess = MatchFragmentCommaSuffix(ref state, matchData); + individualSuccess = MatchFragmentCommaSuffix(ref state, parentMatchData); subSuccess |= individualSuccess; if (individualSuccess) { matchCount++; distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); + delimiterSuccess = true; goto Break; } - Break: - overallSuccess |= subSuccess; + Break: + overallSuccess |= subSuccess; } while (subSuccess && delimiterSuccess); if (delimiterSuccess && range != null) @@ -3736,8 +3951,7 @@ private bool MatchFragmentPartsMultipleModeExpressionSuffix(ref State state, Fra state.DistinctIndex = distinctIndex; } - bool thresholdSuccess = 0 <= matchCount; - bool success = overallSuccess || thresholdSuccess; + bool success = 0 <= matchCount; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -3746,18 +3960,20 @@ private bool MatchFragmentPartsMultipleModeExpressionSuffix(ref State state, Fra return success; } - private bool MatchFragmentExpressionSuffix(ref State state, FragmentMatchData matchData) + private bool MatchFragmentExpressionSuffix(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: ExpressionSuffix"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ExpressionSuffix", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsMultipleModeExpressionSuffix(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "ExpressionSuffix", StartIndex = -1}; + success = (MatchFragmentPartsMultipleModeExpressionSuffix(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -3767,21 +3983,28 @@ private bool MatchFragmentExpressionSuffix(ref State state, FragmentMatchData ma if (success) { - matchData.Parts.AddRange(partMatcherData.Parts); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.AddRange(partMatchData.Parts); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: ExpressionSuffix"); + state.Id--; return success; } - private bool MatchFragmentPartsOrderedModeExpression(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOrderedModeExpression(ref State state, FragmentMatchData parentMatchData) { bool success = true; bool partSuccess; int matchCount = 0; StringMatchData stringMatchData = null; int distinctIndex = state.DistinctIndex; - MatchPatternWhitespace(ref state, false, false); - success = MatchFragmentExpressionValue(ref state, matchData); + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); + success = MatchFragmentExpressionValue(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -3798,14 +4021,14 @@ private bool MatchFragmentPartsOrderedModeExpression(ref State state, FragmentMa } distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + partSuccess = MatchPatternWhitespace(ref state, parentMatchData, out stringMatchData, false, false); success = partSuccess; if (!success) { goto Break; } - success = MatchFragmentExpressionSuffix(ref state, matchData); + success = MatchFragmentExpressionSuffix(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -3821,13 +4044,13 @@ private bool MatchFragmentPartsOrderedModeExpression(ref State state, FragmentMa matchCount++; } - Break: - if (success) - { - MatchPatternWhitespace(ref state, false, false); - } + Break: + if (success) + { + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); + } - success = success || 2 <= matchCount; + success = 2 <= matchCount; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -3836,19 +4059,21 @@ private bool MatchFragmentPartsOrderedModeExpression(ref State state, FragmentMa return success; } - private bool MatchFragmentExpression(ref State state, FragmentMatchData matchData) + private bool MatchFragmentExpression(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: Expression"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "Expression", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOrderedModeExpression(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "Expression", StartIndex = -1}; + success = (MatchFragmentPartsOrderedModeExpression(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - ConvertToExpressionTree(partMatcherData, ExpressionMode.LikeNameTree); + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; + ConvertToExpressionTree(partMatchData, ExpressionMode.LikeNameTree); } else { @@ -3858,13 +4083,20 @@ private bool MatchFragmentExpression(ref State state, FragmentMatchData matchDat if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: Expression"); + state.Id--; return success; } - private (bool success, int offset) MatchLiteral38(string text, int startOffset) + private (bool success, int offset) MatchLiteral40(string text, int startOffset) { string literal = ";"; int length = literal.Length; @@ -3884,26 +4116,29 @@ private bool MatchFragmentExpression(ref State state, FragmentMatchData matchDat return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternSemicolon(ref State state, bool required, bool readOnly = false) + private bool MatchPatternSemicolon(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral38(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchLiteral40(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "Semicolon", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 12 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "Semicolon", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 15}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -3911,39 +4146,34 @@ private bool MatchFragmentExpression(ref State state, FragmentMatchData matchDat success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 12; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 12; + success = matchData.Id == 15; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral38(state.Code, state.CurrentIndex); + (success, length) = MatchLiteral40(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "Semicolon", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 12 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "Semicolon", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 15}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -3951,22 +4181,29 @@ private bool MatchFragmentExpression(ref State state, FragmentMatchData matchDat success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchPartByTextMatcherSemicolon(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherSemicolon(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternSemicolon(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: ;"); + StringMatchData partMatchData; + bool success = MatchPatternSemicolon(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: ;"); + state.Id--; return success; } - private (bool success, int offset) MatchLiteral39(string text, int startOffset) + private (bool success, int offset) MatchLiteral41(string text, int startOffset) { string literal = "."; int length = literal.Length; @@ -3986,26 +4223,29 @@ private bool MatchPartByTextMatcherSemicolon(ref State state, FragmentMatchData return (true, length); } - private (bool success, StringMatchData matchData) MatchPatternPeriod(ref State state, bool required, bool readOnly = false) + private bool MatchPatternPeriod(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral39(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchLiteral41(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "Period", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 13 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "Period", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 16}; + state.DistinctStringMatches.Add(matchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -4013,39 +4253,34 @@ private bool MatchPartByTextMatcherSemicolon(ref State state, FragmentMatchData success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 13; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 13; + success = matchData.Id == 16; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral39(state.Code, state.CurrentIndex); + (success, length) = MatchLiteral41(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "Period", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 13 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "Period", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 16}; + state.DistinctStringMatches[distinctIndex] = matchData; } } if (success && !readOnly) { state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -4053,32 +4288,39 @@ private bool MatchPartByTextMatcherSemicolon(ref State state, FragmentMatchData success = true; } - return (success, stringMatchData); + return success; } } - private bool MatchPartByTextMatcherPeriod(ref State state, FragmentMatchData matchData) + private bool MatchPartByTextMatcherPeriod(ref State state, FragmentMatchData parentMatchData) { - (bool success, StringMatchData partMatchData) = MatchPatternPeriod(ref state, true, false); + state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: \\."); + StringMatchData partMatchData; + bool success = MatchPatternPeriod(ref state, parentMatchData, out partMatchData, true, false); if (success) { - matchData.Parts.Add(partMatchData); + parentMatchData.Parts.Add(partMatchData); + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id + 1)} {partMatchData?.StartIndex ?? startIndex}. Matched: {partMatchData.Text}"); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: \\."); + state.Id--; return success; } - private bool MatchFragmentPartsOneModeEndMark(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOneModeEndMark(ref State state, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - success = MatchPartByTextMatcherSemicolon(ref state, matchData) || MatchPartByTextMatcherPeriod(ref state, matchData); + success = MatchPartByTextMatcherSemicolon(ref state, parentMatchData) || MatchPartByTextMatcherPeriod(ref state, parentMatchData); if (success) { matchCount++; } - success = false || matchCount > 0; + success = matchCount > 0; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -4087,18 +4329,20 @@ private bool MatchFragmentPartsOneModeEndMark(ref State state, FragmentMatchData return success; } - private bool MatchFragmentEndMark(ref State state, FragmentMatchData matchData) + private bool MatchFragmentEndMark(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: EndMark"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "EndMark", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeEndMark(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "EndMark", StartIndex = -1}; + success = (MatchFragmentPartsOneModeEndMark(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -4108,12 +4352,18 @@ private bool MatchFragmentEndMark(ref State state, FragmentMatchData matchData) if (success) { + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: EndMark"); + state.Id--; return success; } - private bool MatchFragmentPartsOrderedModeRule(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsOrderedModeRule(ref State state, FragmentMatchData parentMatchData) { bool success = true; bool partSuccess; @@ -4121,7 +4371,7 @@ private bool MatchFragmentPartsOrderedModeRule(ref State state, FragmentMatchDat StringMatchData stringMatchData = null; int distinctIndex = state.DistinctIndex; ; - success = MatchFragmentRulePrefix(ref state, matchData); + success = MatchFragmentRulePrefix(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -4138,14 +4388,14 @@ private bool MatchFragmentPartsOrderedModeRule(ref State state, FragmentMatchDat } distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + partSuccess = MatchPatternWhitespace(ref state, parentMatchData, out stringMatchData, false, false); success = partSuccess; if (!success) { goto Break; } - success = MatchFragmentExpression(ref state, matchData); + success = MatchFragmentExpression(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -4162,14 +4412,14 @@ private bool MatchFragmentPartsOrderedModeRule(ref State state, FragmentMatchDat } distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + partSuccess = MatchPatternWhitespace(ref state, parentMatchData, out stringMatchData, false, false); success = partSuccess; if (!success) { goto Break; } - success = MatchFragmentEndMark(ref state, matchData); + success = MatchFragmentEndMark(ref state, parentMatchData); if (!success) { if (stringMatchData != null) @@ -4185,8 +4435,8 @@ private bool MatchFragmentPartsOrderedModeRule(ref State state, FragmentMatchDat matchCount++; } - Break: - success = success || 2 <= matchCount; + Break: + success = 2 <= matchCount; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -4195,18 +4445,20 @@ private bool MatchFragmentPartsOrderedModeRule(ref State state, FragmentMatchDat return success; } - private bool MatchFragmentRule(ref State state, FragmentMatchData matchData) + private bool MatchFragmentRule(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: Rule"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "Rule", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOrderedModeRule(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "Rule", StartIndex = -1}; + success = (MatchFragmentPartsOrderedModeRule(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -4216,14 +4468,20 @@ private bool MatchFragmentRule(ref State state, FragmentMatchData matchData) if (success) { - matchData.Parts.Add(partMatcherData); - state.MatchCache.Clear(); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); } + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: Rule"); + state.Id--; return success; } - private bool MatchFragmentPartsMultipleModeRules(ref State state, FragmentMatchData matchData) + private bool MatchFragmentPartsMultipleModeRules(ref State state, FragmentMatchData parentMatchData) { bool overallSuccess = false; bool subSuccess = false; @@ -4231,23 +4489,23 @@ private bool MatchFragmentPartsMultipleModeRules(ref State state, FragmentMatchD StringMatchData range = default; int matchCount = 0; int distinctIndex = state.DistinctIndex; - MatchPatternWhitespace(ref state, false, false); + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); do { subSuccess = false; bool individualSuccess; - individualSuccess = MatchFragmentRule(ref state, matchData); + individualSuccess = MatchFragmentRule(ref state, parentMatchData); subSuccess |= individualSuccess; if (individualSuccess) { matchCount++; distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = MatchPatternWhitespace(ref state, false, false); + delimiterSuccess = MatchPatternWhitespace(ref state, parentMatchData, out range, false, false); goto Break; } - Break: - overallSuccess |= subSuccess; + Break: + overallSuccess |= subSuccess; } while (subSuccess && delimiterSuccess); if (delimiterSuccess && range != null) @@ -4258,11 +4516,10 @@ private bool MatchFragmentPartsMultipleModeRules(ref State state, FragmentMatchD if (overallSuccess) { - MatchPatternWhitespace(ref state, false, false); + MatchPatternWhitespace(ref state, parentMatchData, out _, false, false); } - bool thresholdSuccess = 1 <= matchCount; - bool success = overallSuccess || thresholdSuccess; + bool success = 1 <= matchCount; if (!success) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -4271,18 +4528,20 @@ private bool MatchFragmentPartsMultipleModeRules(ref State state, FragmentMatchD return success; } - private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) + private bool MatchFragmentRules(ref State state, FragmentMatchData parentMatchData) { bool success = false; - FragmentMatchData partMatcherData = null; int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + state.Id++; + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {startIndex}. Try: Rules"); int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "Rules", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsMultipleModeRules(ref state, partMatcherData)); + partMatchData = new FragmentMatchData{Name = "Rules", StartIndex = -1}; + success = (MatchFragmentPartsMultipleModeRules(ref state, partMatchData)); if (success) { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; } else { @@ -4292,9 +4551,49 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) if (success) { - matchData.Parts.Add(partMatcherData); + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } + + parentMatchData.Parts.Add(partMatchData); + } + + state.MatchLogBuilder.AppendLine($"{new String('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: Rules"); + state.Id--; + return success; + } + + private bool MatchPatternOptionalGroup(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) + { + bool success = false; + int startOffset = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + int maxDistinctIndex = state.MaxDistinctIndex; + StringMatchData lastDistinctMatchData = state.DistinctStringMatches.LastOrDefault(); + int cacheIndex = lastDistinctMatchData != null ? lastDistinctMatchData.StartIndex + lastDistinctMatchData.Length : 0; + FragmentMatchData partMatchData; + partMatchData = new FragmentMatchData{Name = "OptionalGroup", StartIndex = -1}; + StringMatchData startMatchData; + StringMatchData endMatchData = null; + success = (MatchFragmentBoundsOpenBracket(ref state, partMatchData, false, out startMatchData) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatchData) && MatchFragmentBoundsCloseBracket(ref state, partMatchData, false, out endMatchData)); + if (success) + { + partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex; + state.MatchCache[new ValueTuple("OptionalGroup", cacheIndex)] = partMatchData; + partMatchData.EndDistinctIndex = distinctIndex; } + state.DistinctStringMatches.RemoveRange(distinctIndex, state.DistinctIndex - distinctIndex); + state.DistinctIndex = distinctIndex; + state.MaxDistinctIndex = maxDistinctIndex; + if (!success) + { + state.CurrentIndex = startOffset; + } + + matchData = null; return success; } @@ -4308,7 +4607,7 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) int offset = startOffset; bool success; int subOffset; - (success, subOffset) = MatchLiteral35(text, offset); + (success, subOffset) = MatchLiteral37(text, offset); if (!success) { return (false, 0); @@ -4358,7 +4657,7 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) } offset += subOffset; - (success, subOffset) = MatchLiteral35(text, offset); + (success, subOffset) = MatchLiteral37(text, offset); if (!success) { return (false, 0); @@ -4372,7 +4671,7 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) } offset += subOffset; - (success, subOffset) = MatchLiteral35(text, offset); + (success, subOffset) = MatchLiteral37(text, offset); if (!success) { return (false, 0); @@ -4389,7 +4688,7 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) return (true, offset - startOffset); } - private (bool success, int offset) MatchLiteral20(string text, int startOffset) + private (bool success, int offset) MatchLiteral22(string text, int startOffset) { string literal = "/"; int length = literal.Length; @@ -4409,7 +4708,7 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) return (true, length); } - private (bool success, int offset) MatchLiteral22(string text, int startOffset) + private (bool success, int offset) MatchLiteral24(string text, int startOffset) { string literal = "*/"; int length = literal.Length; @@ -4431,7 +4730,7 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) private (bool success, int offset) MatchNot5(string text, int startOffset) { - return text.Length > startOffset && !MatchLiteral22(text, startOffset).success ? (true, 0) : (false, 0); + return text.Length > startOffset && !MatchLiteral24(text, startOffset).success ? (true, 0) : (false, 0); } private (bool success, int offset) MatchGroup10(string text, int startOffset) @@ -4475,14 +4774,14 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) int offset = startOffset; bool success; int subOffset; - (success, subOffset) = MatchLiteral20(text, offset); + (success, subOffset) = MatchLiteral22(text, offset); if (!success) { return (false, 0); } offset += subOffset; - (success, subOffset) = MatchLiteral35(text, offset); + (success, subOffset) = MatchLiteral37(text, offset); if (!success) { return (false, 0); @@ -4496,7 +4795,7 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) } offset += subOffset; - (success, subOffset) = MatchLiteral22(text, offset); + (success, subOffset) = MatchLiteral24(text, offset); if (!success) { return (false, 0); @@ -4517,29 +4816,70 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) } (success, subOffset) = MatchGroup11(text, startOffset); - return success ? (true, subOffset) : (false, 0); + if (success) + { + return (true, subOffset); + } + + return (false, 0); + } + + private (bool success, int offset) MatchWildcard4(string text, int startOffset) + { + int offset = startOffset; + bool success = true; + int subOffset; + while (success) + { + (success, subOffset) = MatchWhitespace(text, offset); + offset += subOffset; + } + + return (true, offset - startOffset); } - private (bool success, StringMatchData matchData) MatchPatternComment(ref State state, bool required, bool readOnly = false) + private (bool success, int offset) MatchGroup12(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchOr2(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchWildcard4(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private bool MatchPatternComment(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) { - Rerun: bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) { int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOr2(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + (success, length) = MatchGroup12(state.Code, state.CurrentIndex); + matchData = default; if (success) { - stringMatchData = new StringMatchData { Name = "Comment", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = true, Id = 5 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; + matchData = new StringMatchData{Name = "Comment", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 8}; if (!readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = startOffset + length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -4547,39 +4887,32 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) success = true; } - return (success, stringMatchData); + return success; } else { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) + int length; + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) { - success = stringMatchData.Id != 5; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 5; + success = matchData.Id == 8; } else { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOr2(state.Code, state.CurrentIndex); + (success, length) = MatchGroup12(state.Code, state.CurrentIndex); if (success) { - stringMatchData = new StringMatchData { Name = "Comment", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = true, Id = 5 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; + matchData = new StringMatchData{Name = "Comment", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, Id = 8}; } } if (success && !readOnly) { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + { + fragmentMatchData.StartIndex = matchData.StartIndex; + } } if (!required) @@ -4587,7 +4920,7 @@ private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) success = true; } - return (success, stringMatchData); + return success; } } } diff --git a/src/Matcher.Interop.Ebnf/Matcher.Interop.Ebnf.csproj b/src/Matcher.Interop.Ebnf/Matcher.Interop.Ebnf.csproj index 1231242..cb689f5 100644 --- a/src/Matcher.Interop.Ebnf/Matcher.Interop.Ebnf.csproj +++ b/src/Matcher.Interop.Ebnf/Matcher.Interop.Ebnf.csproj @@ -4,7 +4,7 @@ netstandard2.0 Synfron.Staxe.Matcher.Interop.Bnf Synfron.Staxe.Matcher.Interop.Bnf - 2.0.0 + 2.0.1 Daquanne Dwight Synfron © Daquanne Dwight diff --git a/src/Matcher.Interop.Model/DefinitionConverter.cs b/src/Matcher.Interop.Model/DefinitionConverter.cs index ebc29e4..fb8992e 100644 --- a/src/Matcher.Interop.Model/DefinitionConverter.cs +++ b/src/Matcher.Interop.Model/DefinitionConverter.cs @@ -50,7 +50,7 @@ private static IList ProcessMatcherActions(LanguageMatcherDefinition lan Dictionary createVariableMap = new Dictionary(); foreach (MatcherActionDefinition actionDefinition in languageMatcherModel.Actions) { - if (actionDefinition.ActionType == MatcherActionType.CreateVariable) + if (actionDefinition.Action == MatcherActionType.CreateVariable) { CreateVariableMatcherAction action = new CreateVariableMatcherAction { @@ -68,7 +68,7 @@ private static IList ProcessMatcherActions(LanguageMatcherDefinition lan foreach (MatcherActionDefinition actionDefinition in languageMatcherModel.Actions) { MatcherAction action = null; - switch (actionDefinition.ActionType) + switch (actionDefinition.Action) { case MatcherActionType.Assert: { @@ -97,7 +97,7 @@ private static IList ProcessMatcherActions(LanguageMatcherDefinition lan case MatcherActionType.CreateVariable: break; default: - throw new InvalidOperationException($"Action type {actionDefinition.ActionType} is not supported"); + throw new InvalidOperationException($"Action type {actionDefinition.Action} is not supported"); } } } @@ -117,7 +117,6 @@ private static List ProcessPatternMatchers(LanguageMatcherDefini { PatternMatcher patternMatcher = isEager ? PatternReader.Parse(patternIndex + 1, model.Name, model.Pattern) : PatternReader.LazyParse(patternIndex + 1, model.Name, model.Pattern); patternMatcher.IsNoise = model.IsNoise; - patternMatcher.Mergable = model.Mergable; patternMatcherMap.Add(patternMatcher.Name, patternMatcher); if (!model.IsAuxiliary) { @@ -245,7 +244,6 @@ PatternMatcherDefinition ConvertPattern(PatternMatcher patternMatcher, bool isAu { IsAuxiliary = isAuxilary, IsNoise = patternMatcher.IsNoise, - Mergable = patternMatcher.Mergable, Name = patternMatcher.Name, Pattern = patternMatcher is GroupPatternMatcher groupPatternMatcher ? groupPatternMatcher.ToString(true) : patternMatcher.ToString() }; @@ -258,7 +256,7 @@ MatcherActionDefinition ConvertMatcherAction(MatcherAction matcherAction) case UpdateVariableMatcherAction updateVariableMatcherAction: return new MatcherActionDefinition { - ActionType = updateVariableMatcherAction.ActionType, + Action = updateVariableMatcherAction.Action, Name = updateVariableMatcherAction.Name, Change = updateVariableMatcherAction.Change, FirstVariableName = languageMatcher.Blobs[updateVariableMatcherAction.TargetBlobId], @@ -267,7 +265,7 @@ MatcherActionDefinition ConvertMatcherAction(MatcherAction matcherAction) case CreateVariableMatcherAction createVariableMatcherAction: return new MatcherActionDefinition { - ActionType = createVariableMatcherAction.ActionType, + Action = createVariableMatcherAction.Action, Name = createVariableMatcherAction.Name, Source = createVariableMatcherAction.Source, FirstVariableName = languageMatcher.Blobs[createVariableMatcherAction.BlobId], @@ -276,7 +274,7 @@ MatcherActionDefinition ConvertMatcherAction(MatcherAction matcherAction) case AssertMatcherAction assertMatcherAction: return new MatcherActionDefinition { - ActionType = assertMatcherAction.ActionType, + Action = assertMatcherAction.Action, Name = assertMatcherAction.Name, Assert = assertMatcherAction.Assert, FirstVariableName = languageMatcher.Blobs[assertMatcherAction.FirstBlobId], diff --git a/src/Matcher.Interop.Model/Matcher.Interop.Model.csproj b/src/Matcher.Interop.Model/Matcher.Interop.Model.csproj index 9682605..e9a871a 100644 --- a/src/Matcher.Interop.Model/Matcher.Interop.Model.csproj +++ b/src/Matcher.Interop.Model/Matcher.Interop.Model.csproj @@ -4,7 +4,7 @@ netstandard2.0 Synfron.Staxe.Matcher.Interop.Model Synfron.Staxe.Matcher.Interop.Model - 2.0.0 + 3.0.0 Daquanne Dwight Synfron © Daquanne Dwight diff --git a/src/Matcher.Interop.Model/MatcherActionDefinition.cs b/src/Matcher.Interop.Model/MatcherActionDefinition.cs index 39ca2b5..8c6a2d7 100644 --- a/src/Matcher.Interop.Model/MatcherActionDefinition.cs +++ b/src/Matcher.Interop.Model/MatcherActionDefinition.cs @@ -1,4 +1,5 @@ using Synfron.Staxe.Matcher.Input.Actions; +using System; namespace Synfron.Staxe.Matcher.Interop.Model { @@ -6,7 +7,7 @@ public class MatcherActionDefinition { public string Name { get; set; } - public MatcherActionType ActionType { get; set; } + public MatcherActionType Action { get; set; } public string FirstVariableName { get; set; } @@ -18,6 +19,6 @@ public class MatcherActionDefinition public AssertType Assert { get; set; } - public object Value { get; set; } + public IConvertible Value { get; set; } } } diff --git a/src/Matcher.Interop.Model/PatternMatcherDefinition.cs b/src/Matcher.Interop.Model/PatternMatcherDefinition.cs index 54ab979..baf2b99 100644 --- a/src/Matcher.Interop.Model/PatternMatcherDefinition.cs +++ b/src/Matcher.Interop.Model/PatternMatcherDefinition.cs @@ -8,8 +8,6 @@ public class PatternMatcherDefinition public bool IsNoise { get; set; } - public bool Mergable { get; set; } - public string Fragment { get; set; } public bool IsAuxiliary { get; set; } diff --git a/src/Matcher/AbstractLanguageMatchEngine.cs b/src/Matcher/AbstractLanguageMatchEngine.cs index 6cadcc3..e96b94c 100644 --- a/src/Matcher/AbstractLanguageMatchEngine.cs +++ b/src/Matcher/AbstractLanguageMatchEngine.cs @@ -13,7 +13,6 @@ public abstract class AbstractLanguageMatchEngine : ILanguageMatchEngine protected ref struct State { public int CurrentIndex; - public int MaxIndex; public List DistinctStringMatches; public int Id; public StringBuilder MatchLogBuilder; diff --git a/src/Matcher/Data/Extensions.cs b/src/Matcher/Data/Extensions.cs index a2d01fc..e2153ba 100644 --- a/src/Matcher/Data/Extensions.cs +++ b/src/Matcher/Data/Extensions.cs @@ -4,7 +4,7 @@ namespace Synfron.Staxe.Matcher.Data { - internal static class Extensions + public static class Extensions { public static int GetEndIndex(this IMatchData matchData) diff --git a/src/Matcher/Data/StringMatchData.cs b/src/Matcher/Data/StringMatchData.cs index 96d4f22..3adbe90 100644 --- a/src/Matcher/Data/StringMatchData.cs +++ b/src/Matcher/Data/StringMatchData.cs @@ -12,10 +12,6 @@ public class StringMatchData : IMatchData public string Name { get; set; } - public bool IsNoise { get; set; } - - public bool Mergable { get; set; } - public int Id { get; set; } public string ToXml() diff --git a/src/Matcher/Input/Actions/AssertMatcherAction.cs b/src/Matcher/Input/Actions/AssertMatcherAction.cs index ff1e483..371e246 100644 --- a/src/Matcher/Input/Actions/AssertMatcherAction.cs +++ b/src/Matcher/Input/Actions/AssertMatcherAction.cs @@ -15,7 +15,7 @@ public class AssertMatcherAction : MatcherAction public AssertType Assert { get; set; } - public override MatcherActionType ActionType => MatcherActionType.Assert; + public override MatcherActionType Action => MatcherActionType.Assert; public override bool Perform(Span blobDatas, IList matchDatas) { @@ -27,26 +27,26 @@ public override bool Perform(Span blobDatas, IList matchDa return !Equals(blobDatas[FirstBlobId].Value?.ToString(), blobDatas[SecondBlobId].Value?.ToString()); case AssertType.GreaterThan: { - return double.TryParse(blobDatas[FirstBlobId].Value.ToString(), out double num1) - && double.TryParse(blobDatas[SecondBlobId].Value.ToString(), out double num2) ? + return double.TryParse(blobDatas[FirstBlobId].Value?.ToString(), out double num1) + && double.TryParse(blobDatas[SecondBlobId].Value?.ToString(), out double num2) ? num1 > num2 : false; } case AssertType.GreaterThanOrEquals: { - return double.TryParse(blobDatas[FirstBlobId].Value.ToString(), out double num1) - && double.TryParse(blobDatas[SecondBlobId].Value.ToString(), out double num2) ? + return double.TryParse(blobDatas[FirstBlobId].Value?.ToString(), out double num1) + && double.TryParse(blobDatas[SecondBlobId].Value?.ToString(), out double num2) ? num1 >= num2 : false; } case AssertType.LessThan: { - return double.TryParse(blobDatas[FirstBlobId].Value.ToString(), out double num1) - && double.TryParse(blobDatas[SecondBlobId].Value.ToString(), out double num2) ? + return double.TryParse(blobDatas[FirstBlobId].Value?.ToString(), out double num1) + && double.TryParse(blobDatas[SecondBlobId].Value?.ToString(), out double num2) ? num1 < num2 : false; } case AssertType.LessThanOrEquals: { - return double.TryParse(blobDatas[FirstBlobId].Value.ToString(), out double num1) - && double.TryParse(blobDatas[SecondBlobId].Value.ToString(), out double num2) ? + return double.TryParse(blobDatas[FirstBlobId].Value?.ToString(), out double num1) + && double.TryParse(blobDatas[SecondBlobId].Value?.ToString(), out double num2) ? num1 <= num2 : false; } case AssertType.Contains: @@ -71,10 +71,10 @@ internal override string Generate(MatcherEngineGenerator generator) string code = $@"private bool {methodName}(Span blobDatas, IList matchDatas) {{ {(Assert == AssertType.Equals ? $@" - return Equals(blobDatas[{FirstBlobId}]?.Value, blobDatas[{SecondBlobId}]?.Value); + return Equals(blobDatas[{FirstBlobId}].Value?.ToString(), blobDatas[{SecondBlobId}].Value?.ToString()); " : null)} {(Assert == AssertType.NotEquals ? $@" - return !Equals(blobDatas[{FirstBlobId}]?.Value, blobDatas[{SecondBlobId}]?.Value); + return !Equals(blobDatas[{FirstBlobId}].Value?.ToString(), blobDatas[{SecondBlobId}].Value?.ToString()); " : null)} {(Assert == AssertType.GreaterThan ? $@" return double.TryParse(blobDatas[{FirstBlobId}].Value?.ToString(), out double num1) diff --git a/src/Matcher/Input/Actions/CreateVariableMatcherAction.cs b/src/Matcher/Input/Actions/CreateVariableMatcherAction.cs index b07e8e5..1caf77a 100644 --- a/src/Matcher/Input/Actions/CreateVariableMatcherAction.cs +++ b/src/Matcher/Input/Actions/CreateVariableMatcherAction.cs @@ -1,6 +1,7 @@ using Synfron.Staxe.Matcher.Data; using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace Synfron.Staxe.Matcher.Input.Actions @@ -11,9 +12,9 @@ public class CreateVariableMatcherAction : MatcherAction public VariableValueSource Source { get; set; } - public object Value { get; set; } + public IConvertible Value { get; set; } - public override MatcherActionType ActionType => MatcherActionType.CreateVariable; + public override MatcherActionType Action => MatcherActionType.CreateVariable; public override bool Perform(Span blobDatas, IList matchDatas) { @@ -25,10 +26,10 @@ public override bool Perform(Span blobDatas, IList matchDa Value = Value }; break; - case VariableValueSource.PartsText: + case VariableValueSource.PartsXml: blobDatas[BlobId] = new BlobData { - Value = matchDatas.GetText(true) + Value = string.Join("", matchDatas.Select(matchData => matchData.ToXml())) }; break; case VariableValueSource.PartsLength: @@ -71,13 +72,13 @@ internal override string Generate(MatcherEngineGenerator generator) {(Source == VariableValueSource.Value ? $@" blobDatas[{BlobId}] = new BlobData {{ - Value = Value + Value = {(Value is string str ? $"\"{str}\"" : Value)} }}; " : null)} - {(Source == VariableValueSource.PartsText ? $@" + {(Source == VariableValueSource.PartsXml ? $@" blobDatas[{BlobId}] = new BlobData {{ - Value = GetText(matchDatas) + Value = string.Join("", matchDatas.Select(matchData => matchData.ToXml())) }}; " : null)} {(Source == VariableValueSource.PartsLength ? $@" diff --git a/src/Matcher/Input/Actions/MatcherAction.cs b/src/Matcher/Input/Actions/MatcherAction.cs index fefcf3e..d14ae50 100644 --- a/src/Matcher/Input/Actions/MatcherAction.cs +++ b/src/Matcher/Input/Actions/MatcherAction.cs @@ -10,7 +10,7 @@ public abstract class MatcherAction { public string Name { get; set; } - public abstract MatcherActionType ActionType { get; } + public abstract MatcherActionType Action { get; } public abstract bool Perform(Span blobDatas, IList matchDatas); diff --git a/src/Matcher/Input/Actions/UpdateVariableMatcherAction.cs b/src/Matcher/Input/Actions/UpdateVariableMatcherAction.cs index 7c8cf72..c1749ce 100644 --- a/src/Matcher/Input/Actions/UpdateVariableMatcherAction.cs +++ b/src/Matcher/Input/Actions/UpdateVariableMatcherAction.cs @@ -13,7 +13,7 @@ public class UpdateVariableMatcherAction : MatcherAction public int SourceBlobId { get; set; } - public override MatcherActionType ActionType => MatcherActionType.UpdateVariable; + public override MatcherActionType Action => MatcherActionType.UpdateVariable; public override bool Perform(Span blobDatas, IList matchDatas) { diff --git a/src/Matcher/Input/Actions/VariableValueSource.cs b/src/Matcher/Input/Actions/VariableValueSource.cs index e9ea409..5b7e646 100644 --- a/src/Matcher/Input/Actions/VariableValueSource.cs +++ b/src/Matcher/Input/Actions/VariableValueSource.cs @@ -8,7 +8,7 @@ public enum VariableValueSource { Value, PartsCount, - PartsText, + PartsXml, PartsLength, StringPartsText, StringPartsLength diff --git a/src/Matcher/Input/FallThroughMode.cs b/src/Matcher/Input/FallThroughMode.cs index 36ed5b2..60d5198 100644 --- a/src/Matcher/Input/FallThroughMode.cs +++ b/src/Matcher/Input/FallThroughMode.cs @@ -11,4 +11,12 @@ public enum FallThroughMode One = 1, All = int.MaxValue } + + public static class FallThroughModeExtensions + { + public static bool IsCountBased(this FallThroughMode mode) + { + return FallThroughMode.None < mode && mode < FallThroughMode.All; + } + } } diff --git a/src/Matcher/Input/Patterns/PatternMatcher.cs b/src/Matcher/Input/Patterns/PatternMatcher.cs index e7a9c08..b358c99 100644 --- a/src/Matcher/Input/Patterns/PatternMatcher.cs +++ b/src/Matcher/Input/Patterns/PatternMatcher.cs @@ -12,8 +12,6 @@ public abstract class PatternMatcher : IMatcher public bool IsNoise { get; set; } - public bool Mergable { get; set; } - public bool IsStandard { get; set; } = true; public abstract (bool success, int offset) IsMatch(string text, int startOffset = 0); diff --git a/src/Matcher/LanguageMatchEngine.cs b/src/Matcher/LanguageMatchEngine.cs index 0add4e4..04fe541 100644 --- a/src/Matcher/LanguageMatchEngine.cs +++ b/src/Matcher/LanguageMatchEngine.cs @@ -38,7 +38,6 @@ private bool PreMatchPatterns(ref State state) IList patterns = LanguageMatcher.Patterns; int patternsCount = LanguageMatcher.Patterns.Count; bool success = true; - bool previousNoise = false; int currentIndex; while ((currentIndex = state.CurrentIndex) < codeLength) @@ -49,35 +48,12 @@ private bool PreMatchPatterns(ref State state) PatternMatcher pattern = patterns[patternIndex]; StringMatchData matchData; (success, matchData) = PreMatchPattern(ref state, pattern); + if (matchData != null && _logMatches) + { + state.MatchLogBuilder.AppendLine($"{currentIndex}. Prematched {matchData.Name}: {matchData.Text}"); + } if (success) { - if (pattern.IsStandard) - { - if (_logMatches) - { - state.MatchLogBuilder.AppendLine($"{currentIndex}. Prematched {matchData.Name}: {matchData.Text}"); - } - if (matchData.IsNoise) - { - previousNoise = true; - } - else if (previousNoise) - { - if (state.DistinctIndex > 1) - { - StringMatchData previousMatchData = state.DistinctStringMatches[state.DistinctIndex - 2]; - if (previousMatchData.Name == matchData.Name && previousMatchData.Mergable) - { - previousMatchData.Text += matchData.Text; - previousMatchData.Length = state.CurrentIndex - previousMatchData.StartIndex; - state.DistinctIndex--; - state.MaxDistinctIndex--; - state.DistinctStringMatches.RemoveAt(state.DistinctIndex); - } - } - previousNoise = false; - } - } break; } } @@ -106,42 +82,48 @@ private bool PreMatchPatterns(ref State state) Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, - Id = pattern.Id, - IsNoise = pattern.IsNoise, - Mergable = pattern.Mergable + Id = pattern.Id }; - if (!stringMatchData.IsNoise) + if (!pattern.IsNoise) { state.DistinctStringMatches.Add(stringMatchData); state.DistinctIndex++; state.MaxDistinctIndex++; } state.CurrentIndex = startOffset + length; - state.MaxIndex = state.CurrentIndex; } return (success, stringMatchData); } else if (pattern is FragmentPatternMatcher fragmentPatternMatcher && fragmentPatternMatcher.Fragment is FragmentMatcher fragmentMatcher) { int startIndex = state.CurrentIndex; - int distinctStringMatchesCount = state.DistinctStringMatches.Count; + StringMatchData lastDistinctMatchData = state.DistinctStringMatches.LastOrDefault(); + int cacheIndex = lastDistinctMatchData != null ? + lastDistinctMatchData.StartIndex + lastDistinctMatchData.Length : + 0; int distinctIndex = state.DistinctIndex; int maxDistinctIndex = state.MaxDistinctIndex; - (bool _, FragmentMatchData partMatcherData) = MatchByFragmentMatcher(ref state, fragmentPatternMatcher.Fragment); - if (fragmentMatcher.Cacheable) + (bool fragmentSuccess, FragmentMatchData partMatchData) = MatchByFragmentMatcher(ref state, fragmentPatternMatcher.Fragment); + if (fragmentSuccess && fragmentMatcher.Cacheable) { - state.MatchCache[new ValueTuple(fragmentMatcher.Name, startIndex)] = partMatcherData; + state.MatchCache[new ValueTuple(fragmentMatcher.Name, cacheIndex)] = partMatchData; } - if (pattern.IsNoise) + if (!fragmentSuccess || fragmentPatternMatcher.Fragment.Negate || pattern.IsNoise) { - state.DistinctStringMatches.RemoveRange(distinctStringMatchesCount, state.DistinctStringMatches.Count - distinctStringMatchesCount); + state.DistinctStringMatches.RemoveRange(distinctIndex, state.DistinctIndex - distinctIndex); state.DistinctIndex = distinctIndex; state.MaxDistinctIndex = maxDistinctIndex; + if (!fragmentSuccess || fragmentPatternMatcher.Fragment.Negate) + { + state.CurrentIndex = startIndex; + } + if (pattern.IsNoise && partMatchData != null) + { + partMatchData.EndDistinctIndex = distinctIndex; + } } - state.CurrentIndex = state.MaxIndex; - bool success = state.CurrentIndex > startIndex; - return (success, null); + return (fragmentSuccess, null); } return (false, null); } @@ -160,7 +142,7 @@ protected override void BuildState(ref State state, string code) state.MatchCache = new Dictionary, FragmentMatchData>(); } - protected override (bool success, StringMatchData matchData) MatchPattern(ref State state, PatternMatcher pattern, bool required, bool readOnly = false) + protected override (bool success, StringMatchData matchData) MatchPattern(ref State state, FragmentMatchData fragmentMatchData, PatternMatcher pattern, bool required, bool readOnly = false) { if (pattern != null) { @@ -175,14 +157,15 @@ protected override (bool success, StringMatchData matchData) MatchPattern(ref St Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, - Id = pattern.Id, - IsNoise = pattern.IsNoise, - Mergable = pattern.Mergable + Id = pattern.Id }; if (!readOnly) { state.CurrentIndex = startOffset + length; - state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); + if (fragmentMatchData.StartIndex < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -266,7 +249,7 @@ public MatcherResult Match(string code, FragmentMatcher startingMatcher = null, if ( success && matchFullText && - state.CurrentIndex != (state.PreMatchSuccess ? + state.CurrentIndex < (state.PreMatchSuccess ? state.DistinctStringMatches.LastOrDefault().GetEndIndex() : state.Code.Length )) { @@ -277,7 +260,7 @@ public MatcherResult Match(string code, FragmentMatcher startingMatcher = null, return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, _logMatches ? state.MatchLogBuilder.ToString() : string.Empty); } - protected virtual (bool success, StringMatchData matchData) MatchPattern(ref State state, PatternMatcher pattern, bool required, bool readOnly = false) + protected virtual (bool success, StringMatchData matchData) MatchPattern(ref State state, FragmentMatchData fragmentMatchData, PatternMatcher pattern, bool required, bool readOnly = false) { if (pattern == null) { @@ -296,6 +279,10 @@ protected virtual (bool success, StringMatchData matchData) MatchPattern(ref Sta { state.DistinctIndex++; state.CurrentIndex = stringMatchData.StartIndex + stringMatchData.Length; + if (fragmentMatchData.StartIndex < 0) + { + fragmentMatchData.StartIndex = stringMatchData.StartIndex; + } } else if (!required) { @@ -317,9 +304,7 @@ protected virtual (bool success, StringMatchData matchData) MatchPattern(ref Sta Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, - Id = pattern.Id, - IsNoise = pattern.IsNoise, - Mergable = pattern.Mergable + Id = pattern.Id }; if (!pattern.IsNoise) { @@ -330,7 +315,10 @@ protected virtual (bool success, StringMatchData matchData) MatchPattern(ref Sta if (!readOnly) { state.CurrentIndex = startOffset + length; - state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); + if (fragmentMatchData.StartIndex < 0) + { + fragmentMatchData.StartIndex = startOffset; + } } } else if (!required) @@ -342,42 +330,49 @@ protected virtual (bool success, StringMatchData matchData) MatchPattern(ref Sta return (false, default); } - private bool MatchPartByFragmentMatcher(ref State state, FragmentMatchData matchData, FragmentMatcher part) + private bool MatchPartByFragmentMatcher(ref State state, FragmentMatchData parentMatchData, FragmentMatcher part) { bool success; bool negate = part.Negate; - if (!part.Cacheable || !state.MatchCache.TryGetValue(new ValueTuple(part.Name, state.CurrentIndex), out FragmentMatchData partMatcherData)) + if (_logMatches) + { + state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id)} {state.CurrentIndex}. Try: {part}"); + } + if (!part.Cacheable || !state.MatchCache.TryGetValue(new ValueTuple(part.Name, state.CurrentIndex), out FragmentMatchData partMatchData)) { int startIndex = state.CurrentIndex; int distinctIndex = state.DistinctIndex; - (success, partMatcherData) = MatchByFragmentMatcher(ref state, part); - if (part.Cacheable) - { - state.MatchCache[new ValueTuple(part.Name, startIndex)] = partMatcherData; - } + (success, partMatchData) = MatchByFragmentMatcher(ref state, part); if (!success || negate) { state.CurrentIndex = startIndex; state.DistinctIndex = distinctIndex; } + if (part.Cacheable) + { + state.MatchCache[new ValueTuple(part.Name, startIndex)] = partMatchData; + } } - else if ((success = partMatcherData != null) && !negate) + else if ((success = partMatchData != null) && !negate) { - state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; - state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); - state.DistinctIndex = partMatcherData.EndDistinctIndex; + state.CurrentIndex = partMatchData.StartIndex + partMatchData.Length; + state.DistinctIndex = partMatchData.EndDistinctIndex; } if (success && !negate) { + if (parentMatchData.StartIndex < 0) + { + parentMatchData.StartIndex = partMatchData.StartIndex; + } if (!part.IsNoise) { - if (part.FallThroughMode == FallThroughMode.All || partMatcherData.Parts.Count <= (int)part.FallThroughMode) + if (part.FallThroughMode == FallThroughMode.All || partMatchData.Parts.Count <= (int)part.FallThroughMode) { - matchData.Parts.AddRange(partMatcherData.Parts); + parentMatchData.Parts.AddRange(partMatchData.Parts); } else { - matchData.Parts.Add(partMatcherData); + parentMatchData.Parts.Add(partMatchData); } } if (part.ClearCache) @@ -385,6 +380,10 @@ private bool MatchPartByFragmentMatcher(ref State state, FragmentMatchData match state.MatchCache.Clear(); } } + if (_logMatches) + { + state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: {part}"); + } return success ^ negate; } @@ -393,20 +392,21 @@ private bool MatchPartByFragmentMatcher(ref State state, FragmentMatchData match FragmentMatchData matchData = new FragmentMatchData { Name = matcher.Name, - StartIndex = state.CurrentIndex, + StartIndex = -1, ExpressionOrder = matcher.ExpressionOrder }; StringMatchData endMatchData = default; - bool success = MatchFragmentBounds(ref state, matcher.Start, matcher, matcher.DiscardBounds, out StringMatchData startMatchData) && MatchFragmentParts(ref state, matcher, matchData) && MatchFragmentBounds(ref state, matcher.End, matcher, matcher.DiscardBounds, out endMatchData); + bool success = MatchFragmentBounds(ref state, matcher, matchData, matcher.Start, matcher.DiscardBounds, out StringMatchData startMatchData) && MatchFragmentParts(ref state, matcher, matchData) && MatchFragmentBounds(ref state, matcher, matchData, matcher.End, matcher.DiscardBounds, out endMatchData); if (success && matcher.Actions != null) { - IEnumerator actionEnumerator = matcher.Actions.GetEnumerator(); - actionEnumerator.Reset(); - while (success && actionEnumerator.MoveNext()) + foreach (MatcherAction action in matcher.Actions) { - MatcherAction action = actionEnumerator.Current; success = action.Perform(state.BlobDatas, matchData.Parts); + if (!success) + { + break; + } } } @@ -437,32 +437,32 @@ private bool MatchPartByFragmentMatcher(ref State state, FragmentMatchData match return (false, null); } - private bool MatchFragmentParts(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) + private bool MatchFragmentParts(ref State state, FragmentMatcher parentMatcher, FragmentMatchData parentMatchData) { bool success = true; - if (matcher.Parts.Count > 0) + if (parentMatcher.Parts.Count > 0) { - switch (matcher.PartsMatchMode) + switch (parentMatcher.PartsMatchMode) { case MatchMode.Multiple: - success = MatchFragmentPartsMultipleMode(ref state, matcher, matchData); + success = MatchFragmentPartsMultipleMode(ref state, parentMatcher, parentMatchData); break; case MatchMode.One: - success = MatchFragmentPartsOneMode(ref state, matcher, matchData); + success = MatchFragmentPartsOneMode(ref state, parentMatcher, parentMatchData); break; case MatchMode.Ordered: - success = MatchFragmentPartsOrderedMode(ref state, matcher, matchData); + success = MatchFragmentPartsOrderedMode(ref state, parentMatcher, parentMatchData); break; } } - if (!success ^ matcher.Negate) + if (!success ^ parentMatcher.Negate) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } return success; } - private bool MatchFragmentPartsMultipleMode(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) + private bool MatchFragmentPartsMultipleMode(ref State state, FragmentMatcher parentMatcher, FragmentMatchData parentMatchData) { bool overallSuccess = false; bool subSuccess; @@ -470,19 +470,19 @@ private bool MatchFragmentPartsMultipleMode(ref State state, FragmentMatcher mat StringMatchData range = default; int matchCount = 0; int distinctIndex = state.DistinctIndex; - MatchPattern(ref state, matcher.PartsPadding, false); + MatchPattern(ref state, parentMatchData, parentMatcher.PartsPadding, false); do { subSuccess = false; - foreach (IMatcher part in matcher.Parts) + foreach (IMatcher part in parentMatcher.Parts) { - bool individualSuccess = MatchFragmentPart(ref state, matchData, part); + bool individualSuccess = MatchFragmentPart(ref state, parentMatchData, part); subSuccess |= individualSuccess; if (individualSuccess) { matchCount++; distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = MatchPattern(ref state, matcher.PartsDelimiter, matcher.PartsDelimiterRequired); + (delimiterSuccess, range) = MatchPattern(ref state, parentMatchData, parentMatcher.PartsDelimiter, parentMatcher.PartsDelimiterRequired); break; } } @@ -496,20 +496,19 @@ private bool MatchFragmentPartsMultipleMode(ref State state, FragmentMatcher mat } if (overallSuccess) { - MatchPattern(ref state, matcher.PartsPadding, false); + MatchPattern(ref state, parentMatchData, parentMatcher.PartsPadding, false); } - bool thresholdSuccess = (matcher.MinMatchedParts ?? 1) <= matchCount; - return overallSuccess && thresholdSuccess; + return (parentMatcher.MinMatchedParts ?? 1) <= matchCount; } - private bool MatchFragmentPartsOneMode(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) + private bool MatchFragmentPartsOneMode(ref State state, FragmentMatcher parentMatcher, FragmentMatchData parentMatchData) { bool success = false; int matchCount = 0; - MatchPattern(ref state, matcher.PartsPadding, false); - foreach (IMatcher part in matcher.Parts) + MatchPattern(ref state, parentMatchData, parentMatcher.PartsPadding, false); + foreach (IMatcher part in parentMatcher.Parts) { - success = MatchFragmentPart(ref state, matchData, part); + success = MatchFragmentPart(ref state, parentMatchData, part); if (success) { matchCount++; @@ -518,26 +517,25 @@ private bool MatchFragmentPartsOneMode(ref State state, FragmentMatcher matcher, } if (success) { - MatchPattern(ref state, matcher.PartsPadding, false); + MatchPattern(ref state, parentMatchData, parentMatcher.PartsPadding, false); } - bool thresholdSuccess = (matcher.MinMatchedParts ?? 1) <= 0 || matchCount > 0; - return thresholdSuccess; + return (parentMatcher.MinMatchedParts ?? 1) <= 0 || matchCount > 0; } - private bool MatchFragmentPartsOrderedMode(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) + private bool MatchFragmentPartsOrderedMode(ref State state, FragmentMatcher parentMatcher, FragmentMatchData parentMatchData) { bool success = true; bool partSuccess; int matchCount = 0; StringMatchData stringMatchData = default; int distinctIndex = state.DistinctIndex; - MatchPattern(ref state, matcher.PartsPadding, false); - for (int partIndex = 0; partIndex < matcher.Parts.Count; partIndex++) + MatchPattern(ref state, parentMatchData, parentMatcher.PartsPadding, false); + for (int partIndex = 0; partIndex < parentMatcher.Parts.Count; partIndex++) { if (partIndex > 0) { distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPattern(ref state, matcher.PartsDelimiter, matcher.PartsDelimiterRequired); + (partSuccess, stringMatchData) = MatchPattern(ref state, parentMatchData, parentMatcher.PartsDelimiter, parentMatcher.PartsDelimiterRequired); success = partSuccess; if (!success) { @@ -545,8 +543,8 @@ private bool MatchFragmentPartsOrderedMode(ref State state, FragmentMatcher matc } } - IMatcher part = matcher.Parts[partIndex]; - success = MatchFragmentPart(ref state, matchData, part); + IMatcher part = parentMatcher.Parts[partIndex]; + success = MatchFragmentPart(ref state, parentMatchData, part); if (!success) { if (stringMatchData != null) @@ -563,63 +561,70 @@ private bool MatchFragmentPartsOrderedMode(ref State state, FragmentMatcher matc } if (success) { - MatchPattern(ref state, matcher.PartsPadding, false); + MatchPattern(ref state, parentMatchData, parentMatcher.PartsPadding, false); } - bool thresholdSuccess = (matcher.MinMatchedParts ?? matcher.Parts.Count) <= matchCount; - return success || thresholdSuccess; + return (parentMatcher.MinMatchedParts ?? parentMatcher.Parts.Count) <= matchCount; } - private bool MatchFragmentPart(ref State state, FragmentMatchData matchData, IMatcher part) + private bool MatchFragmentPart(ref State state, FragmentMatchData parentMatchData, IMatcher part) { - int currentId = ++state.Id; - if (_logMatches) - { - state.MatchLogBuilder.AppendLine($"{new string('\t', currentId)} {state.CurrentIndex}. Try: {part}"); - } + state.Id++; bool success = false; switch (part) { case FragmentMatcher partFragmentMatcher: - success = MatchPartByFragmentMatcher(ref state, matchData, partFragmentMatcher); + success = MatchPartByFragmentMatcher(ref state, parentMatchData, partFragmentMatcher); break; case PatternMatcher partPatternMatcher: - success = MatchPartByTextMatcher(ref state, matchData, partPatternMatcher); + success = MatchPartByTextMatcher(ref state, parentMatchData, partPatternMatcher); break; } + state.Id--; + return success; + } + private bool MatchPartByTextMatcher(ref State state, FragmentMatchData parentMatchData, PatternMatcher part) + { + int startIndex = state.CurrentIndex; if (_logMatches) { - state.MatchLogBuilder.AppendLine($"{new string('\t', currentId)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: {part}"); + state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id)} {startIndex}. Try: {part}"); } - state.Id = currentId - 1; - return success; - } - private bool MatchPartByTextMatcher(ref State state, FragmentMatchData matchData, PatternMatcher part) - { - (bool success, StringMatchData stringMatchData) = MatchPattern(ref state, part, true); + (bool success, StringMatchData stringMatchData) = MatchPattern(ref state, parentMatchData, part, true); if (success) { - matchData.Parts.Add(stringMatchData); + parentMatchData.Parts.Add(stringMatchData); if (_logMatches) { - state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id + 1)} {state.CurrentIndex}. Matched: {stringMatchData.Text}"); + state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id + 1)} {(stringMatchData?.StartIndex ?? startIndex)}. Matched: {stringMatchData.Text}"); } } + + if (_logMatches) + { + state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id)} {(success && stringMatchData != null ? stringMatchData.StartIndex + stringMatchData.Length : startIndex)}. {(success ? "Passed" : "Failed")}: {part}"); + } return success; } - private bool MatchFragmentBounds(ref State state, PatternMatcher patternMatcher, FragmentMatcher matcher, bool readOnly, out StringMatchData matchData) + private bool MatchFragmentBounds(ref State state, FragmentMatcher parentMatcher, FragmentMatchData parentMatchData, PatternMatcher patternMatcher, bool readOnly, out StringMatchData matchData) { + if (patternMatcher == null) + { + matchData = default; + return true; + } bool success; - (success, matchData) = MatchPattern(ref state, patternMatcher, true, readOnly); - if (!success ^ matcher.Negate) + int startIndex = state.CurrentIndex; + (success, matchData) = MatchPattern(ref state, parentMatchData, patternMatcher, true, readOnly); + if (!success ^ parentMatcher.Negate) { state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); } if (patternMatcher != null && _logMatches) { - state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id + 1)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")} Bounds: {patternMatcher}"); + state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id + 1)} {(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}. {(success ? "Passed" : "Failed")} Bounds: {patternMatcher}"); } return success; } @@ -638,10 +643,10 @@ internal static string Generate(MatcherEngineGenerator generator) /* Generated by Synfron.Staxe.Matcher v{version}*/ using Synfron.Staxe.Matcher.Data; -using System.Collections.Generic; +using Synfron.Staxe.Matcher.Input; using System; +using System.Collections.Generic; using System.Linq; -using Synfron.Staxe.Matcher.Input; {(generator.LanguageMatcher.LogMatches ? "using System.Text;" : null)} namespace Synfron.Staxe.Matcher @@ -662,7 +667,7 @@ private static string GenerateMatch(MatcherEngineGenerator generator) return $@" public override MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true) {{ - FragmentMatchData matchData = new FragmentMatchData + FragmentMatchData parentMatchData = new FragmentMatchData {{ StartIndex = 0 }}; @@ -673,7 +678,7 @@ public override MatcherResult Match(string code, string fragmentMatcher, bool ma DistinctStringMatches = new List(2000)" : null)}{(languageMatcher.Fragments.Any(fragment => fragment.Cacheable) ? @", MatchCache = new Dictionary, FragmentMatchData>()" : null)}{(languageMatcher.LogMatches ? $@", MatchLogBuilder = new StringBuilder()" : null)}{(languageMatcher.Blobs.Count > 0 ? $@", - BlobDatas = new Span(new BlobData[{languageMatcher.Blobs.Count}]);" : null)} + BlobDatas = new Span(new BlobData[{languageMatcher.Blobs.Count}])" : null)} }}; {(languageMatcher.IndexingMode == IndexingMode.Eager ? "state.PreMatchSuccess = PreMatchPatterns(ref state);" : null)} @@ -688,10 +693,12 @@ public override MatcherResult Match(string code, string fragmentMatcher, bool ma "))} }} - IMatchData resultMatchData = matchData.Parts.FirstOrDefault(); + IMatchData resultMatchData = parentMatchData.Parts.FirstOrDefault(); int? failureIndex = success ? null : state.FailureIndex; - if (success && matchFullText && (state.PreMatchSuccess ? state.MaxIndex : state.CurrentIndex) != state.Code.Length) + if (success && matchFullText && state.CurrentIndex < {(languageMatcher.IndexingMode == IndexingMode.Eager ? $@"(state.PreMatchSuccess ? + state.DistinctStringMatches.LastOrDefault().GetEndIndex() : " : null)}state.Code.Length + {(languageMatcher.IndexingMode == IndexingMode.Eager ? ")" : null)}) {{ success = false; failureIndex = state.CurrentIndex; @@ -702,7 +709,7 @@ public override MatcherResult Match(string code, string fragmentMatcher, bool ma public override MatcherResult Match(string code, bool matchFullText = true) {{ - FragmentMatchData matchData = new FragmentMatchData + FragmentMatchData parentMatchData = new FragmentMatchData {{ StartIndex = 0 }}; @@ -712,17 +719,20 @@ public override MatcherResult Match(string code, bool matchFullText = true) Code = code{(languageMatcher.IndexingMode != IndexingMode.None ? @", DistinctStringMatches = new List(2000)" : null)}{(languageMatcher.Fragments.Any(fragment => fragment.Cacheable) ? @", MatchCache = new Dictionary, FragmentMatchData>()" : null)}{(languageMatcher.LogMatches ? $@", - MatchLogBuilder = new StringBuilder()" : null)} + MatchLogBuilder = new StringBuilder()" : null)}{(languageMatcher.Blobs.Count > 0 ? $@", + BlobDatas = new Span(new BlobData[{languageMatcher.Blobs.Count}])" : null)} }}; - {(languageMatcher.IndexingMode == IndexingMode.Eager ? "PreMatchPatterns(ref state);" : null)} + {(languageMatcher.IndexingMode == IndexingMode.Eager ? "state.PreMatchSuccess = PreMatchPatterns(ref state);" : null)} bool success = {Generate(generator, languageMatcher.StartingFragment)}; - IMatchData resultMatchData = matchData?.Parts.FirstOrDefault(); + IMatchData resultMatchData = parentMatchData?.Parts.FirstOrDefault(); int? failureIndex = success ? null : state.FailureIndex; - if (success && matchFullText && state.CurrentIndex != state.Code.Length) + if (success && matchFullText && state.CurrentIndex < {(languageMatcher.IndexingMode == IndexingMode.Eager ? $@"(state.PreMatchSuccess ? + state.DistinctStringMatches.LastOrDefault().GetEndIndex() : " : null )}state.Code.Length + {(languageMatcher.IndexingMode == IndexingMode.Eager ? ")" : null)}) {{ success = false; failureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -735,39 +745,14 @@ public override MatcherResult Match(string code, bool matchFullText = true) {{ int codeLength = state.Code.Length; bool success = true; - bool previousNoise = false; StringMatchData matchData = null; int currentIndex = 0; - while ((currentIndex = state.CurrentIndex) < codeLength) + while (success && (currentIndex = state.CurrentIndex) < codeLength) {{ - success = {string.Join(" ||\n", languageMatcher.Patterns.Select(pattern => $@"{string.Format(GenerateMatchPattern(generator, pattern), "matchData", "true", "false")}"))}; - if (!success) - {{ - break; - }} - else if (matchData != null) {{ - {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{currentIndex}}. Prematched {{matchData.Name}}: {{matchData.Text}}"");" : null)} - if (matchData.IsNoise) - {{ - previousNoise = true; - }} - else if (previousNoise) - {{ - if (state.DistinctIndex > 1) - {{ - StringMatchData previousMatchData = state.DistinctStringMatches[state.DistinctIndex - 2]; - if (previousMatchData.Name == matchData.Name && previousMatchData.Mergable) - {{ - previousMatchData.Text += matchData.Text; - previousMatchData.Length = state.CurrentIndex - previousMatchData.StartIndex; - state.DistinctIndex--; - state.MaxDistinctIndex--; - state.DistinctStringMatches.RemoveAt(state.DistinctIndex); - }} - }} - previousNoise = false; - }} - }} + success = {string.Join(" ||\n", languageMatcher.Patterns.Select(pattern => $@"{string.Format(GeneratePreMatchPattern(generator, pattern), "null", "matchData", "true", "false")}"))}; + {(generator.LanguageMatcher.LogMatches ? $@"if (matchData != null) {{ + state.MatchLogBuilder.AppendLine($""{{currentIndex}}. Prematched {{matchData.Name}}: {{matchData.Text}}""); + }}" : null)} }} state.CurrentIndex = 0; {(languageMatcher.IndexingMode != IndexingMode.None ? "state.DistinctIndex = 0;" : null)} @@ -779,18 +764,18 @@ public override MatcherResult Match(string code, bool matchFullText = true) private static string GenerateMatchPattern(MatcherEngineGenerator generator, PatternMatcher pattern) { string methodName = $"MatchPattern{GetSafeMethodName(pattern.Name)}"; - string method = $"{methodName}(ref state, out {{0}}, {{1}}, {{2}})"; + string method = $"{methodName}(ref state, {{0}}, out {{1}}, {{2}}, {{3}})"; if (!generator.TryGetMethod(methodName, ref method)) { generator.Add(methodName, method); - string code = $@"private bool {methodName}(ref State state, out StringMatchData matchData, bool required, bool readOnly = false) + string code = $@"private bool {methodName}(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) {{ bool success = false; - {(generator.IndexingMode != IndexingMode.None && pattern.IsStandard ? $@"int distinctIndex = state.DistinctIndex; + int startOffset = state.CurrentIndex; + {(generator.IndexingMode != IndexingMode.None ? $@"int distinctIndex = state.DistinctIndex; if (distinctIndex >= state.MaxDistinctIndex) {{" : null)} int length; - int startOffset = state.CurrentIndex; (success, length) = {GenerateRawMatchPattern(generator, pattern)}; matchData = default; if (success) @@ -801,8 +786,6 @@ private static string GenerateMatchPattern(MatcherEngineGenerator generator, Pat Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, - IsNoise = {pattern.IsNoise.ToString().ToLower()}, - Mergable = {pattern.Mergable.ToString().ToLower()}, Id = {pattern.Id} }}; {(!pattern.IsNoise && generator.IndexingMode != IndexingMode.None ? $@"state.DistinctStringMatches.Add(matchData);" : null)} @@ -811,7 +794,10 @@ private static string GenerateMatchPattern(MatcherEngineGenerator generator, Pat if (!readOnly) {{ state.CurrentIndex = startOffset + length; - state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + {{ + fragmentMatchData.StartIndex = startOffset; + }} }} }} else if (!required) @@ -819,9 +805,10 @@ private static string GenerateMatchPattern(MatcherEngineGenerator generator, Pat success = true; }} return success; - {(generator.IndexingMode != IndexingMode.None && pattern.IsStandard ? $@"}} + {(generator.IndexingMode != IndexingMode.None ? $@"}} else {{ + int length; matchData = state.DistinctStringMatches[distinctIndex]; if (matchData != null) {{ @@ -829,8 +816,6 @@ private static string GenerateMatchPattern(MatcherEngineGenerator generator, Pat }} else {{ - int length; - int startOffset = state.CurrentIndex; (success, length) = {GenerateRawMatchPattern(generator, pattern)}; if (success) {{ @@ -840,8 +825,6 @@ private static string GenerateMatchPattern(MatcherEngineGenerator generator, Pat Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, - IsNoise = {pattern.IsNoise.ToString().ToLower()}, - Mergable = {pattern.Mergable.ToString().ToLower()}, Id = {pattern.Id} }}; {(!pattern.IsNoise ? $@"state.DistinctStringMatches[distinctIndex] = matchData;" : null)} @@ -850,8 +833,11 @@ private static string GenerateMatchPattern(MatcherEngineGenerator generator, Pat if (success && !readOnly) {{ {(!pattern.IsNoise ? $@"state.DistinctIndex++;" : null)} - state.CurrentIndex = startOffset + length; - state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); + state.CurrentIndex = matchData.StartIndex + matchData.Length; + if ((fragmentMatchData?.StartIndex ?? 0) < 0) + {{ + fragmentMatchData.StartIndex = matchData.StartIndex; + }} }} if (!required) {{ @@ -859,26 +845,59 @@ private static string GenerateMatchPattern(MatcherEngineGenerator generator, Pat }} return success; }}" : null)} + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } + + private static string GeneratePreMatchPattern(MatcherEngineGenerator generator, PatternMatcher pattern) + { + if (pattern is FragmentPatternMatcher) + { + string methodName = $"MatchPattern{GetSafeMethodName(pattern.Name)}"; + string method = $"{methodName}(ref state, {{0}}, out {{1}}, {{2}}, {{3}})"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + string code = $@"private bool {methodName}(ref State state, FragmentMatchData fragmentMatchData, out StringMatchData matchData, bool required, bool readOnly = false) + {{ {(!pattern.IsStandard && pattern is FragmentPatternMatcher fragmentPatternMatcher && fragmentPatternMatcher.Fragment is FragmentMatcher fragmentMatcher ? $@" - int startOffset = state.CurrentIndex;{(pattern.IsNoise ? $@" - int distinctStringMatchesCount = state.DistinctStringMatches.Count; + bool success = false; + int startOffset = state.CurrentIndex; int distinctIndex = state.DistinctIndex; int maxDistinctIndex = state.MaxDistinctIndex; - " : null)} - FragmentMatchData partMatcherData; - {GenerateMatchByFragmentMatcherSection(generator, fragmentMatcher)} {(fragmentMatcher.Cacheable ? $@" - state.MatchCache[new ValueTuple({fragmentMatcher.Name}, startOffset)] = partMatcherData; - " : null)}{(pattern.IsNoise ? $@" - state.DistinctStringMatches.RemoveRange(distinctStringMatchesCount, state.DistinctStringMatches.Count - distinctStringMatchesCount); - state.DistinctIndex = distinctIndex; - state.MaxDistinctIndex = maxDistinctIndex; + StringMatchData lastDistinctMatchData = state.DistinctStringMatches.LastOrDefault(); + int cacheIndex = lastDistinctMatchData != null ? + lastDistinctMatchData.StartIndex + lastDistinctMatchData.Length : + 0; " : null)} - success = success && state.CurrentIndex > startIndex;" : null)} + FragmentMatchData partMatchData; + {GenerateMatchByFragmentMatcherSection(generator, fragmentMatcher, $@"{(fragmentMatcher.Cacheable ? $@" + state.MatchCache[new ValueTuple(""{GetEscapedName(fragmentMatcher.Name)}"", cacheIndex)] = partMatchData;" : null)} + {(pattern.IsNoise ? "partMatchData.EndDistinctIndex = distinctIndex;" : null)}", null)} + {(!fragmentMatcher.Negate && !pattern.IsNoise ? $@"if (!success) + {{" : null)} + state.DistinctStringMatches.RemoveRange(distinctIndex, state.DistinctIndex - distinctIndex); + state.DistinctIndex = distinctIndex; + state.MaxDistinctIndex = maxDistinctIndex; + {(!fragmentMatcher.Negate && pattern.IsNoise ? $@"if (!success) + {{ + state.CurrentIndex = startOffset; + }}" : "state.CurrentIndex = startOffset;")} + {(!fragmentMatcher.Negate && !pattern.IsNoise ? $"}}" : null)} + matchData = null; + return success;" : null)} }}"; - method = generator.Add(method, methodName, code); + method = generator.Add(method, methodName, code); + } + return method; + } + else + { + return GenerateMatchPattern(generator, pattern); } - return method; } private static string GenerateRawMatchPattern(MatcherEngineGenerator generator, PatternMatcher pattern) @@ -888,13 +907,13 @@ private static string GenerateRawMatchPattern(MatcherEngineGenerator generator, private static string GenerateMatcherAction(MatcherEngineGenerator generator, MatcherAction action) { - return string.Format(action.Generate(generator), "state.BlobDatas", "partMatcherData.Parts"); + return string.Format(action.Generate(generator), "state.BlobDatas", "partMatchData.Parts"); } private static string GenerateMatchFragmentPartsOrderedMode(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher) { string methodName = $"MatchFragmentPartsOrderedMode{GetSafeMethodName(fragmentMatcher.Name)}"; - string method = $"{methodName}(ref state, partMatcherData)"; + string method = $"{methodName}(ref state, partMatchData)"; if (!generator.TryGetMethod(methodName, ref method)) { generator.Add(methodName, method); @@ -902,7 +921,7 @@ private static string GenerateMatchFragmentPartsOrderedMode(MatcherEngineGenerat for (int partIndex = 0; partIndex < fragmentMatcher.Parts.Count; partIndex++) { functionText.AppendLine($@"{(partIndex > 0 ? $@"distinctIndex = state.DistinctIndex; - partSuccess = {(fragmentMatcher.PartsDelimiter != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsDelimiter), "stringMatchData", fragmentMatcher.PartsDelimiterRequired.ToString().ToLower(), "false") : "true")}; + partSuccess = {(fragmentMatcher.PartsDelimiter != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsDelimiter), "parentMatchData", "stringMatchData", fragmentMatcher.PartsDelimiterRequired.ToString().ToLower(), "false") : "true")}; success = partSuccess; if (!success) {{ @@ -925,14 +944,14 @@ private static string GenerateMatchFragmentPartsOrderedMode(MatcherEngineGenerat }}"); } - string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) + string code = $@"private bool {methodName}(ref State state, FragmentMatchData parentMatchData) {{ bool success = true; bool partSuccess; int matchCount = 0; StringMatchData stringMatchData = null; int distinctIndex = state.DistinctIndex; - {(fragmentMatcher.PartsPadding != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false") : null)}; + {(fragmentMatcher.PartsPadding != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "parentMatchData", "_", "false", "false") : null)}; {functionText} @@ -940,10 +959,10 @@ private static string GenerateMatchFragmentPartsOrderedMode(MatcherEngineGenerat {(fragmentMatcher.PartsPadding != null ? $@"if (success) {{ - {string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false")}; + {string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "parentMatchData", "_", "false", "false")}; }}" : null)} - success = success && {(fragmentMatcher.MinMatchedParts ?? fragmentMatcher.Parts.Count)} <= matchCount; + success = {(fragmentMatcher.MinMatchedParts ?? fragmentMatcher.Parts.Count)} <= matchCount; if ({(!fragmentMatcher.Negate ? "!" : null)}success) {{ state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -958,15 +977,15 @@ private static string GenerateMatchFragmentPartsOrderedMode(MatcherEngineGenerat private static string GenerateMatchFragmentPartsOneMode(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher) { string methodName = $"MatchFragmentPartsOneMode{GetSafeMethodName(fragmentMatcher.Name)}"; - string method = $"{methodName}(ref state, partMatcherData)"; + string method = $"{methodName}(ref state, partMatchData)"; if (!generator.TryGetMethod(methodName, ref method)) { generator.Add(methodName, method); - string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) + string code = $@"private bool {methodName}(ref State state, FragmentMatchData parentMatchData) {{ bool success = false; int matchCount = 0; - {(fragmentMatcher.PartsPadding != null ? $"{string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false")};" : null)} + {(fragmentMatcher.PartsPadding != null ? $"{string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "parentMatchData", "_", "false", "false")};" : null)} {(fragmentMatcher.Parts.Count > 0 ? $@" success = {string.Join(" || ", fragmentMatcher.Parts.Select(part => GenerateMatchFragmentPart(generator, part)))}; @@ -978,10 +997,10 @@ private static string GenerateMatchFragmentPartsOneMode(MatcherEngineGenerator g {(fragmentMatcher.PartsPadding != null ? $@"if (success) {{ - {string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false")}; + {string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "parentMatchData", "_", "false", "false")}; }}" : null)} - success = {((fragmentMatcher.MinMatchedParts ?? 1) <= 0).ToString().ToLower()} && matchCount > 0; + success = {((fragmentMatcher.MinMatchedParts ?? 1) <= 0 ? "true" : "matchCount > 0")}; if ({(!fragmentMatcher.Negate ? "!" : null)}success) {{ state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -996,7 +1015,7 @@ private static string GenerateMatchFragmentPartsOneMode(MatcherEngineGenerator g private static string GenerateMatchFragmentPartsMultipleMode(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher) { string methodName = $"MatchFragmentPartsMultipleMode{GetSafeMethodName(fragmentMatcher.Name)}"; - string method = $"{methodName}(ref state, partMatcherData)"; + string method = $"{methodName}(ref state, partMatchData)"; if (!generator.TryGetMethod(methodName, ref method)) { generator.Add(methodName, method); @@ -1009,13 +1028,13 @@ private static string GenerateMatchFragmentPartsMultipleMode(MatcherEngineGenera {{ matchCount++; distinctIndex = state.DistinctIndex; - delimiterSuccess = {(fragmentMatcher.PartsDelimiter != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsDelimiter), "range", fragmentMatcher.PartsDelimiterRequired.ToString().ToLower(), "false") : "true")}; + delimiterSuccess = {(fragmentMatcher.PartsDelimiter != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsDelimiter), "parentMatchData", "range", fragmentMatcher.PartsDelimiterRequired.ToString().ToLower(), "false") : "true")}; goto Break; }} "); } - string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) + string code = $@"private bool {methodName}(ref State state, FragmentMatchData parentMatchData) {{ bool overallSuccess = false; bool subSuccess = false; @@ -1023,7 +1042,7 @@ private static string GenerateMatchFragmentPartsMultipleMode(MatcherEngineGenera StringMatchData range = default; int matchCount = 0; int distinctIndex = state.DistinctIndex; - {(fragmentMatcher.PartsPadding != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false") : null)}; + {(fragmentMatcher.PartsPadding != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "parentMatchData", "_", "false", "false") : null)}; do {{ @@ -1043,11 +1062,10 @@ private static string GenerateMatchFragmentPartsMultipleMode(MatcherEngineGenera {(fragmentMatcher.PartsPadding != null ? $@"if (overallSuccess) {{ - {string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false")}; + {string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "parentMatchData", "_", "false", "false")}; }}" : null)} - bool thresholdSuccess = {fragmentMatcher.MinMatchedParts ?? 1} <= matchCount; - bool success = overallSuccess && thresholdSuccess; + bool success = {fragmentMatcher.MinMatchedParts ?? 1} <= matchCount; if ({(!fragmentMatcher.Negate ? "!" : null)}success) {{ state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); @@ -1069,18 +1087,19 @@ private static string GenerateMatchFragmentPart(MatcherEngineGenerator generator private static string GenerateMatchFragmentBounds(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher, PatternMatcher matcher) { string methodName = $"MatchFragmentBounds{GetSafeMethodName(matcher.Name)}"; - string method = $"{methodName}(ref state, {{0}}, out {{1}})"; + string method = $"{methodName}(ref state, {{0}}, {{1}}, out {{2}})"; if (!generator.TryGetMethod(methodName, ref method)) { generator.Add(methodName, method); - string code = $@"private bool {methodName}(ref State state, bool readOnly, out StringMatchData matchData) - {{ - bool success = {string.Format(GenerateMatchPattern(generator, matcher), "matchData", "true", "readOnly")}; + string code = $@"private bool {methodName}(ref State state, FragmentMatchData parentMatchData, bool readOnly, out StringMatchData matchData) + {{{(generator.LanguageMatcher.LogMatches ? @" + int startIndex = state.CurrentIndex;" : "")} + bool success = {string.Format(GenerateMatchPattern(generator, matcher), "parentMatchData", "matchData", "true", "readOnly")}; if ({(!fragmentMatcher.Negate ? "!" : null)}success) {{ state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); }} - {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id + 1)}} {{state.CurrentIndex}}. {{(success ? ""Passed"" : ""Failed"")}} Bounds: {{""{HttpUtility.JavaScriptStringEncode(matcher.ToString())}""}}"");" : null)} + {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id + 1)}} {{(success && matchData != null ? matchData.StartIndex + matchData.Length : startIndex)}}. {{(success ? ""Passed"" : ""Failed"")}} Bounds: {{""{HttpUtility.JavaScriptStringEncode(matcher.ToString())}""}}"");" : null)} return success; }}"; method = generator.Add(method, methodName, code); @@ -1091,25 +1110,26 @@ private static string GenerateMatchFragmentBounds(MatcherEngineGenerator generat private static string GenerateMatchPartByTextMatcher(MatcherEngineGenerator generator, PatternMatcher matcher) { string methodName = $"MatchPartByTextMatcher{GetSafeMethodName(matcher.Name)}"; - string method = $"{methodName}(ref state, matchData)"; + string method = $"{methodName}(ref state, parentMatchData)"; if (!generator.TryGetMethod(methodName, ref method)) { generator.Add(methodName, method); - string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) + string code = $@"private bool {methodName}(ref State state, FragmentMatchData parentMatchData) {{ - {(generator.LanguageMatcher.LogMatches ? $@"int currentId = ++state.Id; - state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. Try: {HttpUtility.JavaScriptStringEncode(matcher.Name)}"");" : null)} + {(generator.LanguageMatcher.LogMatches ? $@"state.Id++; + int startIndex = state.CurrentIndex; + state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id)}} {{startIndex}}. Try: {HttpUtility.JavaScriptStringEncode(matcher.ToString())}"");" : null)} StringMatchData partMatchData; - bool success = {string.Format(GenerateMatchPattern(generator, matcher), "partMatchData", "true", "false")}; + bool success = {string.Format(GenerateMatchPattern(generator, matcher), "parentMatchData", "partMatchData", "true", "false")}; if (success) {{ - matchData.Parts.Add(partMatchData); - {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id + 1)}} {{state.CurrentIndex}}. Matched: {{partMatchData.Text}}"");" : null)} + parentMatchData.Parts.Add(partMatchData); + {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id + 1)}} {{partMatchData?.StartIndex ?? startIndex}}. Matched: {{partMatchData.Text}}"");" : null)} }} - {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. {{(success ? ""Passed"" : ""Failed"")}}: {HttpUtility.JavaScriptStringEncode(matcher.Name)}""); - state.Id = currentId - 1;" : null)} + {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id)}} {{(success && partMatchData != null ? partMatchData.StartIndex + partMatchData.Length : startIndex)}}. {{(success ? ""Passed"" : ""Failed"")}}: {HttpUtility.JavaScriptStringEncode(matcher.ToString())}""); + state.Id--;" : null)} return success; }}"; method = generator.Add(method, methodName, code); @@ -1117,18 +1137,18 @@ private static string GenerateMatchPartByTextMatcher(MatcherEngineGenerator gene return method; } - private static string GenerateMatchByFragmentMatcherSection(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher) + private static string GenerateMatchByFragmentMatcherSection(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher, string onSuccess, string onFail) { - return $@"partMatcherData = new FragmentMatchData + return $@"partMatchData = new FragmentMatchData {{ Name = ""{GetEscapedName(fragmentMatcher.Name)}"", - StartIndex = state.CurrentIndex{(fragmentMatcher.ExpressionOrder != null ? $@", + StartIndex = -1{(fragmentMatcher.ExpressionOrder != null ? $@", ExpressionOrder = {fragmentMatcher.ExpressionOrder}" : null)} }}; {(fragmentMatcher.Start != null ? $"StringMatchData startMatchData;" : null)} - {(fragmentMatcher.End != null ? $"StringMatchData endMatchData;" : null)} - success = ({(fragmentMatcher.Start != null ? $"{string.Format(GenerateMatchFragmentBounds(generator, fragmentMatcher, fragmentMatcher.Start), fragmentMatcher.DiscardBounds.ToString().ToLower(), "startMatchData")} && " : null)}{GenerateMatchFragmentParts(generator, fragmentMatcher)}{(fragmentMatcher.End != null ? $" && {string.Format(GenerateMatchFragmentBounds(generator, fragmentMatcher, fragmentMatcher.End), fragmentMatcher.DiscardBounds.ToString().ToLower(), "endMatchData")}" : null)}); + {(fragmentMatcher.End != null ? $"StringMatchData endMatchData = null;" : null)} + success = ({(fragmentMatcher.Start != null ? $"{string.Format(GenerateMatchFragmentBounds(generator, fragmentMatcher, fragmentMatcher.Start), "partMatchData", fragmentMatcher.DiscardBounds.ToString().ToLower(), "startMatchData")} && " : null)}{GenerateMatchFragmentParts(generator, fragmentMatcher)}{(fragmentMatcher.End != null ? $" && {string.Format(GenerateMatchFragmentBounds(generator, fragmentMatcher, fragmentMatcher.End), "partMatchData", fragmentMatcher.DiscardBounds.ToString().ToLower(), "endMatchData")}" : null)}); {(fragmentMatcher.Actions != null && fragmentMatcher.Actions.Count > 0 ? $@" if (success) @@ -1136,73 +1156,76 @@ private static string GenerateMatchByFragmentMatcherSection(MatcherEngineGenerat success = {string.Join(" && ", fragmentMatcher.Actions.Select(action => GenerateMatcherAction(generator, action)))}; }} " : null)} - if (success) {{ - {(!fragmentMatcher.Negate ? $@"partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex;" : null)} - {(fragmentMatcher.Cacheable ? $@"state.MatchCache[new ValueTuple(""{GetEscapedName(fragmentMatcher.Name)}"", startIndex)] = partMatcherData;" : null)} - {(!fragmentMatcher.IsNoise && fragmentMatcher.ExpressionMode != ExpressionMode.None && !fragmentMatcher.Negate ? $"ConvertToExpressionTree(partMatcherData, ExpressionMode.{fragmentMatcher.ExpressionMode});" : null)} + {(!fragmentMatcher.Negate ? $@"partMatchData.Length = state.CurrentIndex - partMatchData.StartIndex; + partMatchData.EndDistinctIndex = state.DistinctIndex;" : null)} + {(!fragmentMatcher.IsNoise && fragmentMatcher.ExpressionMode != ExpressionMode.None && !fragmentMatcher.Negate ? $"ConvertToExpressionTree(partMatchData, ExpressionMode.{fragmentMatcher.ExpressionMode});" : null)} {(fragmentMatcher.BoundsAsParts && fragmentMatcher.Start != null && !fragmentMatcher.Negate ? $@"if (startMatchData != null) {{ - partMatcherData.Parts.Insert(0, startMatchData); + partMatchData.Parts.Insert(0, startMatchData); }}" : null)} {(fragmentMatcher.BoundsAsParts && fragmentMatcher.End != null && !fragmentMatcher.Negate ? $@"if (endMatchData != null) {{ - partMatcherData.Parts.Add(endMatchData); + partMatchData.Parts.Add(endMatchData); }}" : null)} - }}"; + {(!string.IsNullOrWhiteSpace(onSuccess) ? onSuccess : null)} + }}{(!string.IsNullOrWhiteSpace(onFail) ? $@" + else {{ + {onFail} + }} + " : null)}"; } private static string Generate(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher) { string methodName = $"MatchFragment{GetSafeMethodName(fragmentMatcher.Name)}"; - string method = $"{methodName}(ref state, matchData)"; + string method = $"{methodName}(ref state, parentMatchData)"; if (!generator.TryGetMethod(methodName, ref method)) { generator.Add(methodName, method); - string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) + string code = $@"private bool {methodName}(ref State state, FragmentMatchData parentMatchData) {{ - {(generator.LanguageMatcher.LogMatches ? $@"int currentId = ++state.Id; - state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. Try: {GetEscapedName(fragmentMatcher.Name)}"");" : null)} bool success = false; - FragmentMatchData partMatcherData = null; - {(fragmentMatcher.Cacheable ? $@"if (!state.MatchCache.TryGetValue(new ValueTuple(""{GetEscapedName(fragmentMatcher.Name)}"", state.CurrentIndex), out partMatcherData)) - {{" : null)} int startIndex = state.CurrentIndex; + FragmentMatchData partMatchData = null; + {(generator.LanguageMatcher.LogMatches ? $@"state.Id++; + state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id)}} {{startIndex}}. Try: {GetEscapedName(fragmentMatcher.ToString())}"");" : null)} + {(fragmentMatcher.Cacheable ? $@"if (!state.MatchCache.TryGetValue(new ValueTuple(""{GetEscapedName(fragmentMatcher.Name)}"", state.CurrentIndex), out partMatchData)) + {{" : null)} int distinctIndex = state.DistinctIndex; - {GenerateMatchByFragmentMatcherSection(generator, fragmentMatcher)} - else - {{ - {(fragmentMatcher.Cacheable ? $@"state.MatchCache[new ValueTuple(""{GetEscapedName(fragmentMatcher.Name)}"", startIndex)] = null;" : null)} + {GenerateMatchByFragmentMatcherSection(generator, fragmentMatcher, $@"{(fragmentMatcher.Cacheable ? $@"state.MatchCache[new ValueTuple(""{GetEscapedName(fragmentMatcher.Name)}"", startIndex)] = partMatchData;" : null)}", $@"{(fragmentMatcher.Cacheable ? $@"state.MatchCache[new ValueTuple(""{GetEscapedName(fragmentMatcher.Name)}"", startIndex)] = null;" : null)} {(!fragmentMatcher.Negate ? $@"state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex;" : null)} - }} + state.DistinctIndex = distinctIndex;" : null)}")} + {(fragmentMatcher.Negate ? $@"state.CurrentIndex = startIndex; state.DistinctIndex = distinctIndex;" : null)} {(fragmentMatcher.Cacheable ? $@"}}" : null)} - {(fragmentMatcher.Cacheable && !fragmentMatcher.Negate ? $@"else if (success = partMatcherData != null) + {(fragmentMatcher.Cacheable && !fragmentMatcher.Negate ? $@"else if (success = partMatchData != null) {{ - state.CurrentIndex = startIndex + partMatcherData.Length; - state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); - state.DistinctIndex = partMatcherData.EndDistinctIndex; + state.CurrentIndex = partMatchData.StartIndex + partMatchData.Length; + state.DistinctIndex = partMatchData.EndDistinctIndex; }}" : null)} {(!fragmentMatcher.Negate ? $@"if (success) {{ - {(!fragmentMatcher.IsNoise && fragmentMatcher.FallThroughMode == FallThroughMode.All ? "matchData.Parts.AddRange(partMatcherData.Parts);" : null)} - {(!fragmentMatcher.IsNoise && fragmentMatcher.FallThroughMode == FallThroughMode.None ? "matchData.Parts.Add(partMatcherData);" : null)} - {(!fragmentMatcher.IsNoise && fragmentMatcher.FallThroughMode != FallThroughMode.None && fragmentMatcher.FallThroughMode != FallThroughMode.All ? $@"if (partMatcherData.Parts.Count <= {(int)fragmentMatcher.FallThroughMode}) + if (parentMatchData.StartIndex < 0) + {{ + parentMatchData.StartIndex = partMatchData.StartIndex; + }} + {(!fragmentMatcher.IsNoise && fragmentMatcher.FallThroughMode == FallThroughMode.All ? "parentMatchData.Parts.AddRange(partMatchData.Parts);" : null)} + {(!fragmentMatcher.IsNoise && fragmentMatcher.FallThroughMode == FallThroughMode.None ? "parentMatchData.Parts.Add(partMatchData);" : null)} + {(!fragmentMatcher.IsNoise && fragmentMatcher.FallThroughMode.IsCountBased() ? $@"if (partMatchData.Parts.Count <= {(int)fragmentMatcher.FallThroughMode}) {{ - matchData.Parts.AddRange(partMatcherData.Parts); + parentMatchData.Parts.AddRange(partMatchData.Parts); }} else {{ - matchData.Parts.Add(partMatcherData); + parentMatchData.Parts.Add(partMatchData); }}" : null)} {(fragmentMatcher.ClearCache ? "state.MatchCache.Clear();" : null)} }}" : null)} - {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. {{({(fragmentMatcher.Negate ? "!" : null)}success ? ""Passed"" : ""Failed"")}}: {GetEscapedName(fragmentMatcher.Name)}""); - state.Id = currentId - 1;" : null)} + {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id)}} {{state.CurrentIndex}}. {{({(fragmentMatcher.Negate ? "!" : null)}success ? ""Passed"" : ""Failed"")}}: {GetEscapedName(fragmentMatcher.ToString())}""); + state.Id--;" : null)} return {(fragmentMatcher.Negate ? "!" : null)}success; }}"; method = generator.Add(method, methodName, code); diff --git a/src/Matcher/Matcher.csproj b/src/Matcher/Matcher.csproj index d818b3a..02e27ac 100644 --- a/src/Matcher/Matcher.csproj +++ b/src/Matcher/Matcher.csproj @@ -4,7 +4,7 @@ netstandard2.0 Synfron.Staxe.Matcher Synfron.Staxe.Matcher - 2.0.1 + 3.0.0 Daquanne Dwight Synfron © Daquanne Dwight diff --git a/src/Shared/Shared.csproj b/src/Shared/Shared.csproj index 3bea713..030393b 100644 --- a/src/Shared/Shared.csproj +++ b/src/Shared/Shared.csproj @@ -5,7 +5,7 @@ 7.3 Synfron.Staxe.Shared Synfron.Staxe.Shared - 1.0.2 + 1.0.3 Daquanne Dwight Synfron © Daquanne Dwight diff --git a/src/Tests/MatcherTests/Files/CharacterClassDefinition.json b/src/Tests/MatcherTests/Files/CharacterClassDefinition.json index 9bf8140..dd6d60c 100644 --- a/src/Tests/MatcherTests/Files/CharacterClassDefinition.json +++ b/src/Tests/MatcherTests/Files/CharacterClassDefinition.json @@ -1,6 +1,7 @@ { "name": "CharacterClass", "startingFragment": "CharacterClass", + "indexingMode": "None", "patterns": [ { "name": "OpenBracket", diff --git a/src/Tests/MatcherTests/Files/EagerIndexTestDefinition.json b/src/Tests/MatcherTests/Files/EagerIndexTestDefinition.json index 13838ad..81458e0 100644 --- a/src/Tests/MatcherTests/Files/EagerIndexTestDefinition.json +++ b/src/Tests/MatcherTests/Files/EagerIndexTestDefinition.json @@ -203,8 +203,7 @@ }, { "name": "Whitespace", - "pattern": "\\s+", - "mergable": true + "pattern": "\\s+" }, { "name": "Native", @@ -218,10 +217,8 @@ "[Namespace]", "[BlockOrStatement]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, - "partsMatchMode": "Multiple", - "partsPadding": "Whitespace" + "partsMatchMode": "Multiple" }, { "name": "Namespace", @@ -229,11 +226,9 @@ "[NamespaceIdentifier]", "[NamespaceBody]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "start": "Namespace", - "partsMatchMode": "Ordered", - "partsPadding": "Whitespace" + "partsMatchMode": "Ordered" }, { "name": "NamespaceBody", @@ -243,10 +238,8 @@ ], "start": "OpenBrace", "end": "CloseBrace", - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, - "partsMatchMode": "Multiple", - "partsPadding": "Whitespace" + "partsMatchMode": "Multiple" }, { "name": "UsingStatement", @@ -255,7 +248,6 @@ ], "start": "Using", "partsMatchMode": "One", - "partsPadding": "Whitespace", "end": "SemiColon" }, { @@ -292,8 +284,6 @@ "[ClassBody]" ], "start": "Class", - "partsDelimiter": "Whitespace", - "partsPadding": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "Ordered" }, @@ -311,7 +301,6 @@ "[Identifier]" ], "partsDelimiterRequired": true, - "partsPadding": "Whitespace", "partsMatchMode": "One", "start": "Colon", "fallThroughMode": "All" @@ -322,7 +311,6 @@ "[Class]" ], "start": "Static", - "partsPadding": "Whitespace", "partsMatchMode": "One" }, { @@ -360,11 +348,9 @@ "[Constructor]", "[Function]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "minMatchedParts": 0, "partsMatchMode": "Multiple", - "partsPadding": "Whitespace", "start": "OpenBrace", "end": "CloseBrace" }, @@ -374,7 +360,6 @@ "[FunctionParameters]", "[BlockOrStatement]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "Ordered", "clearCache": true @@ -386,9 +371,7 @@ "[FunctionParameters]", "[BlockOrStatement]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, - "partsPadding": "Whitespace", "partsMatchMode": "Ordered", "clearCache": true }, @@ -398,7 +381,6 @@ "[FunctionParameters]", "[BlockOrStatement]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "Ordered", "clearCache": true @@ -409,8 +391,6 @@ "[Identifier]", "[ArgumentValues]" ], - "partsDelimiter": "Whitespace", - "partsPadding": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "Ordered", "start": "New", @@ -421,8 +401,6 @@ "parts": [ "[ArrayInitializer]" ], - "partsDelimiter": "Whitespace", - "partsPadding": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "One", "start": "New" @@ -433,8 +411,6 @@ "[SetterBlock]", "[ArrayBlock]" ], - "partsDelimiter": "Whitespace", - "partsPadding": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "One", "start": "New" @@ -447,7 +423,6 @@ "partsDelimiter": "CommaSeparator", "minMatchedParts": 0, "partsMatchMode": "Multiple", - "partsPadding": "Whitespace", "start": "OpenParens", "end": "CloseParens" }, @@ -456,11 +431,9 @@ "parts": [ "[BlockOrStatement]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "minMatchedParts": 0, "partsMatchMode": "Multiple", - "partsPadding": "Whitespace", "start": "OpenBrace", "end": "CloseBrace" }, @@ -473,7 +446,6 @@ "partsDelimiterRequired": false, "minMatchedParts": 0, "partsMatchMode": "Multiple", - "partsPadding": "Whitespace", "start": "OpenBrace", "end": "CloseBrace" }, @@ -486,7 +458,6 @@ "partsDelimiterRequired": false, "minMatchedParts": 0, "partsMatchMode": "Multiple", - "partsPadding": "Whitespace", "start": "OpenBrace", "end": "CloseBrace" }, @@ -498,7 +469,6 @@ "[Evaluable]" ], "partsMatchMode": "Ordered", - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false }, { @@ -535,7 +505,6 @@ "[OptionalElseIfStatements]", "[OptionalElseStatement]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "Ordered" }, @@ -546,7 +515,6 @@ ], "minMatchedParts": 0, "partsMatchMode": "Multiple", - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "fallThroughMode": "All" }, @@ -565,7 +533,6 @@ "[IfStatement]" ], "start": "Else", - "partsPadding": "Whitespace", "partsMatchMode": "One", "fallThroughMode": "All" }, @@ -575,8 +542,6 @@ "[BlockOrStatement]" ], "start": "Else", - "partsPadding": "Whitespace", - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "One" }, @@ -587,8 +552,6 @@ "[BlockOrStatement]" ], "start": "If", - "partsPadding": "Whitespace", - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "Ordered" }, @@ -598,7 +561,6 @@ "[Evaluable]" ], "partsMatchMode": "One", - "partsPadding": "Whitespace", "start": "OpenParens", "end": "CloseParens", "fallThroughMode": "All" @@ -612,7 +574,6 @@ ], "partsDelimiter": "CommaSeparator", "partsMatchMode": "Ordered", - "partsPadding": "Whitespace", "start": "OpenParens", "end": "CloseParens" }, @@ -623,9 +584,7 @@ "[Of]", "[Evaluable]" ], - "partsDelimiter": "Whitespace", "partsMatchMode": "Ordered", - "partsPadding": "Whitespace", "partsDelimiterRequired": false, "start": "OpenParens", "end": "CloseParens" @@ -637,10 +596,8 @@ "[BlockOrStatement]" ], "start": "ForEach", - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, - "partsMatchMode": "Ordered", - "partsPadding": "Whitespace" + "partsMatchMode": "Ordered" }, { "name": "ForBlock", @@ -649,10 +606,8 @@ "[BlockOrStatement]" ], "start": "For", - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, - "partsMatchMode": "Ordered", - "partsPadding": "Whitespace" + "partsMatchMode": "Ordered" }, { "name": "WhileBlock", @@ -661,10 +616,8 @@ "[BlockOrStatement]" ], "start": "While", - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, - "partsMatchMode": "Ordered", - "partsPadding": "Whitespace" + "partsMatchMode": "Ordered" }, { "name": "Statement", @@ -706,7 +659,6 @@ "[DirectedValuableChain]" ], "partsMatchMode": "One", - "partsPadding": "Whitespace", "fallThroughMode": "All" }, { @@ -715,7 +667,6 @@ "[Identifier]" ], "partsMatchMode": "One", - "partsPadding": "Whitespace", "start": "Variable" }, { @@ -733,7 +684,6 @@ "[AssignmentEqual]", "[Evaluable]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "Ordered" }, @@ -769,7 +719,6 @@ "[ValuableChainStart]", "[OptionalChainables]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "Ordered", "fallThroughMode": "One" @@ -813,7 +762,6 @@ "[DotChainable]", "[DotIdentifier]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "Multiple", "fallThroughMode": "All" @@ -823,8 +771,6 @@ "parts": [ "[Chainable]" ], - "partsPadding": "Whitespace", - "partsDelimiter": "Whitespace", "partsMatchMode": "Multiple", "minMatchedParts": 0, "fallThroughMode": "All" @@ -835,7 +781,6 @@ "[Return]", "[Evaluable]" ], - "partsDelimiter": "Whitespace", "partsMatchMode": "Ordered", "minMatchedParts": 1 }, @@ -856,10 +801,8 @@ "[Equal]", "[Evaluable]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "Ordered", - "partsPadding": "Whitespace", "start": "Variable" }, { @@ -867,7 +810,6 @@ "parts": [ "[Identifier]" ], - "partsPadding": "Whitespace", "end": "SemiColon", "partsMatchMode": "One" }, @@ -878,8 +820,6 @@ "[Equal]", "[Value]" ], - "partsPadding": "Whitespace", - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "end": "SemiColon", "partsMatchMode": "Ordered" @@ -961,7 +901,6 @@ "[Identifier]" ], "start": "Dot", - "partsPadding": "Whitespace", "partsMatchMode": "One" }, { @@ -970,7 +909,6 @@ "[SetterBlock]" ], "start": "Dot", - "partsPadding": "Whitespace", "partsMatchMode": "One", "fallThroughMode": "All" }, @@ -982,7 +920,6 @@ "partsDelimiter": "CommaSeparator", "minMatchedParts": 0, "partsMatchMode": "Multiple", - "partsPadding": "Whitespace", "start": "OpenParens", "end": "CloseParens" }, @@ -1003,7 +940,6 @@ "[ExpressionSuffix]" ], "partsMatchMode": "Ordered", - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "expressionMode": "BinaryTree", "fallThroughMode": "All" @@ -1030,7 +966,6 @@ "[Valuable]" ], "start": "Multiplicative", - "partsPadding": "Whitespace", "partsMatchMode": "One", "boundsAsParts": true, "expressionOrder": 1 @@ -1041,7 +976,6 @@ "[Valuable]" ], "start": "Additive", - "partsPadding": "Whitespace", "partsMatchMode": "One", "boundsAsParts": true, "expressionOrder": 2 @@ -1052,7 +986,6 @@ "[Valuable]" ], "start": "Subtractive", - "partsPadding": "Whitespace", "partsMatchMode": "One", "boundsAsParts": true, "expressionOrder": 2 @@ -1063,7 +996,6 @@ "[Valuable]" ], "start": "Equality", - "partsPadding": "Whitespace", "partsMatchMode": "One", "boundsAsParts": true, "expressionOrder": 4 @@ -1074,7 +1006,6 @@ "[Valuable]" ], "start": "Relational", - "partsPadding": "Whitespace", "partsMatchMode": "One", "boundsAsParts": true, "expressionOrder": 3 @@ -1085,7 +1016,6 @@ "[Valuable]" ], "start": "And", - "partsPadding": "Whitespace", "partsMatchMode": "One", "boundsAsParts": true, "expressionOrder": 7 @@ -1096,7 +1026,6 @@ "[Valuable]" ], "start": "Or", - "partsPadding": "Whitespace", "partsMatchMode": "One", "boundsAsParts": true, "expressionOrder": 8 @@ -1107,7 +1036,6 @@ "[Valuable]" ], "start": "BitwiseAnd", - "partsPadding": "Whitespace", "partsMatchMode": "One", "boundsAsParts": true, "expressionOrder": 5 @@ -1118,7 +1046,6 @@ "[Valuable]" ], "start": "BitwiseOr", - "partsPadding": "Whitespace", "partsMatchMode": "One", "boundsAsParts": true, "expressionOrder": 6 @@ -1128,10 +1055,8 @@ "parts": [ "[Evaluable]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "One", - "partsPadding": "Whitespace", "start": "OpenParens", "end": "CloseParens", "cacheable": true @@ -1141,10 +1066,8 @@ "parts": [ "[Evaluable]" ], - "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "partsMatchMode": "One", - "partsPadding": "Whitespace", "start": "OpenBracket", "end": "CloseBracket", "cacheable": true @@ -1156,7 +1079,6 @@ ], "minMatchedParts": 0, "partsMatchMode": "One", - "partsPadding": "Whitespace", "start": "OpenBracket", "end": "CloseBracket", "fallThroughMode": "All", diff --git a/src/Tests/MatcherTests/Files/EbnfDefinition.json b/src/Tests/MatcherTests/Files/EbnfDefinition.json index fa62ef0..a8e7dac 100644 --- a/src/Tests/MatcherTests/Files/EbnfDefinition.json +++ b/src/Tests/MatcherTests/Files/EbnfDefinition.json @@ -2,6 +2,7 @@ "name": "Ebnf", "startingFragment": "Rules", "indexingMode": "Eager", + "logMatches": true, "patterns": [ { "name": "Number", @@ -15,13 +16,28 @@ "name": "SingleQuoteLiteral", "pattern": "'('!.)*'" }, + { + "name": "OptionalGroup", + "fragment": "OptionalGroup", + "isNoise": true + }, + { + "name": "OpenBracket", + "pattern": "\\[", + "isAuxiliary": true + }, + { + "name": "CloseBracket", + "pattern": "\\]", + "isAuxiliary": true + }, { "name": "SpecialGroup", "pattern": "\\[((\\[!\\]!.)|(\\\\\\[|\\\\\\]))+\\]" }, { "name": "Comment", - "pattern": "\\(\\*(\\*\\)!.)*\\*\\)|/\\*((\\*/)!.)*\\*/", + "pattern": "(\\(\\*(\\*\\)!.)*\\*\\)|/\\*((\\*/)!.)*\\*/)\\s*", "isNoise": true }, { @@ -64,14 +80,6 @@ "name": "CloseBrace", "pattern": "\\}" }, - { - "name": "OpenBracket", - "pattern": "\\[" - }, - { - "name": "CloseBracket", - "pattern": "\\]" - }, { "name": "OpenParens", "pattern": "\\(" @@ -150,13 +158,11 @@ "[RulePrefix]", "[Expression]", "[EndMark]" - ], "minMatchedParts": 2, "partsMatchMode": "Ordered", "partsDelimiter": "Whitespace", - "partsDelimiterRequired": false, - "clearCache": true + "partsDelimiterRequired": false }, { "name": "Expression", @@ -227,7 +233,6 @@ "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, "expressionOrder": 4 - }, { "name": "OptionalComma", @@ -278,7 +283,8 @@ "partsMatchMode": "One", "partsPadding": "Whitespace", "start": "OpenBracket", - "end": "CloseBracket" + "end": "CloseBracket", + "cacheable": true }, { "name": "ZeroOrOneItem", @@ -321,4 +327,4 @@ "cacheable": true } ] -} +} \ No newline at end of file diff --git a/src/Tests/MatcherTests/Shared/LanguageMatchEngineFactory.cs b/src/Tests/MatcherTests/Shared/LanguageMatchEngineFactory.cs index 6e4986a..2a0dc6f 100644 --- a/src/Tests/MatcherTests/Shared/LanguageMatchEngineFactory.cs +++ b/src/Tests/MatcherTests/Shared/LanguageMatchEngineFactory.cs @@ -20,7 +20,7 @@ public static ILanguageMatchEngine Get(GenerationType generationType, LanguageMa { MatcherClassGenerator generator = new MatcherClassGenerator(languageMatcher); Assembly assembly = generator.GetAssembly(); - return (ILanguageMatchEngine)Activator.CreateInstance(assembly.GetType($"Synfron.Staxe.Matcher.{languageMatcher.Name}")); + return (ILanguageMatchEngine)Activator.CreateInstance(assembly.GetType($"Synfron.Staxe.Matcher.{languageMatcher.Name}MatchEngine")); } case GenerationType.PostGen: { diff --git a/src/Tests/MatcherTests/Tests/Actions/MatcherActionTests.cs b/src/Tests/MatcherTests/Tests/Actions/MatcherActionTests.cs index 850abdf..9f42e33 100644 --- a/src/Tests/MatcherTests/Tests/Actions/MatcherActionTests.cs +++ b/src/Tests/MatcherTests/Tests/Actions/MatcherActionTests.cs @@ -130,7 +130,7 @@ public void UpdateVariableMatcherAction_Perform(VariableUpdateAction updateActio [InlineData(VariableValueSource.StringPartsLength, 4)] [InlineData(VariableValueSource.PartsCount, 3)] [InlineData(VariableValueSource.PartsLength, 14)] - [InlineData(VariableValueSource.PartsText, "abcTestd")] + [InlineData(VariableValueSource.PartsXml, "abcd")] [InlineData(VariableValueSource.Value, "abc")] public void CreateVariableMatcherAction_Perform(VariableValueSource source, object expected) { @@ -145,6 +145,7 @@ public void CreateVariableMatcherAction_Perform(VariableValueSource source, obje { new StringMatchData { + Name = "Str1", Length = 3, Text = "abc" }, @@ -155,6 +156,7 @@ public void CreateVariableMatcherAction_Perform(VariableValueSource source, obje }, new StringMatchData { + Name = "Str2", Length = 1, Text = "d" } diff --git a/src/Tests/MatcherTests/Tests/Engine/EngineTests.cs b/src/Tests/MatcherTests/Tests/Engine/EngineTests.cs index cf565b1..9934d6f 100644 --- a/src/Tests/MatcherTests/Tests/Engine/EngineTests.cs +++ b/src/Tests/MatcherTests/Tests/Engine/EngineTests.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using Synfron.Staxe.Matcher; using Synfron.Staxe.Matcher.Input; +using Synfron.Staxe.Matcher.Input.Actions; using Synfron.Staxe.Matcher.Interop.Model; using System; using System.Collections.Generic; @@ -37,7 +38,6 @@ public void LazyIndexTest() string script = File.ReadAllText("Files/Parsables/JibberishScript.lz"); LanguageMatcherDefinition languageMatcherDefinition = JsonConvert.DeserializeObject(definition); LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); - ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); MatcherResult result = engine.Match(script); @@ -65,13 +65,19 @@ public void FragmentPatternMatcherTest() Name = "Test", StartingFragment = "Start", IndexingMode = IndexingMode.Eager, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { - new PatternMatcherDefinition { + new PatternMatcherDefinition + { Name = "A", Pattern = "a" }, new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + }, + new PatternMatcherDefinition { Name = "FragB", Fragment = "Bs" @@ -81,14 +87,36 @@ public void FragmentPatternMatcherTest() Name = "B", Pattern = "b", IsAuxiliary = true + } + }, + Actions = new[] + { + new MatcherActionDefinition + { + Name = "1", + Action = MatcherActionType.CreateVariable, + FirstVariableName = "1", + Source = VariableValueSource.Value, + Value = "1" }, - new PatternMatcherDefinition + new MatcherActionDefinition { - Name = "C", - Pattern = "c" + Name = "Test", + Action = MatcherActionType.Assert, + Assert = AssertType.NotEquals, + FirstVariableName = "1", + SecondVariableName = "1a" + }, + new MatcherActionDefinition + { + Name = "1a", + Action = MatcherActionType.CreateVariable, + FirstVariableName = "1a", + Source = VariableValueSource.Value, + Value = "1" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -100,7 +128,9 @@ public void FragmentPatternMatcherTest() { Name = "Bs", Parts = new [] { "B", "B" }, - PartsMatchMode = MatchMode.Ordered + PartsMatchMode = MatchMode.Ordered, + Actions = new [] { "1", "Test", "1a" }, + Cacheable = true } } }; @@ -114,118 +144,162 @@ public void FragmentPatternMatcherTest() } [Fact] - public void FragmentPatternMatcherNoiseTest() + public void FragmentPatternMatcherAfterNoiseTest() { LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition { Name = "Test", StartingFragment = "Start", IndexingMode = IndexingMode.Eager, - Patterns = new PatternMatcherDefinition[] + Patterns = new[] { - new PatternMatcherDefinition { + new PatternMatcherDefinition + { Name = "A", - Pattern = "a" + Pattern = "a", + IsNoise = true + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" }, new PatternMatcherDefinition { Name = "FragB", - Fragment = "Bs", - IsNoise = true + Fragment = "Bs" }, new PatternMatcherDefinition { Name = "B", Pattern = "b", IsAuxiliary = true + } + }, + Actions = new[] + { + new MatcherActionDefinition + { + Name = "1", + Action = MatcherActionType.CreateVariable, + FirstVariableName = "1", + Source = VariableValueSource.Value, + Value = "1" }, - new PatternMatcherDefinition + new MatcherActionDefinition { - Name = "C", - Pattern = "c" + Name = "Test", + Action = MatcherActionType.Assert, + Assert = AssertType.NotEquals, + FirstVariableName = "1", + SecondVariableName = "1a" + }, + new MatcherActionDefinition + { + Name = "1a", + Action = MatcherActionType.CreateVariable, + FirstVariableName = "1a", + Source = VariableValueSource.Value, + Value = "1" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new[] { new FragmentMatcherDefinition { Name = "Start", - Parts = new [] { "A", "C" }, + Parts = new [] { "C", "[Bs]", "C" }, PartsMatchMode = MatchMode.Ordered }, new FragmentMatcherDefinition { Name = "Bs", Parts = new [] { "B", "B" }, - PartsMatchMode = MatchMode.Ordered + PartsMatchMode = MatchMode.Ordered, + Actions = new [] { "1", "Test", "1a" }, + Cacheable = true } } }; - string script = @"ac"; + string script = @"cabbc"; LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); MatcherResult result = engine.Match(script); Assert.True(result.Success); - Assert.True(CompareXml(@"ac", result.MatchData.ToXml())); + Assert.True(CompareXml(@"cbbc", result.MatchData.ToXml())); } - [Theory] - [InlineData(IndexingMode.Eager)] - [InlineData(IndexingMode.Lazy)] - [InlineData(IndexingMode.None)] - public void SimpleTest(IndexingMode indexingMode) + [Fact] + public void FragmentPatternMatcherNoiseTest() { LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition { Name = "Test", StartingFragment = "Start", - IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + IndexingMode = IndexingMode.Eager, + Patterns = new [] { - new PatternMatcherDefinition { + new PatternMatcherDefinition + { Name = "A", Pattern = "a" }, new PatternMatcherDefinition { - Name = "B", - Pattern = "b" + Name = "C", + Pattern = "c" }, new PatternMatcherDefinition { - Name = "C", - Pattern = "c" + Name = "FragB", + Fragment = "Bs", + IsNoise = true + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b", + IsAuxiliary = true } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { Name = "Start", - Parts = new [] { "A", "B", "C" }, + Parts = new [] { "A", "C" }, + PartsMatchMode = MatchMode.Ordered + }, + new FragmentMatcherDefinition + { + Name = "Bs", + Parts = new [] { "B", "B" }, PartsMatchMode = MatchMode.Ordered } } }; - string script = @"abc"; + string script = @"ac"; LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); MatcherResult result = engine.Match(script); Assert.True(result.Success); - Assert.True(CompareXml(@"abc", result.MatchData.ToXml())); + Assert.True(CompareXml(@"ac", result.MatchData.ToXml())); } - [Fact] - public void PatternNoiseTest() + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void SimpleTest(IndexingMode indexingMode) { LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition { Name = "Test", StartingFragment = "Start", - IndexingMode = IndexingMode.Eager, - Patterns = new PatternMatcherDefinition[] + IndexingMode = indexingMode, + Patterns = new [] { new PatternMatcherDefinition { @@ -235,8 +309,7 @@ public void PatternNoiseTest() new PatternMatcherDefinition { Name = "B", - Pattern = "b", - IsNoise = true + Pattern = "b" }, new PatternMatcherDefinition { @@ -244,40 +317,39 @@ public void PatternNoiseTest() Pattern = "c" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { Name = "Start", - Parts = new [] { "A", "C" }, + Parts = new [] { "A", "B", "C" }, PartsMatchMode = MatchMode.Ordered } } }; - string script = @"ac"; + string script = @"abc"; LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); MatcherResult result = engine.Match(script); Assert.True(result.Success); - Assert.True(CompareXml(@"ac", result.MatchData.ToXml())); + Assert.True(CompareXml(@"abc", result.MatchData.ToXml())); } [Fact] - public void MergableTest() + public void PatternNoiseTest() { LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition { Name = "Test", StartingFragment = "Start", IndexingMode = IndexingMode.Eager, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { - new PatternMatcherDefinition + new PatternMatcherDefinition { Name = "A", - Pattern = "a+", - Mergable = true + Pattern = "a" }, new PatternMatcherDefinition { @@ -289,9 +361,9 @@ public void MergableTest() { Name = "C", Pattern = "c" - }, + } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -301,13 +373,13 @@ public void MergableTest() } } }; - string script = @"abac"; + string script = @"ac"; LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); MatcherResult result = engine.Match(script); Assert.True(result.Success); - Assert.True(CompareXml(@"aac", result.MatchData.ToXml())); + Assert.True(CompareXml(@"ac", result.MatchData.ToXml())); } [Theory] @@ -321,7 +393,7 @@ public void BoundsAsPartsTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { new PatternMatcherDefinition { @@ -339,7 +411,7 @@ public void BoundsAsPartsTest(IndexingMode indexingMode) Pattern = "c" }, }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -372,7 +444,7 @@ public void BoundsTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { new PatternMatcherDefinition { @@ -390,7 +462,7 @@ public void BoundsTest(IndexingMode indexingMode) Pattern = "c" }, }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -422,7 +494,7 @@ public void DiscardBoundsTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { new PatternMatcherDefinition { @@ -440,7 +512,7 @@ public void DiscardBoundsTest(IndexingMode indexingMode) Pattern = "c" }, }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -469,9 +541,10 @@ public void PreMatchSkipAuxiliaryTest() Name = "Test", StartingFragment = "Start", IndexingMode = IndexingMode.Eager, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { - new PatternMatcherDefinition { + new PatternMatcherDefinition + { Name = "A", Pattern = "a" }, @@ -492,7 +565,7 @@ public void PreMatchSkipAuxiliaryTest() Pattern = "c" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -522,7 +595,7 @@ public void LessThanMinMatchedPartsTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { new PatternMatcherDefinition { @@ -530,7 +603,7 @@ public void LessThanMinMatchedPartsTest(IndexingMode indexingMode) Pattern = "b" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -562,6 +635,174 @@ public void LessThanMinMatchedPartsTest(IndexingMode indexingMode) Assert.True(CompareXml(@"bb", result.MatchData.ToXml())); } + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void NoMinMatchedMultiplePartsTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new[] + { + new PatternMatcherDefinition + { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + } + }, + Fragments = new[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[As]", "[Bs]" }, + PartsMatchMode = MatchMode.Ordered + }, + new FragmentMatcherDefinition + { + Name = "As", + Parts = new [] { "A" }, + PartsMatchMode = MatchMode.Multiple, + MinMatchedParts = 0 + }, + new FragmentMatcherDefinition + { + Name = "Bs", + Parts = new [] { "B" }, + PartsMatchMode = MatchMode.Multiple + } + } + }; + string script = @"bb"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"bb", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void NoMinMatchedOrderedPartsTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new[] + { + new PatternMatcherDefinition + { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + } + }, + Fragments = new[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[As]", "[Bs]" }, + PartsMatchMode = MatchMode.Ordered + }, + new FragmentMatcherDefinition + { + Name = "As", + Parts = new [] { "A" }, + PartsMatchMode = MatchMode.Ordered, + MinMatchedParts = 0 + }, + new FragmentMatcherDefinition + { + Name = "Bs", + Parts = new [] { "B" }, + PartsMatchMode = MatchMode.Multiple + } + } + }; + string script = @"bb"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"bb", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void NoMinMatchedOnePartsTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new[] + { + new PatternMatcherDefinition + { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + } + }, + Fragments = new[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[As]", "[Bs]" }, + PartsMatchMode = MatchMode.Ordered + }, + new FragmentMatcherDefinition + { + Name = "As", + Parts = new [] { "A" }, + PartsMatchMode = MatchMode.One, + MinMatchedParts = 0 + }, + new FragmentMatcherDefinition + { + Name = "Bs", + Parts = new [] { "B" }, + PartsMatchMode = MatchMode.Multiple + } + } + }; + string script = @"bb"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"bb", result.MatchData.ToXml())); + } + [Theory] [InlineData(IndexingMode.Eager)] [InlineData(IndexingMode.Lazy)] @@ -573,7 +814,7 @@ public void EqualToMinMatchedMultiplePartsTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { new PatternMatcherDefinition { @@ -581,7 +822,7 @@ public void EqualToMinMatchedMultiplePartsTest(IndexingMode indexingMode) Pattern = "b" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -624,7 +865,7 @@ public void EqualToMinMatchedOrderedPartsTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { new PatternMatcherDefinition { @@ -632,7 +873,7 @@ public void EqualToMinMatchedOrderedPartsTest(IndexingMode indexingMode) Pattern = "b" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -675,7 +916,7 @@ public void NegateTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { new PatternMatcherDefinition { @@ -693,7 +934,7 @@ public void NegateTest(IndexingMode indexingMode) Pattern = "c" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -749,9 +990,10 @@ public void PartsDelimiterNotRequiredTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { - new PatternMatcherDefinition { + new PatternMatcherDefinition + { Name = "A", Pattern = "a" }, @@ -766,7 +1008,7 @@ public void PartsDelimiterNotRequiredTest(IndexingMode indexingMode) Pattern = "c" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -810,9 +1052,10 @@ public void PartsDelimiterRequiredTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { - new PatternMatcherDefinition { + new PatternMatcherDefinition + { Name = "A", Pattern = "a" }, @@ -827,7 +1070,7 @@ public void PartsDelimiterRequiredTest(IndexingMode indexingMode) Pattern = "c" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -870,9 +1113,10 @@ public void FallThroughOneModeTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { - new PatternMatcherDefinition { + new PatternMatcherDefinition + { Name = "A", Pattern = "a" }, @@ -882,7 +1126,7 @@ public void FallThroughOneModeTest(IndexingMode indexingMode) Pattern = "b" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -920,9 +1164,10 @@ public void FallThroughAllModeTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { - new PatternMatcherDefinition { + new PatternMatcherDefinition + { Name = "A", Pattern = "a" }, @@ -932,7 +1177,7 @@ public void FallThroughAllModeTest(IndexingMode indexingMode) Pattern = "b" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -969,9 +1214,10 @@ public void PartsPaddingTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { - new PatternMatcherDefinition { + new PatternMatcherDefinition + { Name = "A", Pattern = "a" }, @@ -986,7 +1232,7 @@ public void PartsPaddingTest(IndexingMode indexingMode) Pattern = "c" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -1030,9 +1276,10 @@ public void BinaryTreeExpressionTest(IndexingMode indexingMode) Name = "Test", StartingFragment = "Start", IndexingMode = indexingMode, - Patterns = new PatternMatcherDefinition[] + Patterns = new [] { - new PatternMatcherDefinition { + new PatternMatcherDefinition + { Name = "E", Pattern = "^" }, @@ -1062,7 +1309,7 @@ public void BinaryTreeExpressionTest(IndexingMode indexingMode) Pattern = "2" } }, - Fragments = new FragmentMatcherDefinition[] + Fragments = new [] { new FragmentMatcherDefinition { @@ -1116,6 +1363,116 @@ public void BinaryTreeExpressionTest(IndexingMode indexingMode) Assert.True(CompareXml(@"2+2-2^2*2/2", result.MatchData.ToXml())); } + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void ActionMatcherTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new [] + { + new PatternMatcherDefinition + { + Name = "A", + Pattern = "a" + } + }, + Actions = new [] + { + new MatcherActionDefinition + { + Name = "Create1", + Action = MatcherActionType.CreateVariable, + Source = VariableValueSource.Value, + FirstVariableName = "count", + Value = 0 + }, + new MatcherActionDefinition + { + Name = "Create2", + Action = MatcherActionType.CreateVariable, + Source = VariableValueSource.Value, + FirstVariableName = "change", + Value = 1 + }, + new MatcherActionDefinition + { + Name = "Create3", + Action = MatcherActionType.CreateVariable, + Source = VariableValueSource.Value, + FirstVariableName = "expected", + Value = 3 + }, + new MatcherActionDefinition + { + Name = "Increment", + Action = MatcherActionType.UpdateVariable, + Change = VariableUpdateAction.Add, + FirstVariableName = "count", + SecondVariableName = "change" + }, + new MatcherActionDefinition + { + Name = "Assert", + Action = MatcherActionType.Assert, + Assert = AssertType.Equals, + SecondVariableName = "count", + FirstVariableName = "expected" + }, + + }, + Fragments = new [] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[A1]", "[A2]", "[A3]", "[A4]" }, + PartsMatchMode = MatchMode.One + }, + new FragmentMatcherDefinition + { + Name = "A1", + Parts = new [] { "A" }, + PartsMatchMode = MatchMode.Multiple, + Actions = new [] { "Create1", "Create2", "Create3", "Increment", "Assert" } + }, + new FragmentMatcherDefinition + { + Name = "A2", + Parts = new [] { "A" }, + PartsMatchMode = MatchMode.Multiple, + Actions = new [] { "Increment", "Assert" } + }, + new FragmentMatcherDefinition + { + Name = "A3", + Parts = new [] { "A" }, + PartsMatchMode = MatchMode.Multiple, + Actions = new [] { "Increment", "Assert" } + }, + new FragmentMatcherDefinition + { + Name = "A4", + Parts = new [] { "A" }, + PartsMatchMode = MatchMode.Multiple, + Actions = new [] { "Increment", "Assert" } + } + } + }; + string script = @"a"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"a", result.MatchData.ToXml())); + } + //[Fact] public void LazyIndexPerformanceTest() { diff --git a/src/Tests/MatcherTests/Tests/Interop/DefinitionConverterTests.cs b/src/Tests/MatcherTests/Tests/Interop/DefinitionConverterTests.cs index c304226..50c2719 100644 --- a/src/Tests/MatcherTests/Tests/Interop/DefinitionConverterTests.cs +++ b/src/Tests/MatcherTests/Tests/Interop/DefinitionConverterTests.cs @@ -36,7 +36,7 @@ public void DefinitionConverter_ConvertLanguageMatcher() new MatcherActionDefinition { Name = "TestAction1", - ActionType = MatcherActionType.UpdateVariable, + Action = MatcherActionType.UpdateVariable, Change = VariableUpdateAction.Subtract, FirstVariableName = "Var1", SecondVariableName = "Var2" @@ -44,7 +44,7 @@ public void DefinitionConverter_ConvertLanguageMatcher() new MatcherActionDefinition { Name = "TestAction2", - ActionType = MatcherActionType.Assert, + Action = MatcherActionType.Assert, Assert = AssertType.LessThanOrEquals, FirstVariableName = "Var1", SecondVariableName = "Var2" @@ -52,16 +52,16 @@ public void DefinitionConverter_ConvertLanguageMatcher() new MatcherActionDefinition { Name = "TestAction3", - ActionType = MatcherActionType.CreateVariable, - Source = VariableValueSource.PartsText, + Action = MatcherActionType.CreateVariable, + Source = VariableValueSource.PartsXml, FirstVariableName = "Var1", Value = 20 }, new MatcherActionDefinition { Name = "TestAction4", - ActionType = MatcherActionType.CreateVariable, - Source = VariableValueSource.PartsText, + Action = MatcherActionType.CreateVariable, + Source = VariableValueSource.PartsXml, FirstVariableName = "Var2", Value = 10 } @@ -99,8 +99,7 @@ public void DefinitionConverter_ConvertLanguageMatcher() Name = "TestPattern1", Pattern = "A", IsAuxiliary = true, - IsNoise = true, - Mergable = true + IsNoise = true } }, Fragments = new[] diff --git a/src/Tests/MatcherTests/Tests/Interop/Ebnf/ConversionTests.cs b/src/Tests/MatcherTests/Tests/Interop/Ebnf/ConversionTests.cs index 6b1b996..dd8acd4 100644 --- a/src/Tests/MatcherTests/Tests/Interop/Ebnf/ConversionTests.cs +++ b/src/Tests/MatcherTests/Tests/Interop/Ebnf/ConversionTests.cs @@ -1,6 +1,9 @@ -using Synfron.Staxe.Matcher; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Synfron.Staxe.Matcher; using Synfron.Staxe.Matcher.Input; using Synfron.Staxe.Matcher.Interop.Bnf; +using Synfron.Staxe.Matcher.Interop.Model; using System.Diagnostics; using System.IO; using System.Linq; @@ -29,6 +32,12 @@ public void ConversionTest(string grammarFileName, int numRules) Stopwatch watch = new Stopwatch(); watch.Start(); + string json = JsonConvert.SerializeObject(DefinitionConverter.Convert(languageMatcher), new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore, + Converters = new [] { new StringEnumConverter() } + }); LanguageMatchEngine languageMatchEngine = LanguageMatchEngine.Build(languageMatcher); MatcherResult result = languageMatchEngine.Match(parsable); diff --git a/src/Tests/MatcherTests/Tests/Interop/MatcherActionDefintionComparer.cs b/src/Tests/MatcherTests/Tests/Interop/MatcherActionDefintionComparer.cs index f881be5..be58e78 100644 --- a/src/Tests/MatcherTests/Tests/Interop/MatcherActionDefintionComparer.cs +++ b/src/Tests/MatcherTests/Tests/Interop/MatcherActionDefintionComparer.cs @@ -15,7 +15,7 @@ public bool Equals(MatcherActionDefinition x, MatcherActionDefinition y) && x.SecondVariableName == y.SecondVariableName && x.Name == y.Name && x.Assert == y.Assert - && x.ActionType == y.ActionType + && x.Action == y.Action && x.Value == y.Value; } diff --git a/src/Tests/MatcherTests/Tests/Interop/PatternMatcherDefinitionComparer.cs b/src/Tests/MatcherTests/Tests/Interop/PatternMatcherDefinitionComparer.cs index 6213b18..fc254a7 100644 --- a/src/Tests/MatcherTests/Tests/Interop/PatternMatcherDefinitionComparer.cs +++ b/src/Tests/MatcherTests/Tests/Interop/PatternMatcherDefinitionComparer.cs @@ -10,13 +10,11 @@ public class PatternMatcherDefinitionComparer : IEqualityComparer Run(string code, InstructionExecutor executor = null) { - (Synfron.Staxe.Matcher.Data.IMatchData matchData, bool success, int _, int? _, string _) = LanguageMatchEngine.Match(code); + (Synfron.Staxe.Matcher.Data.IMatchData matchData, bool success, int _, int? _, string log) = LanguageMatchEngine.Match(code); Assert.True(success); @@ -282,7 +282,7 @@ public void InvalidAssignmentTest() LanguageConstraintException exception = Assert.Throws(() => generator.Generate(matchData)); Assert.Contains("Cannot assign to the return of a function.", exception.Message); - Assert.Equal(invalidAssignment.IndexOf("= 7"), exception.Position); + Assert.Equal(invalidAssignment.IndexOf(" = 7"), exception.Position); } [Fact]