From fed4dceb6bb092cd98e6af16bde41e8871dea102 Mon Sep 17 00:00:00 2001 From: tinygrox Date: Thu, 19 Mar 2026 17:22:07 +0800 Subject: [PATCH 1/3] fix(parser): avoid misparsing translated text in string expressions Replace \w-based string expression matching with explicit ASCII identifier rules so non-English contract text is not mistaken for function calls or identifier content. --- .../ExpressionParser/ExpressionParser.cs | 6 +++--- .../Parsers/SimpleTypes/ListExpressionParser.cs | 2 +- .../Parsers/SimpleTypes/StringExpressionParser.cs | 15 ++++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/source/ContractConfigurator/ExpressionParser/ExpressionParser.cs b/source/ContractConfigurator/ExpressionParser/ExpressionParser.cs index 7bf252f62..f49dc8eb6 100644 --- a/source/ContractConfigurator/ExpressionParser/ExpressionParser.cs +++ b/source/ContractConfigurator/ExpressionParser/ExpressionParser.cs @@ -1353,7 +1353,7 @@ public virtual T ParseDataStoreIdentifier(Token token) public Token ParseIdentifier() { - Match m = Regex.Match(expression, @"([A-Za-z][\w\d]*).*"); + Match m = Regex.Match(expression, @"([A-Za-z][A-Za-z0-9_]*).*"); string identifier = m.Groups[1].Value; expression = (expression.Length > identifier.Length ? expression.Substring(identifier.Length) : ""); @@ -1365,7 +1365,7 @@ public Token ParseIdentifier() public Token ParseSpecialIdentifier() { - Match m = Regex.Match(expression, @"^@(/?(?>([A-Za-z][\w\d]*|\.\.)/)*[A-Za-z][\w\d:]*).*"); + Match m = Regex.Match(expression, @"^@(/?(?>(?:[A-Za-z][A-Za-z0-9_]*|\.\.)/)*[A-Za-z][A-Za-z0-9_]*(?::[A-Za-z][A-Za-z0-9_]*)*).*"); string identifier = m.Groups[1].Value; expression = (expression.Length > identifier.Length + 1 ? expression.Substring(identifier.Length + 1) : ""); @@ -1374,7 +1374,7 @@ public Token ParseSpecialIdentifier() public Token ParseDataStoreIdentifier() { - Match m = Regex.Match(expression, @"^\$(/?(?>([A-Za-z][\w\d]*|\.\.)/)*[A-Za-z][\w\d:]*).*"); + Match m = Regex.Match(expression, @"^\$(/?(?>(?:[A-Za-z][A-Za-z0-9_]*|\.\.)/)*[A-Za-z][A-Za-z0-9_]*(?::[A-Za-z][A-Za-z0-9_]*)*).*"); string identifier = m.Groups[1].Value; expression = (expression.Length > identifier.Length + 1 ? expression.Substring(identifier.Length + 1) : ""); diff --git a/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/ListExpressionParser.cs b/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/ListExpressionParser.cs index 8a5d5d374..3a4cc0c38 100644 --- a/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/ListExpressionParser.cs +++ b/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/ListExpressionParser.cs @@ -230,7 +230,7 @@ public TResult ParseWhereMethod(List obj) ParseToken("("); // Get the identifier for the object - Match m = Regex.Match(expression, @"([A-Za-z][\w\d]*)[\s]*=>[\s]*(.*)"); + Match m = Regex.Match(expression, @"([A-Za-z][A-Za-z0-9_]*)[\s]*=>[\s]*(.*)"); string identifier = m.Groups[1].Value; expression = (string.IsNullOrEmpty(identifier) ? expression : m.Groups[2].Value); diff --git a/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs b/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs index e9b107aa1..3d64738b3 100644 --- a/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs +++ b/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs @@ -90,14 +90,15 @@ public override TResult ParseStatement() value = (string)(object)result ?? ""; } - else + else if (token != null && token.tokenType == TokenType.FUNCTION) { // Check for an immediate function call - Match m = Regex.Match(expression, @"^\w[\w\d]*\("); - if (m.Success) - { - return base.ParseStatement(); - } + // Match m = Regex.Match(expression, @"^\w[\w\d]*\("); + // if (m.Success) + // { + // return base.ParseStatement(); + // } + return base.ParseStatement(); } while (expression.Length > 0) @@ -110,7 +111,7 @@ public override TResult ParseStatement() int dataStoreIdentifierIndex = m.Success ? m.Index : -1; // Look for function calls - m = Regex.Match(expression, @"(\A|\s)\w[\w\d]*\("); + m = Regex.Match(expression, @"(\A|\s)[A-Za-z][A-Za-z0-9_]*\("); int functionIndex = m == Match.Empty ? -1 : m.Index; // Look for an end quote From 356b296e3a75f02dfdf0196c3b8de41ba2a5ddc6 Mon Sep 17 00:00:00 2001 From: tinygrox Date: Thu, 19 Mar 2026 17:28:26 +0800 Subject: [PATCH 2/3] Update StringExpressionParser.cs --- .../Parsers/SimpleTypes/StringExpressionParser.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs b/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs index 3d64738b3..b6dee3376 100644 --- a/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs +++ b/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs @@ -93,12 +93,11 @@ public override TResult ParseStatement() else if (token != null && token.tokenType == TokenType.FUNCTION) { // Check for an immediate function call - // Match m = Regex.Match(expression, @"^\w[\w\d]*\("); - // if (m.Success) - // { - // return base.ParseStatement(); - // } - return base.ParseStatement(); + Match m = Regex.Match(expression, @"^[A-Za-z][A-Za-z0-9_]*\("); + if (m.Success) + { + return base.ParseStatement(); + } } while (expression.Length > 0) From 3885eac6bc03d12bd43da592143992e82282063a Mon Sep 17 00:00:00 2001 From: tinygrox Date: Thu, 19 Mar 2026 17:28:43 +0800 Subject: [PATCH 3/3] Update StringExpressionParser.cs --- .../Parsers/SimpleTypes/StringExpressionParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs b/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs index b6dee3376..cdefcf8c9 100644 --- a/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs +++ b/source/ContractConfigurator/ExpressionParser/Parsers/SimpleTypes/StringExpressionParser.cs @@ -90,7 +90,7 @@ public override TResult ParseStatement() value = (string)(object)result ?? ""; } - else if (token != null && token.tokenType == TokenType.FUNCTION) + else { // Check for an immediate function call Match m = Regex.Match(expression, @"^[A-Za-z][A-Za-z0-9_]*\(");