From 0441b5dc83b6808dd3f572db1d84e6aa09b26c9e Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sat, 20 Dec 2025 20:35:25 +0400 Subject: [PATCH 01/52] =?UTF-8?q?=D1=83=D0=BF=D0=BE=D1=80=D1=8F=D0=B4?= =?UTF-8?q?=D0=BE=D1=87=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=82=D0=BE?= =?UTF-8?q?=D0=BA=D0=B5=D0=BD=D0=BE=D0=B2;=20=D0=B7=D0=B0=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=86=D0=B5=D0=BF=D0=BE=D1=87=D0=B5=D0=BA=20?= =?UTF-8?q?=D1=81=D1=80=D0=B0=D0=B2=D0=BD=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Language/LanguageDef.cs | 114 +++++++++++------- .../LexicalAnalysis/Token.cs | 13 +- 2 files changed, 79 insertions(+), 48 deletions(-) diff --git a/src/OneScript.Language/LanguageDef.cs b/src/OneScript.Language/LanguageDef.cs index 1583db1b0..3f8100fe3 100644 --- a/src/OneScript.Language/LanguageDef.cs +++ b/src/OneScript.Language/LanguageDef.cs @@ -247,6 +247,7 @@ public static string GetTokenName(Token token) return Enum.GetName(typeof(Token), token); } + public static string GetTokenAlias(Token token) { if (_keywords.TryGetValue(token,out var strings)) @@ -257,11 +258,9 @@ public static string GetTokenAlias(Token token) return Enum.GetName(typeof(Token), token); } - public static Token GetToken(string tokText) { - Token result; - if (_stringToToken.TryGetValue(tokText, out result)) + if (_stringToToken.TryGetValue(tokText, out Token result)) { return result; } @@ -284,22 +283,28 @@ public static bool IsBuiltInFunction(Token token) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsBinaryOperator(Token token) - { - return token == Token.Plus - || token == Token.Minus - || token == Token.Multiply - || token == Token.Division - || token == Token.Modulo - || token == Token.And - || token == Token.Or - || token == Token.LessThan - || token == Token.LessOrEqual - || token == Token.MoreThan - || token == Token.MoreOrEqual - || token == Token.Equal - || token == Token.NotEqual; - } - + { + switch (token) + { + case Token.Plus: + case Token.Minus: + case Token.Multiply: + case Token.Division: + case Token.Modulo: + case Token.Equal: + case Token.LessThan: + case Token.LessOrEqual: + case Token.MoreThan: + case Token.MoreOrEqual: + case Token.NotEqual: + case Token.And: + case Token.Or: + return true; + default: + return false; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLogicalBinaryOperator(Token token) { @@ -315,24 +320,42 @@ public static bool IsUnaryOperator(Token token) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLiteral(in Lexem lex) { - return lex.Type == LexemType.StringLiteral - || lex.Type == LexemType.NumberLiteral - || lex.Type == LexemType.BooleanLiteral - || lex.Type == LexemType.DateLiteral - || lex.Type == LexemType.UndefinedLiteral - || lex.Type == LexemType.NullLiteral; + switch (lex.Type) + { + case LexemType.StringLiteral: + case LexemType.NumberLiteral: + case LexemType.BooleanLiteral: + case LexemType.DateLiteral: + case LexemType.UndefinedLiteral: + case LexemType.NullLiteral: + return true; + default: + return false; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsValidPropertyName(in Lexem lex) { - return lex.Type == LexemType.Identifier - || lex.Type == LexemType.BooleanLiteral - || lex.Type == LexemType.NullLiteral - || lex.Type == LexemType.UndefinedLiteral - || lex.Token == Token.And - || lex.Token == Token.Or - || lex.Token == Token.Not; + switch (lex.Type) + { + case LexemType.Identifier: + case LexemType.BooleanLiteral: + case LexemType.NullLiteral: + case LexemType.UndefinedLiteral: + return true; + + default: + switch (lex.Token) + { + case Token.And: + case Token.Or: + case Token.Not: + return true; + default: + return false; + } + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -394,17 +417,22 @@ public static bool IsBeginOfStatement(Token token) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEndOfBlockToken(Token token) - { - return token == Token.EndIf - || token == Token.EndProcedure - || token == Token.EndFunction - || token == Token.Else - || token == Token.EndLoop - || token == Token.EndTry - || token == Token.EndOfText - || token == Token.ElseIf - || token == Token.Exception - ; + { + switch (token) + { + case Token.EndIf: + case Token.EndProcedure: + case Token.EndFunction: + case Token.Else: + case Token.EndLoop: + case Token.EndTry: + case Token.EndOfText: + case Token.ElseIf: + case Token.Exception: + return true; + default: + return false; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/OneScript.Language/LexicalAnalysis/Token.cs b/src/OneScript.Language/LexicalAnalysis/Token.cs index aca7738f1..681fdf324 100644 --- a/src/OneScript.Language/LexicalAnalysis/Token.cs +++ b/src/OneScript.Language/LexicalAnalysis/Token.cs @@ -43,13 +43,15 @@ public enum Token RemoveHandler, Async, Await, - Goto, - - // operators + Goto, + + // operators + UnaryPlus, + UnaryMinus, + // binary begin + // recommend to be in continuous block Plus, Minus, - UnaryPlus, - UnaryMinus, Multiply, Division, Modulo, @@ -61,6 +63,7 @@ public enum Token NotEqual, And, Or, + // binary end Not, Dot, OpenPar, From f3bdb661cf7bf59f0e9cfe418555eccb5e598a52 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sun, 21 Dec 2025 01:38:19 +0400 Subject: [PATCH 02/52] =?UTF-8?q?=D0=BA=D1=8D=D1=88=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D1=8C=20=D1=80=D0=B0=D0=B7=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LexicalAnalysis/SourceCodeIterator.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs b/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs index d82d9e2c9..75f290a76 100644 --- a/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs +++ b/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs @@ -18,6 +18,7 @@ public class SourceCodeIterator : ISourceCodeIndexer private int _lineCounter; private int _index; private int _startPosition; + private int _codeLength; private List _lineBounds; private bool _onNewLine; @@ -44,6 +45,7 @@ internal SourceCodeIterator() private void InitOnString(string code) { _code = code; + _codeLength = code.Length; int cap = code.Length < 512 ? 32 : 512; _lineBounds = new List(cap); _index = OUT_OF_TEXT; @@ -70,7 +72,7 @@ private void InitOnString(string code) public int CurrentColumn { get - { + { if (_startPosition == OUT_OF_TEXT) { return OUT_OF_TEXT; @@ -87,13 +89,13 @@ public int CurrentColumn public bool MoveNext() { _index++; - if (_index < _code.Length) + if (_index < _codeLength) { _currentSymbol = _code[_index]; if (_currentSymbol == '\n') { _lineCounter++; - if (_index < _code.Length) + if (_index < _codeLength) _lineBounds.Add(_index + 1); } @@ -109,7 +111,7 @@ public bool MoveNext() public char PeekNext() { char result = '\0'; - if (_index + 1 < _code.Length) + if (_index + 1 < _codeLength) { result = _code[_index + 1]; } @@ -155,7 +157,7 @@ public bool SkipSpaces() } } - if (_index >= _code.Length) + if (_index >= _codeLength) { return false; } @@ -200,7 +202,7 @@ public ReadOnlyMemory GetContentSpan() { int len; - if (_startPosition == _index && _startPosition < _code.Length) + if (_startPosition == _index && _startPosition < _codeLength) { len = 1; } From 6323232b04f2de76da6c728ae6bfcee5a4040fce Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sun, 21 Dec 2025 14:30:51 +0400 Subject: [PATCH 03/52] =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BB=D0=B8=D1=82=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2/=D1=81=D0=BB=D0=BE=D0=B2=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=82=D0=B8=D0=BF=D1=8B=20=D0=B8=20=D1=80=D0=B5=D1=84=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BB=D0=B5=D0=BA?= =?UTF-8?q?=D1=81=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Language/LanguageDef.cs | 66 +++++--- .../LexicalAnalysis/WordLexerState.cs | 154 ++++++++---------- 2 files changed, 112 insertions(+), 108 deletions(-) diff --git a/src/OneScript.Language/LanguageDef.cs b/src/OneScript.Language/LanguageDef.cs index 3f8100fe3..aac5dcec4 100644 --- a/src/OneScript.Language/LanguageDef.cs +++ b/src/OneScript.Language/LanguageDef.cs @@ -5,10 +5,11 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.Language.LexicalAnalysis; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -using OneScript.Language.LexicalAnalysis; +using static OneScript.Language.LanguageDef; namespace OneScript.Language { @@ -21,14 +22,20 @@ public static class LanguageDef private static readonly IdentifiersTrie _stringToToken = new IdentifiersTrie(); - private static readonly IdentifiersTrie _undefined = new IdentifiersTrie(); - private static readonly IdentifiersTrie _booleans = new IdentifiersTrie(); - private static readonly IdentifiersTrie _logicalOp = new IdentifiersTrie(); - - private static readonly IdentifiersTrie _preprocImport = new IdentifiersTrie(); - const int BUILTINS_INDEX = (int)Token.ByValParam; + public enum WordType + { + Undefined, + Boolean, + Logical, + Null, + Preproc, + None + }; + + private static readonly IdentifiersTrie _specwords = new IdentifiersTrie(); + static LanguageDef() { _priority.Add(Token.Plus, 5); @@ -52,21 +59,26 @@ static LanguageDef() #region constants - _undefined.Add("Undefined", true); - _undefined.Add("Неопределено", true); + _specwords.Add("Undefined", WordType.Undefined); + _specwords.Add("Неопределено", WordType.Undefined); - _booleans.Add("True", true); - _booleans.Add("False", true); - _booleans.Add("Истина", true); - _booleans.Add("Ложь", true); + _specwords.Add("True", WordType.Boolean); + _specwords.Add("False", WordType.Boolean); + _specwords.Add("Истина", WordType.Boolean); + _specwords.Add("Ложь", WordType.Boolean); - _logicalOp.Add("And", true); - _logicalOp.Add("Or", true); - _logicalOp.Add("Not", true); + _specwords.Add("And", WordType.Logical); + _specwords.Add("Or", WordType.Logical); + _specwords.Add("Not", WordType.Logical); - _logicalOp.Add("И", true); - _logicalOp.Add("ИЛИ", true); - _logicalOp.Add("НЕ", true); + _specwords.Add("И", WordType.Logical); + _specwords.Add("ИЛИ", WordType.Logical); + _specwords.Add("НЕ", WordType.Logical); + + _specwords.Add("NULL", WordType.Null); + + _specwords.Add("Использовать", WordType.Preproc); + _specwords.Add("Use", WordType.Preproc); #endregion @@ -216,8 +228,6 @@ static LanguageDef() #endregion - _preprocImport.Add("Использовать", true); - _preprocImport.Add("Use", true); } private static void AddToken(Token token, string name) @@ -433,18 +443,24 @@ public static bool IsEndOfBlockToken(Token token) default: return false; } + } + + + public static WordType GetWordType(string value) + { + return _specwords.TryGetValue(value, out var wordType)? wordType : WordType.None; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsBooleanLiteralString(string value) { - return _booleans.TryGetValue(value, out var nodeIsFilled) && nodeIsFilled; + return _specwords.TryGetValue(value, out var wordType) && wordType == WordType.Boolean; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsUndefinedString(string value) { - return _undefined.TryGetValue(value, out var nodeIsFilled) && nodeIsFilled; + return _specwords.TryGetValue(value, out var wordType) && wordType == WordType.Undefined; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -456,13 +472,13 @@ public static bool IsNullString(string value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLogicalOperatorString(string content) { - return _logicalOp.TryGetValue(content, out var nodeIsFilled) && nodeIsFilled; + return _specwords.TryGetValue(content, out var wordType) && wordType == WordType.Logical; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsImportDirective(string value) { - return _preprocImport.TryGetValue(value, out var nodeIsFilled) && nodeIsFilled; + return _specwords.TryGetValue(value, out var wordType) && wordType == WordType.Preproc; } } } diff --git a/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs b/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs index c9249ca46..1c704ddd7 100644 --- a/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs @@ -11,91 +11,79 @@ public class WordLexerState : LexerState { public override Lexem ReadNextLexem(SourceCodeIterator iterator) { - bool isEndOfText = false; - char cs = '\0'; - int start = iterator.Position; - int currentLine = iterator.CurrentLine; - int currentColumn = iterator.CurrentColumn; - while (true) - { - if (!isEndOfText) - { - cs = iterator.CurrentSymbol; - } - - if (SpecialChars.IsDelimiter(cs) || isEndOfText) - { - var content = iterator.GetContents(); + var location = new CodeRange(iterator.CurrentLine, iterator.CurrentColumn); - Lexem lex; - - if (LanguageDef.IsLogicalOperatorString(content)) - { - lex = new Lexem() - { - Type = LexemType.Operator, - Token = LanguageDef.GetToken(content), - Content = content, - Location = new CodeRange(currentLine, currentColumn) - }; - } - else if (LanguageDef.IsBooleanLiteralString(content)) - { - lex = new Lexem() - { - Type = LexemType.BooleanLiteral, - Content = content, - Location = new CodeRange(currentLine, currentColumn) - }; - } - else if (LanguageDef.IsUndefinedString(content)) - { - lex = new Lexem() - { - Type = LexemType.UndefinedLiteral, - Content = content, - Location = new CodeRange(currentLine, currentColumn) - }; - - } - else if (LanguageDef.IsNullString(content)) - { - lex = new Lexem() - { - Type = LexemType.NullLiteral, - Content = content, - Location = new CodeRange(currentLine, currentColumn) - }; - - } - else - { - lex = new Lexem() - { - Type = LexemType.Identifier, - Content = content, - Token = LanguageDef.GetToken(content), - Location = new CodeRange(currentLine, currentColumn) - }; - - if (LanguageDef.IsBuiltInFunction(lex.Token)) - { - iterator.SkipSpaces(); - if (iterator.CurrentSymbol != '(') - { - lex.Token = Token.NotAToken; - } - } - } - - return lex; - } - - if (!iterator.MoveNext()) - { - isEndOfText = true; - } + do + { + if (SpecialChars.IsDelimiter(iterator.CurrentSymbol)) + break; } + while (iterator.MoveNext()); + + var content = iterator.GetContents(); + Lexem lex; + + switch (LanguageDef.GetWordType(content)) + { + case LanguageDef.WordType.Logical: + lex = new Lexem() + { + Type = LexemType.Operator, + Token = LanguageDef.GetToken(content), + Content = content, + Location = location + }; + break; + + case LanguageDef.WordType.Boolean: + lex = new Lexem() + { + Type = LexemType.BooleanLiteral, + Content = content, + Location = location + }; + break; + + case LanguageDef.WordType.Undefined: + lex = new Lexem() + { + Type = LexemType.UndefinedLiteral, + Content = content, + Location = location + }; + break; + + case LanguageDef.WordType.Null: + lex = new Lexem() + { + Type = LexemType.NullLiteral, + Content = content, + Location = location + }; + break; + + default: + var tok = LanguageDef.GetToken(content); + if (LanguageDef.IsBuiltInFunction(tok)) + { + iterator.SkipSpaces(); + if (iterator.CurrentSymbol != '(') + { + tok = Token.NotAToken; + } + } + + lex = new Lexem() + { + Type = LexemType.Identifier, + Content = content, + Token = tok, + Location = location + }; + break; + } + + return lex; } } } From ab43599077218f4965df90ee9071d0cb7df70a3b Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sun, 21 Dec 2025 16:47:03 +0400 Subject: [PATCH 04/52] =?UTF-8?q?=D1=83=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=BF=D1=83=D1=81=D1=82=D1=8B=D0=B5=20=D0=BD=D0=BE=D0=B4=D1=8B?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B8;=20=D1=83=D1=81=D0=BA=D0=BE=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D0=B8?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Language/IdentifiersTrie.cs | 146 ++++++++++------------ 1 file changed, 67 insertions(+), 79 deletions(-) diff --git a/src/OneScript.Language/IdentifiersTrie.cs b/src/OneScript.Language/IdentifiersTrie.cs index df4b68a19..5f014ba6f 100644 --- a/src/OneScript.Language/IdentifiersTrie.cs +++ b/src/OneScript.Language/IdentifiersTrie.cs @@ -17,26 +17,20 @@ public class IdentifiersTrie : IDictionary private class TrieNode { - public char charL; - public char charU; - public TrieNode sibl; - public TrieNode next; + internal char charL; + internal char charU; + internal TrieNode sibl; + internal TrieNode next; - private T _value; + internal T value; - public T Value - { - get => _value; - set - { - HasValue = true; - _value = value; - } - } - - public bool HasValue { get; private set; } - - public TrieNode Find(char ch) + internal bool hasValue; + + internal TrieNode() { } + internal TrieNode(char ch) + { charL = char.ToLower(ch); charU = char.ToUpper(ch); } + + internal TrieNode Find(char ch) { var node = sibl; while (node != null) @@ -47,47 +41,46 @@ public TrieNode Find(char ch) } return null; } - - } - + } + public void Add(string str, T val) { var node = _root; + TrieNode key = node; foreach (char ch in str) { - var key = node.Find(ch); - if (key == null) + if (node == null) { - key = new TrieNode + node = new TrieNode(ch); + key.next = node; + key = node; + node = null; + } + else + { + TrieNode last = node; + key = node; + while (key != null && key.charL != ch && key.charU != ch) + { + last = key; + key = key.sibl; + } + if (key == null) { - charL = char.ToLower(ch), - charU = char.ToUpper(ch), - Value = default(T), - sibl = node.sibl - }; - node.sibl = key; - key.next = new TrieNode(); + key = new TrieNode(ch); + last.sibl = key; + } + node = key.next; } - node = key.next; } - node.Value = val; + key.value = val; + key.hasValue = true; } - public bool ContainsKey(string key) - { - var node = _root; - foreach (char ch in key) - { - var keyNode = node.Find(ch); - if (keyNode == null) - { - return false; - } - node = keyNode.next; - } - - return node.next == null && node.HasValue; + public bool ContainsKey(string str) + { + return TryGetValue(str, out _); } public bool Remove(string key) @@ -96,22 +89,10 @@ public bool Remove(string key) } public T Get(string str) - { - var node = _root; - foreach (char ch in str) - { - TrieNode key = node.Find(ch); - if (key == null) - throw new KeyNotFoundException(); - - node = key.next; - } - - if (!node.HasValue) - throw new KeyNotFoundException(); - - return node.Value; - } + { + return TryGetValue(str, out var value) ? value + : throw new KeyNotFoundException(); + } public T this[string index] { @@ -124,27 +105,34 @@ public T this[string index] public bool TryGetValue(string str, out T value) { - var node = _root; + TrieNode key = _root; + var node = key.sibl; foreach (char ch in str) - { - var key = node.Find(ch); - if (key == null) - { - value = default; - return false; - } - + { + while (node != null && node.charL != ch && node.charU != ch) + { + node = node.sibl; + } + if (node == null) + { + value = default; + return false; + } + + key = node; node = key.next; } - if (!node.HasValue) - { - value = default; - return false; + if (key.hasValue) + { + value = key.value; + return true; + } + else + { + value = default; + return false; } - - value = node.Value; - return true; } public IEnumerator> GetEnumerator() From 8ea8b740ac6c6b3c84790dba4401219fa0049af5 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sun, 21 Dec 2025 21:11:06 +0400 Subject: [PATCH 05/52] =?UTF-8?q?=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=B3=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=8B;=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=BF.=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F;=20=D0=BC?= =?UTF-8?q?=D0=B5=D0=BB=D0=BE=D1=87=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Language/LanguageDef.cs | 1 - .../LexicalAnalysis/SourceCodeIterator.cs | 26 ++- .../LexicalAnalysis/StringLexerState.cs | 191 +++++++++--------- .../LexicalAnalysis/WordLexerState.cs | 3 +- src/OneScript.Language/ScriptException.cs | 6 +- 5 files changed, 124 insertions(+), 103 deletions(-) diff --git a/src/OneScript.Language/LanguageDef.cs b/src/OneScript.Language/LanguageDef.cs index aac5dcec4..954101361 100644 --- a/src/OneScript.Language/LanguageDef.cs +++ b/src/OneScript.Language/LanguageDef.cs @@ -9,7 +9,6 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -using static OneScript.Language.LanguageDef; namespace OneScript.Language { diff --git a/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs b/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs index 75f290a76..7112fa3b3 100644 --- a/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs +++ b/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs @@ -78,7 +78,7 @@ public int CurrentColumn return OUT_OF_TEXT; } - int start = GetLineBound(CurrentLine); + int start = GetLineBound(_lineCounter); return _index - start + 1; } } @@ -95,8 +95,7 @@ public bool MoveNext() if (_currentSymbol == '\n') { _lineCounter++; - if (_index < _codeLength) - _lineBounds.Add(_index + 1); + _lineBounds.Add(_index + 1); } return true; @@ -163,8 +162,26 @@ public bool SkipSpaces() } return true; + } + + public char ReadNextChar() + { + while (Char.IsWhiteSpace(_currentSymbol)) + { + if (_currentSymbol == '\n') + { + _onNewLine = true; + } + if (!MoveNext()) + { + break; + } + } + + return _currentSymbol; } + public string ReadToLineEnd() { while (_currentSymbol != '\n' && MoveNext()) @@ -182,6 +199,9 @@ public string ReadToLineEnd() public string GetCodeLine(int lineNumber) { int start = GetLineBound(lineNumber); + if (start >= _code.Length) + return String.Empty; + int end = _code.IndexOf('\n', start); if (end >= 0) { diff --git a/src/OneScript.Language/LexicalAnalysis/StringLexerState.cs b/src/OneScript.Language/LexicalAnalysis/StringLexerState.cs index 3afe4c4df..b9163315d 100644 --- a/src/OneScript.Language/LexicalAnalysis/StringLexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/StringLexerState.cs @@ -1,97 +1,96 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System.Text; - -namespace OneScript.Language.LexicalAnalysis -{ - public class StringLexerState : LexerState - { - private void SkipSpacesAndComments(SourceCodeIterator iterator) - { - while (true) - { /* Пропускаем все пробелы и комментарии */ - iterator.SkipSpaces(); - - if (iterator.CurrentSymbol == '/') - { - if (!iterator.MoveNext()) - throw CreateExceptionOnCurrentLine("Некорректный символ", iterator); - - if (iterator.CurrentSymbol != '/') - throw CreateExceptionOnCurrentLine("Некорректный символ", iterator); - - do - { - if (!iterator.MoveNext()) - break; - - } while (iterator.CurrentSymbol != '\n'); - - } - else - break; - } - } - - public override Lexem ReadNextLexem(SourceCodeIterator iterator) - { +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Text; + +namespace OneScript.Language.LexicalAnalysis +{ + public class StringLexerState : LexerState + { + private void SkipSpacesAndComments(SourceCodeIterator iterator) + { + while (true) + { /* Пропускаем все пробелы и комментарии */ + if (iterator.ReadNextChar() == '/') + { + if (!iterator.MoveNext()) + throw CreateExceptionOnCurrentLine("Некорректный символ", iterator); + + if (iterator.CurrentSymbol != '/') + throw CreateExceptionOnCurrentLine("Некорректный символ", iterator); + + do + { + if (!iterator.MoveNext()) + break; + + } while (iterator.CurrentSymbol != '\n'); + + } + else + break; + } + } + + public override Lexem ReadNextLexem(SourceCodeIterator iterator) + { StringBuilder contentBuilder = new StringBuilder(); - - while (iterator.MoveNext()) - { - var cs = iterator.CurrentSymbol; - - if (cs == SpecialChars.StringQuote) - { - if (iterator.MoveNext()) - { - if (iterator.CurrentSymbol == SpecialChars.StringQuote) - { - /* Двойная кавычка */ - contentBuilder.Append("\""); - continue; - } - - /* Завершение строки */ - SkipSpacesAndComments(iterator); - - if (iterator.CurrentSymbol == SpecialChars.StringQuote) - { - /* Сразу же началась новая строка */ - contentBuilder.Append('\n'); - continue; - } - } - - var lex = new Lexem - { - Type = LexemType.StringLiteral, - Content = contentBuilder.ToString() - }; - return lex; - } - - if (cs == '\n') - { - iterator.MoveNext(); - SkipSpacesAndComments(iterator); - - if (iterator.CurrentSymbol != '|') - throw CreateExceptionOnCurrentLine("Некорректный строковый литерал!", iterator); - - contentBuilder.Append('\n'); - } - else if(cs != '\r') - contentBuilder.Append(cs); - - } - - throw CreateExceptionOnCurrentLine("Незавершённый строковой интервал!", iterator); - } - } -} + + while (iterator.MoveNext()) + { + var cs = iterator.CurrentSymbol; + + if (cs == SpecialChars.StringQuote) + { + if (iterator.MoveNext()) + { + if (iterator.CurrentSymbol == SpecialChars.StringQuote) + { + /* Двойная кавычка */ + contentBuilder.Append('"'); + + continue; + } + + /* Завершение строки */ + SkipSpacesAndComments(iterator); + + if (iterator.CurrentSymbol == SpecialChars.StringQuote) + { + /* Сразу же началась новая строка */ + contentBuilder.Append('\n'); + + continue; + } + } + + return new Lexem + { + Type = LexemType.StringLiteral, + Content = contentBuilder.ToString() + }; + } + + if (cs == '\n') + { + iterator.MoveNext(); + SkipSpacesAndComments(iterator); + + if (iterator.CurrentSymbol != '|') + throw CreateExceptionOnCurrentLine("Некорректный строковый литерал", iterator); + + contentBuilder.Append('\n'); + } + else if (cs != '\r') + contentBuilder.Append(cs); + + } + + throw CreateExceptionOnCurrentLine("Незавершённый строковый литерал", iterator); + } + } +} diff --git a/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs b/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs index 1c704ddd7..3fa944d63 100644 --- a/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs @@ -66,8 +66,7 @@ public override Lexem ReadNextLexem(SourceCodeIterator iterator) var tok = LanguageDef.GetToken(content); if (LanguageDef.IsBuiltInFunction(tok)) { - iterator.SkipSpaces(); - if (iterator.CurrentSymbol != '(') + if (iterator.ReadNextChar() != '(') { tok = Token.NotAToken; } diff --git a/src/OneScript.Language/ScriptException.cs b/src/OneScript.Language/ScriptException.cs index 89048f73b..329285557 100644 --- a/src/OneScript.Language/ScriptException.cs +++ b/src/OneScript.Language/ScriptException.cs @@ -98,7 +98,11 @@ public override string Message { var sb = new StringBuilder(MessageWithoutCodeFragment); sb.AppendLine(); - var codeLine = Code?.Replace('\t', ' ').TrimEnd(); + var codeLine = Code?.Replace('\t', ' ')?.TrimEnd() ?? String.Empty; + if (ColumnNumber > codeLine.Length) + { + ColumnNumber = codeLine.Length; + } if (ColumnNumber != ErrorPositionInfo.OUT_OF_TEXT) { From a7c5fada3b44a1b28a035867d32c6e99071b91e9 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Mon, 22 Dec 2025 00:12:32 +0400 Subject: [PATCH 06/52] =?UTF-8?q?=D1=81=D0=BE=D0=BA=D1=80=D0=B0=D1=89?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20CurrentColumn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LexicalAnalysis/SourceCodeIterator.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs b/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs index 7112fa3b3..720ebf271 100644 --- a/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs +++ b/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs @@ -70,18 +70,8 @@ private void InitOnString(string code) public int CurrentLine => _lineCounter; public int CurrentColumn - { - get - { - if (_startPosition == OUT_OF_TEXT) - { - return OUT_OF_TEXT; - } - - int start = GetLineBound(_lineCounter); - return _index - start + 1; - } - } + => _startPosition == OUT_OF_TEXT ? OUT_OF_TEXT : _index - _lineBounds[^1] + 1; + // CurrentLine's start is last in _lineBounds public char CurrentSymbol => _currentSymbol; From 4ba9eb7c6f07a0da87d2d83ceed486a7299465a1 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Thu, 25 Dec 2025 16:17:42 +0400 Subject: [PATCH 07/52] =?UTF-8?q?VM:=20=D0=B8=D0=BC=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=81=D0=B2=D0=BE=D0=B9=D1=81=D1=82=D0=B2=20=D0=B8=20=D0=BC?= =?UTF-8?q?=D0=B5=D1=82=D0=BE=D0=B4=D0=BE=D0=B2=20=D0=B2=20=D0=BE=D1=82?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D1=81=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ScriptEngine/Compiler/ModuleDumpWriter.cs | 7 +++++ .../Compiler/StackMachineCodeGenerator.cs | 30 +++++++++++-------- src/ScriptEngine/Machine/MachineInstance.cs | 10 +++---- .../Machine/StackRuntimeModule.cs | 8 +++-- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/ScriptEngine/Compiler/ModuleDumpWriter.cs b/src/ScriptEngine/Compiler/ModuleDumpWriter.cs index ea402b1f2..198b13e62 100644 --- a/src/ScriptEngine/Compiler/ModuleDumpWriter.cs +++ b/src/ScriptEngine/Compiler/ModuleDumpWriter.cs @@ -129,6 +129,13 @@ private void WriteImage(TextWriter output, StackRuntimeModule module) output.WriteLine( $"{i,-3}:type: {item.SystemType.Alias}, val: {item}"); } + output.WriteLine(".identifiers"); + for (int i = 0; i < module.Identifiers.Count; i++) + { + var item = module.Identifiers[i]; + output.WriteLine( + $"{i,-3}: {item}"); + } output.WriteLine(".code"); for (int i = 0; i < module.Code.Count; i++) { diff --git a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs index 5e2200f49..00b8d955a 100644 --- a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs +++ b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs @@ -489,7 +489,7 @@ protected override void VisitReturnNode(BslSyntaxNode node) protected override void VisitRaiseNode(BslSyntaxNode node) { int arg = -1; - if (node.Children.Any()) + if (node.Children.Count != 0) { VisitExpression(node.Children[0]); arg = 0; @@ -728,24 +728,17 @@ private void ResolveObjectMethod(BslSyntaxNode callNode, bool asFunction) PushCallArguments(args); - var cDef = new ConstDefinition(); - cDef.Type = DataType.String; - cDef.Presentation = name.GetIdentifier(); - int lastIdentifierConst = GetConstNumber(cDef); + int lastIdentifierIndex = GetIdentNumber(name.GetIdentifier()); if (asFunction) - AddCommand(OperationCode.ResolveMethodFunc, lastIdentifierConst); + AddCommand(OperationCode.ResolveMethodFunc, lastIdentifierIndex); else - AddCommand(OperationCode.ResolveMethodProc, lastIdentifierConst); + AddCommand(OperationCode.ResolveMethodProc, lastIdentifierIndex); } private void ResolveProperty(string identifier) { - var cDef = new ConstDefinition(); - cDef.Type = DataType.String; - cDef.Presentation = identifier; - var identifierConstIndex = GetConstNumber(cDef); - AddCommand(OperationCode.ResolveProp, identifierConstIndex); + AddCommand(OperationCode.ResolveProp, GetIdentNumber(identifier)); } private int PushVariable(TerminalNode node) @@ -1328,6 +1321,19 @@ private int GetConstNumber(in ConstDefinition cDef) return idx; } + private int GetIdentNumber(string ident) + { + + var idx = _module.Identifiers.IndexOf(ident); + if (idx < 0) + { + idx = _module.Identifiers.Count; + _module.Identifiers.Add(ident); + } + return idx; + } + + private int GetMethodRefNumber(in SymbolBinding methodBinding) { var descriptor = _ctx.GetBinding(methodBinding.ScopeNumber); diff --git a/src/ScriptEngine/Machine/MachineInstance.cs b/src/ScriptEngine/Machine/MachineInstance.cs index 992dbb292..c3aaed25f 100644 --- a/src/ScriptEngine/Machine/MachineInstance.cs +++ b/src/ScriptEngine/Machine/MachineInstance.cs @@ -680,8 +680,8 @@ private void PushConst(int arg) { _operationStack.Push(_module.Constants[arg]); NextInstruction(); - } - + } + private void PushBool(int arg) { _operationStack.Push(BslBooleanValue.Create(arg == 1)); @@ -1011,7 +1011,7 @@ private void ResolveProp(int arg) var objIValue = _operationStack.Pop(); var context = objIValue.AsObject(); - var propName = _module.Constants[arg].ToString(_process); + var propName = _module.Identifiers[arg]; var propNum = context.GetPropertyNumber(propName); var propReference = Variable.CreateContextPropertyReference(context, propNum, "stackvar"); @@ -1048,7 +1048,7 @@ private void PrepareContextCallArguments(int arg, out IRuntimeContextInstance co var objIValue = _operationStack.Pop(); context = objIValue.AsObject(); - var methodName = _module.Constants[arg].ToString(_process); + var methodName = _module.Identifiers[arg]; methodId = context.GetMethodNumber(methodName); if (context.DynamicMethodSignatures) @@ -1191,7 +1191,7 @@ private void NewInstance(int argCount) argValues[i] = RawValue(argValue); } - var typeName = PopRawBslValue().ToString(_process); + var typeName = _operationStack.Pop().ToString(); // is BslStringValue by code generation if (!_typeManager.TryGetType(typeName, out var type)) { throw RuntimeException.TypeIsNotDefined(typeName); diff --git a/src/ScriptEngine/Machine/StackRuntimeModule.cs b/src/ScriptEngine/Machine/StackRuntimeModule.cs index 6f570b49d..ea4d02803 100644 --- a/src/ScriptEngine/Machine/StackRuntimeModule.cs +++ b/src/ScriptEngine/Machine/StackRuntimeModule.cs @@ -26,10 +26,12 @@ public StackRuntimeModule(Type ownerType) public int EntryMethodIndex { get; set; } = -1; public List Constants { get; } = new List(); + + internal List Identifiers { get; } = new List(); - internal IList VariableRefs { get; } = new List(); + internal List VariableRefs { get; } = new List(); - internal IList MethodRefs { get; } = new List(); + internal List MethodRefs { get; } = new List(); #region IExecutableModule members @@ -52,7 +54,7 @@ public BslScriptMethodInfo ModuleBody public IList Methods { get; } = new List(); - public IList Code { get; } = new List(512); + public List Code { get; } = new List(512); public SourceCode Source { get; set; } From 0ff7ea4ecc7a824fa5cbc95ed131bfe3f99e9650 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Thu, 25 Dec 2025 19:44:34 +0400 Subject: [PATCH 08/52] =?UTF-8?q?=D1=82=D0=B8=D0=BF=D1=8B=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BD=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=20-=20=D0=B2=20=D0=B8=D0=B4=D0=B5=D0=BD=D1=82=D0=B8?= =?UTF-8?q?=D1=84=D0=B8=D0=BA=D0=B0=D1=82=D0=BE=D1=80=D1=8B,=20=D0=B2?= =?UTF-8?q?=D1=8B=D0=B7=D0=BE=D0=B2=20=D0=BA=D0=B0=D0=BA=20=D1=83=20=D0=BC?= =?UTF-8?q?=D0=B5=D1=82=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Compiler/StackMachineCodeGenerator.cs | 41 +++++++++---------- src/ScriptEngine/Machine/MachineInstance.cs | 33 +++++++-------- 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs index 00b8d955a..407c573d8 100644 --- a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs +++ b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs @@ -856,11 +856,13 @@ private void GlobalCall(CallNode call, bool asFunction) else { // can be defined later - var forwarded = new ForwardedMethodDecl(); - forwarded.identifier = identifier; - forwarded.asFunction = asFunction; - forwarded.location = identifierNode.Location; - forwarded.factArguments = argList; + var forwarded = new ForwardedMethodDecl + { + identifier = identifier, + asFunction = asFunction, + location = identifierNode.Location, + factArguments = argList + }; PushCallArguments(call.ArgumentList); @@ -878,17 +880,17 @@ private void PushCallArguments(BslSyntaxNode argList) private void PushArgumentsList(BslSyntaxNode argList) { - for (int i = 0; i < argList.Children.Count; i++) + var arguments = argList.Children; + for (int i = 0; i < arguments.Count; i++) { - var passedArg = argList.Children[i]; - VisitCallArgument(passedArg); + VisitCallArgument(arguments[i]); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void VisitCallArgument(BslSyntaxNode passedArg) { - if (passedArg.Children.Count > 0) + if (passedArg.Children.Count != 0) { VisitExpression(passedArg.Children[0]); } @@ -1061,21 +1063,17 @@ private void MakeNewObjectDynamic(NewObjectNode node) private void MakeNewObjectStatic(NewObjectNode node) { - var cDef = new ConstDefinition() - { - Type = DataType.String, - Presentation = node.TypeNameNode.GetIdentifier() - }; - AddCommand(OperationCode.PushConst, GetConstNumber(cDef)); - - var callArgs = 0; if (node.ConstructorArguments != default) { - PushArgumentsList(node.ConstructorArguments); - callArgs = node.ConstructorArguments.Children.Count; + PushCallArguments(node.ConstructorArguments); + } + else + { + AddCommand(OperationCode.ArgNum, 0); } - AddCommand(OperationCode.NewInstance, callArgs); + var idNum = GetIdentNumber(node.TypeNameNode.GetIdentifier()); + AddCommand(OperationCode.NewInstance, idNum); } private void ExitTryBlocks() @@ -1322,8 +1320,7 @@ private int GetConstNumber(in ConstDefinition cDef) } private int GetIdentNumber(string ident) - { - + { var idx = _module.Identifiers.IndexOf(ident); if (idx < 0) { diff --git a/src/ScriptEngine/Machine/MachineInstance.cs b/src/ScriptEngine/Machine/MachineInstance.cs index c3aaed25f..17f8f3a02 100644 --- a/src/ScriptEngine/Machine/MachineInstance.cs +++ b/src/ScriptEngine/Machine/MachineInstance.cs @@ -1180,23 +1180,14 @@ private void Inc(int arg) NextInstruction(); } - private void NewInstance(int argCount) - { - IValue[] argValues = new IValue[argCount]; - // fact args - for (int i = argCount - 1; i >= 0; i--) - { - var argValue = _operationStack.Pop(); - if(!argValue.IsSkippedArgument()) - argValues[i] = RawValue(argValue); - } - - var typeName = _operationStack.Pop().ToString(); // is BslStringValue by code generation + private void NewInstance(int arg) + { + var typeName = _module.Identifiers[arg]; if (!_typeManager.TryGetType(typeName, out var type)) { throw RuntimeException.TypeIsNotDefined(typeName); - } - + } + // TODO убрать cast после рефакторинга ITypeFactory var factory = (TypeFactory)_typeManager.GetFactoryFor(type); var context = new TypeActivationContext @@ -1205,8 +1196,18 @@ private void NewInstance(int argCount) TypeManager = _typeManager, Services = _process.Services, CurrentProcess = _process - }; - + }; + + int argCount = (int)_operationStack.Pop().AsNumber(); + IValue[] argValues = new IValue[argCount]; + // fact args + for (int i = argCount - 1; i >= 0; i--) + { + var argValue = _operationStack.Pop(); + if(!argValue.IsSkippedArgument()) + argValues[i] = RawValue(argValue); + } + var instance = (IValue)factory.Activate(context, argValues); _operationStack.Push(instance); NextInstruction(); From 10aa8e113d19a448c6a814795199c031714f163a Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Thu, 25 Dec 2025 20:28:24 +0400 Subject: [PATCH 09/52] =?UTF-8?q?=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=BA=D1=82=D0=B0=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=D0=BD=D0=B8=20=D1=82=D0=B8=D0=BF=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Compiler/StackMachineCodeGenerator.cs | 2 +- src/ScriptEngine/Machine/MachineInstance.cs | 46 +++++++------------ 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs index 407c573d8..ae8fff066 100644 --- a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs +++ b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs @@ -1051,7 +1051,7 @@ private void MakeNewObjectDynamic(NewObjectNode node) var argsPassed = node.ConstructorArguments.Children.Count; if (argsPassed == 1) { - PushArgumentsList(node.ConstructorArguments); + VisitCallArgument(node.ConstructorArguments.Children[0]); ; } else if (argsPassed > 1) { diff --git a/src/ScriptEngine/Machine/MachineInstance.cs b/src/ScriptEngine/Machine/MachineInstance.cs index 17f8f3a02..1ad01370f 100644 --- a/src/ScriptEngine/Machine/MachineInstance.cs +++ b/src/ScriptEngine/Machine/MachineInstance.cs @@ -24,6 +24,7 @@ This Source Code Form is subject to the terms of the using OneScript.Values; using ScriptEngine.Compiler; using ScriptEngine.Machine.Debugger; +using System.Dynamic; namespace ScriptEngine.Machine { @@ -1182,22 +1183,6 @@ private void Inc(int arg) private void NewInstance(int arg) { - var typeName = _module.Identifiers[arg]; - if (!_typeManager.TryGetType(typeName, out var type)) - { - throw RuntimeException.TypeIsNotDefined(typeName); - } - - // TODO убрать cast после рефакторинга ITypeFactory - var factory = (TypeFactory)_typeManager.GetFactoryFor(type); - var context = new TypeActivationContext - { - TypeName = typeName, - TypeManager = _typeManager, - Services = _process.Services, - CurrentProcess = _process - }; - int argCount = (int)_operationStack.Pop().AsNumber(); IValue[] argValues = new IValue[argCount]; // fact args @@ -1206,10 +1191,10 @@ private void NewInstance(int arg) var argValue = _operationStack.Pop(); if(!argValue.IsSkippedArgument()) argValues[i] = RawValue(argValue); - } - - var instance = (IValue)factory.Activate(context, argValues); - _operationStack.Push(instance); + } + + var typeName = _module.Identifiers[arg]; + _operationStack.Push(CreateInstance(typeName, argValues)); NextInstruction(); } @@ -2396,13 +2381,19 @@ private void NewFunc(int argCount) else argValues = Array.Empty(); } - + var typeName = PopRawBslValue().ToString(_process); + _operationStack.Push(CreateInstance(typeName, argValues)); + NextInstruction(); + } + + private IValue CreateInstance(string typeName, IValue[] args) + { if (!_typeManager.TryGetType(typeName, out var type)) { throw RuntimeException.TypeIsNotDefined(typeName); } - + // TODO убрать cast после рефакторинга ITypeFactory var factory = (TypeFactory)_typeManager.GetFactoryFor(type); var context = new TypeActivationContext @@ -2411,17 +2402,14 @@ private void NewFunc(int argCount) TypeManager = _typeManager, Services = _process.Services, CurrentProcess = _process - }; - - var instance = factory.Activate(context, argValues); - _operationStack.Push(instance); - NextInstruction(); + }; + return factory.Activate(context, args); } #endregion - + #endregion - + private StackRuntimeModule CompileExpressionModule(string expression) { var entryId = CurrentCodeEntry().ToString(); From a13d2fe8700ba2ba184fa715b31190f462a0259d Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Thu, 25 Dec 2025 21:02:12 +0400 Subject: [PATCH 10/52] =?UTF-8?q?=D0=BC=D0=B8=D0=BA=D1=80=D0=BE=D0=BE?= =?UTF-8?q?=D0=BF=D1=82=D0=B8=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8,?= =?UTF-8?q?=20=D0=B3=D0=BB=D0=BE=D0=B1=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Core/Contexts/ClassBuilder.cs | 2 +- .../SyntaxAnalysis/BslSyntaxWalker.cs | 2 +- .../ConditionalDirectiveHandler.cs | 2 +- .../SyntaxAnalysis/DefaultBslParser.cs | 4 +- .../Compiler/MethodCompiler.cs | 10 +- .../Compiler/StatementBlocksWriter.cs | 2 +- .../Collections/Indexes/CollectionIndex.cs | 2 +- .../CustomLineFeedStreamReader.cs | 2 +- .../Tasks/BackgroundTasksManager.cs | 2 +- .../Compiler/StackMachineCodeGenerator.cs | 9 +- src/ScriptEngine/Machine/MachineInstance.cs | 6 +- .../Web/Multipart/BinaryStreamStack.cs | 806 +++++++++--------- 12 files changed, 424 insertions(+), 425 deletions(-) diff --git a/src/OneScript.Core/Contexts/ClassBuilder.cs b/src/OneScript.Core/Contexts/ClassBuilder.cs index 88cf838bb..c402e9591 100644 --- a/src/OneScript.Core/Contexts/ClassBuilder.cs +++ b/src/OneScript.Core/Contexts/ClassBuilder.cs @@ -113,7 +113,7 @@ private static bool MarkedAsContextMethod(MemberInfo member, bool includeDepreca private static bool MarkedAsContextProperty(MemberInfo member, bool includeDeprecations = false) { - return member.GetCustomAttributes(typeof(ContextPropertyAttribute), false).Any(); + return member.GetCustomAttributes(typeof(ContextPropertyAttribute), false).Length != 0; } public ClassBuilder ExportConstructor(MethodInfo info) diff --git a/src/OneScript.Language/SyntaxAnalysis/BslSyntaxWalker.cs b/src/OneScript.Language/SyntaxAnalysis/BslSyntaxWalker.cs index 7c2b352da..1a0c6f8cf 100644 --- a/src/OneScript.Language/SyntaxAnalysis/BslSyntaxWalker.cs +++ b/src/OneScript.Language/SyntaxAnalysis/BslSyntaxWalker.cs @@ -324,7 +324,7 @@ protected virtual void VisitTernaryOperation(BslSyntaxNode node) protected virtual void VisitModuleBody(BslSyntaxNode codeBlock) { - if(codeBlock.Children.Count > 0) + if(codeBlock.Children.Count != 0) VisitCodeBlock(codeBlock.Children[0]); } diff --git a/src/OneScript.Language/SyntaxAnalysis/ConditionalDirectiveHandler.cs b/src/OneScript.Language/SyntaxAnalysis/ConditionalDirectiveHandler.cs index d84e8febc..5a5572dde 100644 --- a/src/OneScript.Language/SyntaxAnalysis/ConditionalDirectiveHandler.cs +++ b/src/OneScript.Language/SyntaxAnalysis/ConditionalDirectiveHandler.cs @@ -297,7 +297,7 @@ private void MarkAsSolved() private void PopBlock() { - if (_blocks.Count > 0) + if (_blocks.Count != 0) _blocks.Pop(); else AddError(LocalizedErrors.DirectiveIsMissing("Если")); diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 7f6b980ca..1d1a543c9 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -123,7 +123,7 @@ private void ParseModuleAnnotation() .Cast() .ToList(); - if (!annotationParser.Any()) + if (annotationParser.Count == 0) return; while (_lastExtractedLexem.Type == LexemType.PreprocessorDirective) @@ -1650,7 +1650,7 @@ private void AddError(CodeError err, bool doFastForward = true) if (doFastForward) { - if (_tokenStack.Count > 0) + if (_tokenStack.Count != 0) SkipToNextStatement(_tokenStack.Peek()); else SkipToNextStatement(); diff --git a/src/OneScript.Native/Compiler/MethodCompiler.cs b/src/OneScript.Native/Compiler/MethodCompiler.cs index 3237a29f7..190643ebf 100644 --- a/src/OneScript.Native/Compiler/MethodCompiler.cs +++ b/src/OneScript.Native/Compiler/MethodCompiler.cs @@ -713,7 +713,7 @@ protected override void VisitIfNode(ConditionNode node) { stack.Push(elif); } - else if (stack.Count > 0) + else if (stack.Count != 0) { var cond = stack.Pop(); @@ -726,7 +726,7 @@ protected override void VisitIfNode(ConditionNode node) } } - while (stack.Count > 0) + while (stack.Count != 0) { var elseIfNode = stack.Pop(); VisitElseIfNode(elseIfNode); @@ -1147,7 +1147,7 @@ protected override void VisitObjectProcedureCall(BslSyntaxNode node) private IEnumerable PrepareDynamicCallArguments(BslSyntaxNode argList) { return argList.Children.Select(passedArg => - passedArg.Children.Count > 0 + passedArg.Children.Count != 0 ? ConvertToExpressionTree(passedArg.Children[0]) : Expression.Constant(BslSkippedParameterValue.Instance)); } @@ -1414,7 +1414,7 @@ private List PrepareCallArguments(BslSyntaxNode argList, ParameterIn } var parameters = argList.Children.Select(passedArg => - passedArg.Children.Count > 0 + passedArg.Children.Count != 0 ? ConvertToExpressionTree(passedArg.Children[0]) : null).ToArray(); @@ -1523,7 +1523,7 @@ protected override void VisitNewObjectCreation(NewObjectNode node) if (node.ConstructorArguments != default) { parameters = node.ConstructorArguments.Children.Select(passedArg => - passedArg.Children.Count > 0 ? + passedArg.Children.Count != 0 ? ConvertToExpressionTree(passedArg.Children[0]) : Expression.Default(typeof(BslValue))).ToArray(); } diff --git a/src/OneScript.Native/Compiler/StatementBlocksWriter.cs b/src/OneScript.Native/Compiler/StatementBlocksWriter.cs index f781f1a08..6bf68f434 100644 --- a/src/OneScript.Native/Compiler/StatementBlocksWriter.cs +++ b/src/OneScript.Native/Compiler/StatementBlocksWriter.cs @@ -18,7 +18,7 @@ public class StatementBlocksWriter public void EnterBlock(JumpInformationRecord newJumpStates) { - var current = _blocks.Count > 0 ? GetCurrentBlock() : null; + var current = _blocks.Count != 0 ? GetCurrentBlock() : null; if (current != null) { newJumpStates.MethodReturn ??= current.MethodReturn; diff --git a/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndex.cs b/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndex.cs index 71fb5c62b..73f13347e 100644 --- a/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndex.cs +++ b/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndex.cs @@ -42,7 +42,7 @@ public CollectionIndex(IIndexCollectionSource source, IEnumerable fields internal bool CanBeUsedFor(IEnumerable searchFields) { - return _fields.Count > 0 && _fields.All(f => searchFields.Contains(f)); + return _fields.Count != 0 && _fields.All(f => searchFields.Contains(f)); } private CollectionIndexKey IndexKey(PropertyNameIndexAccessor source) diff --git a/src/OneScript.StandardLibrary/CustomLineFeedStreamReader.cs b/src/OneScript.StandardLibrary/CustomLineFeedStreamReader.cs index b6933543c..f087c5d26 100644 --- a/src/OneScript.StandardLibrary/CustomLineFeedStreamReader.cs +++ b/src/OneScript.StandardLibrary/CustomLineFeedStreamReader.cs @@ -56,7 +56,7 @@ public int Read () _buffer.Dequeue (); UpdateCharQueue (); - if (_buffer.Count > 0 && _buffer.Peek () == '\n') { + if (_buffer.Count != 0 && _buffer.Peek () == '\n') { _buffer.Dequeue (); UpdateCharQueue (); } diff --git a/src/OneScript.StandardLibrary/Tasks/BackgroundTasksManager.cs b/src/OneScript.StandardLibrary/Tasks/BackgroundTasksManager.cs index 0a875dc84..9999dd86c 100644 --- a/src/OneScript.StandardLibrary/Tasks/BackgroundTasksManager.cs +++ b/src/OneScript.StandardLibrary/Tasks/BackgroundTasksManager.cs @@ -120,7 +120,7 @@ public void WaitCompletionOfTasks() var failedTasks = _tasks.Where(x => x.State == TaskStateEnum.CompletedWithErrors) .ToList(); - if (failedTasks.Any()) + if (failedTasks.Count != 0) { throw new ParametrizedRuntimeException( Locale.NStr("ru = 'Задания завершились с ошибками';en = 'Tasks are completed with errors'"), diff --git a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs index ae8fff066..3edae66b7 100644 --- a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs +++ b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs @@ -12,7 +12,6 @@ This Source Code Form is subject to the terms of the using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using System.Text; using OneScript.Compilation; using OneScript.Compilation.Binding; using OneScript.Contexts; @@ -126,7 +125,7 @@ private void HandleImportClause(AnnotationNode node) private void CheckForwardedDeclarations() { - if (_forwardedMethods.Count > 0) + if (_forwardedMethods.Count != 0) { foreach (var item in _forwardedMethods) { @@ -477,7 +476,7 @@ protected override void VisitContinueNode(LineMarkerNode node) protected override void VisitReturnNode(BslSyntaxNode node) { - if (node.Children.Count > 0) + if (node.Children.Count != 0) { VisitExpression(node.Children[0]); AddCommand(OperationCode.MakeRawValue); @@ -1085,7 +1084,7 @@ private void ExitTryBlocks() private void PushTryNesting() { - if (_nestedLoops.Count > 0) + if (_nestedLoops.Count != 0) { _nestedLoops.Peek().tryNesting++; } @@ -1093,7 +1092,7 @@ private void PushTryNesting() private void PopTryNesting() { - if (_nestedLoops.Count > 0) + if (_nestedLoops.Count != 0) { _nestedLoops.Peek().tryNesting--; } diff --git a/src/ScriptEngine/Machine/MachineInstance.cs b/src/ScriptEngine/Machine/MachineInstance.cs index 1ad01370f..288b726f5 100644 --- a/src/ScriptEngine/Machine/MachineInstance.cs +++ b/src/ScriptEngine/Machine/MachineInstance.cs @@ -1134,7 +1134,7 @@ private void Return(int arg) if (_currentFrame.DiscardReturnValue) _operationStack.Pop(); - while(_exceptionsStack.Count > 0 && _exceptionsStack.Peek().HandlerFrame == _currentFrame) + while(_exceptionsStack.Count != 0 && _exceptionsStack.Peek().HandlerFrame == _currentFrame) { _exceptionsStack.Pop(); } @@ -1248,7 +1248,7 @@ private void BeginTry(int exceptBlockAddress) private void EndTry(int arg) { - if (_exceptionsStack.Count > 0) + if (_exceptionsStack.Count != 0) { var jmpInfo = _exceptionsStack.Peek(); if (jmpInfo.HandlerFrame == _currentFrame && arg == jmpInfo.HandlerAddress) @@ -1371,7 +1371,7 @@ private void Execute(int arg) { var code = PopRawBslValue().ToString(_process); var module = CompileCached(code, CompileExecutionBatchModule); - if (!module.Methods.Any()) + if (module.Methods.Count == 0) { NextInstruction(); return; diff --git a/src/oscript/Web/Multipart/BinaryStreamStack.cs b/src/oscript/Web/Multipart/BinaryStreamStack.cs index eac9dbb10..3fe42c7fd 100644 --- a/src/oscript/Web/Multipart/BinaryStreamStack.cs +++ b/src/oscript/Web/Multipart/BinaryStreamStack.cs @@ -1,404 +1,404 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2013 Jake Woods -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software -// and associated documentation files (the "Software"), to deal in the Software without restriction, -// including without limitation the rights to use, copy, modify, merge, publish, distribute, -// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies -// or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR -// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// Jake Woods -// -// Provides character based and byte based stream-like read operations over multiple -// streams and provides methods to add data to the front of the buffer. -// -// -------------------------------------------------------------------------------------------------------------------- - -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace HttpMultipartParser -{ - /// - /// Provides character based and byte based stream-like read operations over multiple - /// streams and provides methods to add data to the front of the buffer. - /// - internal class BinaryStreamStack - { - #region Fields - - /// - /// Holds the streams to read from, the stream on the top of the - /// stack will be read first. - /// - private readonly Stack streams = new Stack(); - - #endregion - - #region Constructors and Destructors - - /// - /// Initializes a new instance of the class with the default - /// encoding of UTF8. - /// - public BinaryStreamStack() - : this(Encoding.UTF8) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The encoding to use for character based operations. - /// - public BinaryStreamStack(Encoding encoding) - { - CurrentEncoding = encoding; - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets the current encoding. - /// - public Encoding CurrentEncoding { get; set; } - - #endregion - - #region Public Methods and Operators - - /// - /// Returns true if there is any data left to read. - /// - /// - /// True or false. - /// - public bool HasData() - { - return streams.Any(); - } - - /// - /// Returns the reader on the top of the stack but does not remove it. - /// - /// - /// The . - /// - public BinaryReader Peek() - { - return streams.Peek(); - } - - /// - /// Returns the reader on the top of the stack and removes it - /// - /// - /// The . - /// - public BinaryReader Pop() - { - return streams.Pop(); - } - - /// - /// Pushes data to the front of the stack. The most recently pushed data will - /// be read first. - /// - /// - /// The data to add to the stack. - /// - public void Push(byte[] data) - { - streams.Push(new BinaryReader(new MemoryStream(data), CurrentEncoding)); - } - - /// - /// Reads a single byte as an integer from the stack. Returns -1 if no - /// data is left to read. - /// - /// - /// The that was read. - /// - public int Read() - { - BinaryReader top = streams.Peek(); - - int value; - while ((value = top.Read()) == -1) - { - top.Dispose(); - streams.Pop(); - - if (!streams.Any()) - { - return -1; - } - - top = streams.Peek(); - } - - return value; - } - - /// - /// Reads the specified number of bytes from the stack, starting from a specified point in the byte array. - /// - /// - /// The buffer to read data into. - /// - /// - /// The index of buffer to start reading into. - /// - /// - /// The number of bytes to read into the buffer. - /// - /// - /// The number of bytes read into buffer. This might be less than the number of bytes requested if that many bytes are not available, - /// or it might be zero if the end of the stream is reached. - /// - public int Read(byte[] buffer, int index, int count) - { - if (!HasData()) - { - return 0; - } - - // Read through all the stream untill we exhaust them - // or untill count is satisfied - int amountRead = 0; - BinaryReader top = streams.Peek(); - while (amountRead < count && streams.Any()) - { - int read = top.Read(buffer, index + amountRead, count - amountRead); - if (read == 0) - { - if ((top = NextStream()) == null) - { - return amountRead; - } - } - else - { - amountRead += read; - } - } - - return amountRead; - } - - /// - /// Reads the specified number of characters from the stack, starting from a specified point in the byte array. - /// - /// - /// The buffer to read data into. - /// - /// - /// The index of buffer to start reading into. - /// - /// - /// The number of characters to read into the buffer. - /// - /// - /// The number of characters read into buffer. This might be less than the number of bytes requested if that many bytes are not available, - /// or it might be zero if the end of the stream is reached. - /// - public int Read(char[] buffer, int index, int count) - { - if (!HasData()) - { - return 0; - } - - // Read through all the stream untill we exhaust them - // or untill count is satisfied - int amountRead = 0; - BinaryReader top = streams.Peek(); - while (amountRead < count && streams.Any()) - { - int read = top.Read(buffer, index + amountRead, count - amountRead); - if (read == 0) - { - if ((top = NextStream()) == null) - { - return amountRead; - } - } - else - { - amountRead += read; - } - } - - return amountRead; - } - - /// - /// Reads the specified number of characters from the stack, starting from a specified point in the byte array. - /// - /// - /// A byte array containing all the data up to but not including the next newline in the stack. - /// - public byte[] ReadByteLine() - { - bool dummy; - return ReadByteLine(out dummy); - } - - /// - /// Reads a line from the stack delimited by the newline for this platform. The newline - /// characters will not be included in the stream - /// - /// - /// This will be set to true if we did not end on a newline but instead found the end of - /// our data. - /// - /// - /// The containing the line. - /// - public byte[] ReadByteLine(out bool hitStreamEnd) - { - hitStreamEnd = false; - if (!HasData()) - { - // No streams, no data! - return null; - } - - // This is horribly inefficient, consider profiling here if - // it becomes an issue. - BinaryReader top = streams.Peek(); - byte[] ignore = CurrentEncoding.GetBytes(new[] {'\r'}); - byte[] search = CurrentEncoding.GetBytes(new[] {'\n'}); - int searchPos = 0; - var builder = new MemoryStream(); - - while (true) - { - // First we need to read a byte from one of the streams - var bytes = new byte[search.Length]; - int amountRead = top.Read(bytes, 0, bytes.Length); - while (amountRead == 0) - { - streams.Pop(); - if (!streams.Any()) - { - hitStreamEnd = true; - return builder.ToArray(); - } - - top.Dispose(); - top = streams.Peek(); - amountRead = top.Read(bytes, 0, bytes.Length); - } - - // Now we've got some bytes, we need to check it against the search array. - foreach (byte b in bytes) - { - if (ignore.Contains(b)) - { - continue; - } - - if (b == search[searchPos]) - { - searchPos += 1; - } - else - { - // We only want to append the information if it's - // not part of the newline sequence - if (searchPos != 0) - { - byte[] append = search.Take(searchPos).ToArray(); - builder.Write(append, 0, append.Length); - } - - builder.Write(new[] {b}, 0, 1); - searchPos = 0; - } - - // Finally if we've found our string - if (searchPos == search.Length) - { - return builder.ToArray(); - } - } - } - } - - /// - /// Reads a line from the stack delimited by the newline for this platform. The newline - /// characters will not be included in the stream - /// - /// - /// The containing the line. - /// - public string ReadLine() - { - bool dummy; - return ReadLine(out dummy); - } - - /// - /// Reads a line from the stack delimited by the newline for this platform. The newline - /// characters will not be included in the stream - /// - /// - /// This will be set to true if we did not end on a newline but instead found the end of - /// our data. - /// - /// - /// The containing the line. - /// - public string ReadLine(out bool hitStreamEnd) - { - bool foundEnd; - byte[] result = ReadByteLine(out foundEnd); - hitStreamEnd = foundEnd; - - if (result == null) - { - return null; - } - - return CurrentEncoding.GetString(result); - } - - #endregion - - #region Methods - - /// - /// Removes the current reader from the stack and ensures it is correctly - /// destroyed and then returns the next available reader. If no reader - /// is available this method returns null. - /// - /// - /// The next reader. - /// - private BinaryReader NextStream() - { - BinaryReader top = streams.Pop(); - top.Dispose(); - - return streams.Any() ? streams.Peek() : null; - } - - #endregion - } +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2013 Jake Woods +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software +// and associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies +// or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Jake Woods +// +// Provides character based and byte based stream-like read operations over multiple +// streams and provides methods to add data to the front of the buffer. +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace HttpMultipartParser +{ + /// + /// Provides character based and byte based stream-like read operations over multiple + /// streams and provides methods to add data to the front of the buffer. + /// + internal class BinaryStreamStack + { + #region Fields + + /// + /// Holds the streams to read from, the stream on the top of the + /// stack will be read first. + /// + private readonly Stack streams = new Stack(); + + #endregion + + #region Constructors and Destructors + + /// + /// Initializes a new instance of the class with the default + /// encoding of UTF8. + /// + public BinaryStreamStack() + : this(Encoding.UTF8) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The encoding to use for character based operations. + /// + public BinaryStreamStack(Encoding encoding) + { + CurrentEncoding = encoding; + } + + #endregion + + #region Public Properties + + /// + /// Gets or sets the current encoding. + /// + public Encoding CurrentEncoding { get; set; } + + #endregion + + #region Public Methods and Operators + + /// + /// Returns true if there is any data left to read. + /// + /// + /// True or false. + /// + public bool HasData() + { + return streams.Count != 0; + } + + /// + /// Returns the reader on the top of the stack but does not remove it. + /// + /// + /// The . + /// + public BinaryReader Peek() + { + return streams.Peek(); + } + + /// + /// Returns the reader on the top of the stack and removes it + /// + /// + /// The . + /// + public BinaryReader Pop() + { + return streams.Pop(); + } + + /// + /// Pushes data to the front of the stack. The most recently pushed data will + /// be read first. + /// + /// + /// The data to add to the stack. + /// + public void Push(byte[] data) + { + streams.Push(new BinaryReader(new MemoryStream(data), CurrentEncoding)); + } + + /// + /// Reads a single byte as an integer from the stack. Returns -1 if no + /// data is left to read. + /// + /// + /// The that was read. + /// + public int Read() + { + BinaryReader top = streams.Peek(); + + int value; + while ((value = top.Read()) == -1) + { + top.Dispose(); + streams.Pop(); + + if (streams.Count == 0) + { + return -1; + } + + top = streams.Peek(); + } + + return value; + } + + /// + /// Reads the specified number of bytes from the stack, starting from a specified point in the byte array. + /// + /// + /// The buffer to read data into. + /// + /// + /// The index of buffer to start reading into. + /// + /// + /// The number of bytes to read into the buffer. + /// + /// + /// The number of bytes read into buffer. This might be less than the number of bytes requested if that many bytes are not available, + /// or it might be zero if the end of the stream is reached. + /// + public int Read(byte[] buffer, int index, int count) + { + if (!HasData()) + { + return 0; + } + + // Read through all the stream untill we exhaust them + // or untill count is satisfied + int amountRead = 0; + BinaryReader top = streams.Peek(); + while (amountRead < count && streams.Count != 0) + { + int read = top.Read(buffer, index + amountRead, count - amountRead); + if (read == 0) + { + if ((top = NextStream()) == null) + { + return amountRead; + } + } + else + { + amountRead += read; + } + } + + return amountRead; + } + + /// + /// Reads the specified number of characters from the stack, starting from a specified point in the byte array. + /// + /// + /// The buffer to read data into. + /// + /// + /// The index of buffer to start reading into. + /// + /// + /// The number of characters to read into the buffer. + /// + /// + /// The number of characters read into buffer. This might be less than the number of bytes requested if that many bytes are not available, + /// or it might be zero if the end of the stream is reached. + /// + public int Read(char[] buffer, int index, int count) + { + if (!HasData()) + { + return 0; + } + + // Read through all the stream untill we exhaust them + // or untill count is satisfied + int amountRead = 0; + BinaryReader top = streams.Peek(); + while (amountRead < count && streams.Count != 0) + { + int read = top.Read(buffer, index + amountRead, count - amountRead); + if (read == 0) + { + if ((top = NextStream()) == null) + { + return amountRead; + } + } + else + { + amountRead += read; + } + } + + return amountRead; + } + + /// + /// Reads the specified number of characters from the stack, starting from a specified point in the byte array. + /// + /// + /// A byte array containing all the data up to but not including the next newline in the stack. + /// + public byte[] ReadByteLine() + { + bool dummy; + return ReadByteLine(out dummy); + } + + /// + /// Reads a line from the stack delimited by the newline for this platform. The newline + /// characters will not be included in the stream + /// + /// + /// This will be set to true if we did not end on a newline but instead found the end of + /// our data. + /// + /// + /// The containing the line. + /// + public byte[] ReadByteLine(out bool hitStreamEnd) + { + hitStreamEnd = false; + if (!HasData()) + { + // No streams, no data! + return null; + } + + // This is horribly inefficient, consider profiling here if + // it becomes an issue. + BinaryReader top = streams.Peek(); + byte[] ignore = CurrentEncoding.GetBytes(new[] {'\r'}); + byte[] search = CurrentEncoding.GetBytes(new[] {'\n'}); + int searchPos = 0; + var builder = new MemoryStream(); + + while (true) + { + // First we need to read a byte from one of the streams + var bytes = new byte[search.Length]; + int amountRead = top.Read(bytes, 0, bytes.Length); + while (amountRead == 0) + { + streams.Pop(); + if (!streams.Any()) + { + hitStreamEnd = true; + return builder.ToArray(); + } + + top.Dispose(); + top = streams.Peek(); + amountRead = top.Read(bytes, 0, bytes.Length); + } + + // Now we've got some bytes, we need to check it against the search array. + foreach (byte b in bytes) + { + if (ignore.Contains(b)) + { + continue; + } + + if (b == search[searchPos]) + { + searchPos += 1; + } + else + { + // We only want to append the information if it's + // not part of the newline sequence + if (searchPos != 0) + { + byte[] append = search.Take(searchPos).ToArray(); + builder.Write(append, 0, append.Length); + } + + builder.Write(new[] {b}, 0, 1); + searchPos = 0; + } + + // Finally if we've found our string + if (searchPos == search.Length) + { + return builder.ToArray(); + } + } + } + } + + /// + /// Reads a line from the stack delimited by the newline for this platform. The newline + /// characters will not be included in the stream + /// + /// + /// The containing the line. + /// + public string ReadLine() + { + bool dummy; + return ReadLine(out dummy); + } + + /// + /// Reads a line from the stack delimited by the newline for this platform. The newline + /// characters will not be included in the stream + /// + /// + /// This will be set to true if we did not end on a newline but instead found the end of + /// our data. + /// + /// + /// The containing the line. + /// + public string ReadLine(out bool hitStreamEnd) + { + bool foundEnd; + byte[] result = ReadByteLine(out foundEnd); + hitStreamEnd = foundEnd; + + if (result == null) + { + return null; + } + + return CurrentEncoding.GetString(result); + } + + #endregion + + #region Methods + + /// + /// Removes the current reader from the stack and ensures it is correctly + /// destroyed and then returns the next available reader. If no reader + /// is available this method returns null. + /// + /// + /// The next reader. + /// + private BinaryReader NextStream() + { + BinaryReader top = streams.Pop(); + top.Dispose(); + + return streams.Count != 0 ? streams.Peek() : null; + } + + #endregion + } } \ No newline at end of file From 3262cf92bdaa9fc45fda8ea8571a189919eb0fcb Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Fri, 26 Dec 2025 01:47:22 +0400 Subject: [PATCH 11/52] =?UTF-8?q?=D0=BF=D0=BE=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=BF=D0=BE=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=87?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=8F=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Core/Contexts/ClassBuilder.cs | 5 +++-- src/ScriptEngine/Machine/MachineInstance.cs | 1 - src/oscript/Web/Multipart/BinaryStreamStack.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OneScript.Core/Contexts/ClassBuilder.cs b/src/OneScript.Core/Contexts/ClassBuilder.cs index c402e9591..3b7839c83 100644 --- a/src/OneScript.Core/Contexts/ClassBuilder.cs +++ b/src/OneScript.Core/Contexts/ClassBuilder.cs @@ -111,9 +111,10 @@ private static bool MarkedAsContextMethod(MemberInfo member, bool includeDepreca .Any(x => includeDeprecations || (x as ContextMethodAttribute)?.IsDeprecated == false); } - private static bool MarkedAsContextProperty(MemberInfo member, bool includeDeprecations = false) + private static bool MarkedAsContextProperty(PropertyInfo member, bool includeDeprecations = false) { - return member.GetCustomAttributes(typeof(ContextPropertyAttribute), false).Length != 0; + return member.GetCustomAttributes(typeof(ContextPropertyAttribute), false) + .Any(x => includeDeprecations || (x as ContextPropertyAttribute)?.IsDeprecated == false); } public ClassBuilder ExportConstructor(MethodInfo info) diff --git a/src/ScriptEngine/Machine/MachineInstance.cs b/src/ScriptEngine/Machine/MachineInstance.cs index 288b726f5..eb5d73bf1 100644 --- a/src/ScriptEngine/Machine/MachineInstance.cs +++ b/src/ScriptEngine/Machine/MachineInstance.cs @@ -24,7 +24,6 @@ This Source Code Form is subject to the terms of the using OneScript.Values; using ScriptEngine.Compiler; using ScriptEngine.Machine.Debugger; -using System.Dynamic; namespace ScriptEngine.Machine { diff --git a/src/oscript/Web/Multipart/BinaryStreamStack.cs b/src/oscript/Web/Multipart/BinaryStreamStack.cs index 3fe42c7fd..bb07961cc 100644 --- a/src/oscript/Web/Multipart/BinaryStreamStack.cs +++ b/src/oscript/Web/Multipart/BinaryStreamStack.cs @@ -295,7 +295,7 @@ public byte[] ReadByteLine(out bool hitStreamEnd) while (amountRead == 0) { streams.Pop(); - if (!streams.Any()) + if (streams.Count == 0) { hitStreamEnd = true; return builder.ToArray(); From 67bd5899fff67e295036427b3ac50c70337745d4 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Sat, 27 Dec 2025 14:21:12 +0300 Subject: [PATCH 12/52] =?UTF-8?q?=D0=91=D0=B0=D0=BC=D0=BF=20=D0=BE=D1=87?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B4=D0=BD=D0=BE=D0=B9=20=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=81=D0=B8=D0=B8=20=D0=B4=D0=B5=D0=B1=D0=B0=D0=B3-=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D0=BF=D1=82=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/VSCode.DebugAdapter/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VSCode.DebugAdapter/package.json b/src/VSCode.DebugAdapter/package.json index 931c390c5..a0938e592 100644 --- a/src/VSCode.DebugAdapter/package.json +++ b/src/VSCode.DebugAdapter/package.json @@ -1,7 +1,7 @@ { "name": "oscript-debug", "displayName": "OneScript Debug (BSL)", - "version": "1.0.0", + "version": "1.1.0", "publisher": "EvilBeaver", "description": "Visual Studio Code debugger extension for OneScript (BSL)", "icon": "images/logo-dbg.png", From 48b94ce73e4bc7db5e7181eb27eb9aacb54b0dd9 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Sat, 27 Dec 2025 14:29:55 +0300 Subject: [PATCH 13/52] =?UTF-8?q?=D0=90=D0=BA=D1=82=D1=83=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D0=B7=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D1=83=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA=D0=B5?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README-EN.md | 33 ++++++++++++++------------------- README.md | 33 ++++++++++++++------------------- 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/README-EN.md b/README-EN.md index 5b1dac8a4..531111fc8 100644 --- a/README-EN.md +++ b/README-EN.md @@ -34,28 +34,23 @@ The OneScript distribution already includes a set of the most commonly used pack ### Linux ### -- (interactively) download the required package from the [official website](https://oscript.io) or installer from the [Releases](https://github.com/EvilBeaver/OneScript/releases) section and install it. +- Download the ZIP archive for Linux from the [Releases](https://github.com/EvilBeaver/OneScript/releases) section or from the [official website](https://oscript.io). +- Extract the archive to a convenient directory. +- Set executable permissions: + ```bash + chmod +x oscript + ``` ### MacOS ### -There is no interactive installer, but the engine can be installed from the command line: - -- install [homebrew](https://brew.sh/index_ru) -- install mono with the command `brew install mono` -- download [ovm](https://github.com/oscript-library/ovm/releases). Download the `ovm.exe` file (despite the .exe extension it will work on MacOS) -- run the command `mono path/to/ovm.exe install stable` - - *Tip: To correctly specify the path to `ovm.exe`, drag the `ovm.exe` file into the terminal. Example of a full command: `mono Users/username/Downloads/ovm.exe install stable`* -- run the command `mono path/to/ovm.exe use stable` -- restart the terminal - -#### Additional configuration for Self-Contained distribution (does not require dotnet installation) - -``` -chmod +x ./oscript -xattr -d com.apple.quarantine *.dylib oscript -codesign -s - ./oscript -``` +- Download the ZIP archive for macOS (x64 or arm64) from the [Releases](https://github.com/EvilBeaver/OneScript/releases) section or from the [official website](https://oscript.io). +- Extract the archive to a convenient directory. +- Perform additional configuration to remove quarantine and sign the binary: + ```bash + chmod +x ./oscript + xattr -d com.apple.quarantine *.dylib oscript + codesign -s - ./oscript + ``` # Manual local build diff --git a/README.md b/README.md index 98fab370d..959253f91 100644 --- a/README.md +++ b/README.md @@ -34,28 +34,23 @@ OneScript позволяет создавать и выполнять текст ### Linux ### -- (интерактивно) скачать нужный пакет [официального сайта](https://oscript.io) или установщик из раздела [Releases](https://github.com/EvilBeaver/OneScript/releases) и установить его. +- Скачать ZIP-архив для Linux со [страницы релизов](https://github.com/EvilBeaver/OneScript/releases) или с [официального сайта](https://oscript.io). +- Распаковать архив в удобный каталог. +- Установить права на выполнение: + ```bash + chmod +x oscript + ``` ### MacOS ### -Интерактивного установщика нет, но движок можно установить из командной строки: - -- установить [homebrew](https://brew.sh/index_ru) -- установить mono командой `brew install mono` -- скачать [ovm](https://github.com/oscript-library/ovm/releases). Скачать файл `ovm.exe` (несмотря на расширение .exe он сработает на MacOS) -- выполнить команду `mono path/to/ovm.exe install stable` - - *Совет: Чтобы корректно указать путь до `ovm.exe` перенесите мышкой файл `ovm.exe` в терминал. Пример полной команды: `mono Users/username/Downloads/ovm.exe install stable`* -- выполнить команду `mono path/to/ovm.exe use stable` -- перезапустить терминал - -#### Донастройка Self-Contained варианта поставки (не требующего инсталляции dotnet) - -``` -chmod +x ./oscript -xattr -d com.apple.quarantine *.dylib oscript -codesign -s - ./oscript -``` +- Скачать ZIP-архив для macOS (x64 или arm64) со [страницы релизов](https://github.com/EvilBeaver/OneScript/releases) или с [официального сайта](https://oscript.io). +- Распаковать архив в удобный каталог. +- Выполнить донастройку для снятия карантина и подписи: + ```bash + chmod +x ./oscript + xattr -d com.apple.quarantine *.dylib oscript + codesign -s - ./oscript + ``` # Ручная локальная сборка From 1c7de1e39885ab6ca480556caf07e6003d30e082 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Sat, 27 Dec 2025 17:16:33 +0300 Subject: [PATCH 14/52] =?UTF-8?q?=D0=A3=D1=82=D0=BE=D1=87=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BD=D1=81=D0=BE=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC=D0=B0=D0=BC=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/oscript/ShowUsageBehavior.cs | 41 ++++++++++++++++++++++---------- tests/process.os | 33 +++++++++++++++---------- 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/oscript/ShowUsageBehavior.cs b/src/oscript/ShowUsageBehavior.cs index 5b5baba3a..ee1c26a57 100644 --- a/src/oscript/ShowUsageBehavior.cs +++ b/src/oscript/ShowUsageBehavior.cs @@ -14,22 +14,37 @@ public override int Execute() Output.WriteLine($"1Script Execution Engine. Version {Program.GetVersion()}"); Output.WriteLine(); Output.WriteLine("Usage:"); + Output.WriteLine(" oscript.exe [options] [script_arguments...]"); + Output.WriteLine(" oscript.exe [mode_options] [script_arguments...]"); Output.WriteLine(); - Output.WriteLine("I. Script execution: oscript.exe [script arguments..]"); - Output.WriteLine(); - Output.WriteLine("II. Special mode: oscript.exe [script arguments..]"); - Output.WriteLine("Mode can be one of these:"); - Output.WriteLine($" {"-measure",-12}measures execution time"); - Output.WriteLine($" {"-compile",-12}shows compiled module without execution"); - Output.WriteLine($" {"-check [-env=]",-12}provides syntax check"); - Output.WriteLine($" {"-check -cgi",-12}provides syntax check in CGI-mode"); - Output.WriteLine($" {"-version",-12}output version string"); + + const int modeWidth = -18; + const int subOptionWidth = -14; + + Output.WriteLine("Modes:"); + Output.WriteLine($" {"-measure",modeWidth} Measures script execution time."); + Output.WriteLine($" {"-compile",modeWidth} Shows compiled module without execution."); + Output.WriteLine($" {"-check",modeWidth} Provides syntax check."); + Output.WriteLine($" {"",modeWidth} Options:"); + Output.WriteLine($" {"",modeWidth} {"-cgi",subOptionWidth} Syntax check in CGI-mode."); + Output.WriteLine($" {"",modeWidth} {"-env=",subOptionWidth} Path to entrypoint file for context."); + + Output.WriteLine($" {"-debug",modeWidth} Runs script in debug mode."); + Output.WriteLine($" {"",modeWidth} Options:"); + Output.WriteLine($" {"",modeWidth} {"-port=",subOptionWidth} Debugger port (default is 2801)."); + Output.WriteLine($" {"",modeWidth} {"-noWait",subOptionWidth} Do not wait for debugger connection."); + + Output.WriteLine($" {"-version, -v",modeWidth} Output version string."); Output.WriteLine(); - Output.WriteLine(" -encoding= set output encoding"); - Output.WriteLine(" -codestat= write code statistics"); + + Output.WriteLine("Options:"); + Output.WriteLine($" {"-encoding=",modeWidth} Set output encoding (e.g. utf-8)."); + Output.WriteLine($" {"-codestat=",modeWidth} Write code execution statistics to file."); Output.WriteLine(); - Output.WriteLine("III. Run as CGI application: oscript.exe -cgi [script arguments..]"); - Output.WriteLine(" Runs as CGI application under HTTP-server (Apache/Nginx/IIS/etc...)"); + + Output.WriteLine("CGI Mode:"); + Output.WriteLine(" oscript.exe -cgi [script_arguments...]"); + Output.WriteLine(" Runs as CGI application under HTTP-server."); return 0; } diff --git a/tests/process.os b/tests/process.os index b65cc25c8..44da651b5 100644 --- a/tests/process.os +++ b/tests/process.os @@ -144,22 +144,29 @@ "1Script Execution Engine. Version " + СИ.Версия + " | |Usage: + | oscript.exe [options] [script_arguments...] + | oscript.exe [mode_options] [script_arguments...] | - |I. Script execution: oscript.exe [script arguments..] + |Modes: + | -measure Measures script execution time. + | -compile Shows compiled module without execution. + | -check Provides syntax check. + | Options: + | -cgi Syntax check in CGI-mode. + | -env= Path to entrypoint file for context. + | -debug Runs script in debug mode. + | Options: + | -port= Debugger port (default is 2801). + | -noWait Do not wait for debugger connection. + | -version, -v Output version string. | - |II. Special mode: oscript.exe [script arguments..] - |Mode can be one of these: - | -measure measures execution time - | -compile shows compiled module without execution - | -check [-env=]provides syntax check - | -check -cgi provides syntax check in CGI-mode - | -version output version string + |Options: + | -encoding= Set output encoding (e.g. utf-8). + | -codestat= Write code execution statistics to file. | - | -encoding= set output encoding - | -codestat= write code statistics - | - |III. Run as CGI application: oscript.exe -cgi [script arguments..] - | Runs as CGI application under HTTP-server (Apache/Nginx/IIS/etc...)"; + |CGI Mode: + | oscript.exe -cgi [script_arguments...] + | Runs as CGI application under HTTP-server."; Возврат НормализоватьПереводыСтрок(Текст); From 92d33e5ab301314272af3dbac80c6fce6e58b3e4 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Sat, 27 Dec 2025 17:29:49 +0300 Subject: [PATCH 15/52] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20global-funcs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/global-funcs.os | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/global-funcs.os b/tests/global-funcs.os index 159433ae9..2acfd4bef 100644 --- a/tests/global-funcs.os +++ b/tests/global-funcs.os @@ -14,7 +14,8 @@ Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт Если мАдресРесурса = Неопределено Тогда - ПодключитьСценарий("./tests/http.os", "http_тест"); + ПутьКHttpТесту = ОбъединитьПути(ТекущийСценарий().Каталог, "http.os"); + ПодключитьСценарий(ПутьКHttpТесту, "http_тест"); мАдресРесурса = Новый http_тест().ВыбратьДоступныйХост(); КонецЕсли; From 62779ebd3d4bd51da19a8f190717e8f2363991a7 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 1 Jan 2026 15:30:15 +0300 Subject: [PATCH 16/52] =?UTF-8?q?=D0=9E=D0=BF=D0=B8=D1=81=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8=202.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/release-notes.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/install/release-notes.md b/install/release-notes.md index 1cbadb956..dd8193e6a 100644 --- a/install/release-notes.md +++ b/install/release-notes.md @@ -1,19 +1,23 @@ -# Версия 2.0.0-rc.10 +# Версия 2.0.0 -## Исправления ошибок, обнаруженных в 2.0.0-rc.10 +Версия нового поколения, основанная на современной платформе .NET. +За счет этого обеспечивается кроссплатформенность без использования Mono на Linux и MacOS. -* Изменено поведение по умолчанию для опции lang.explicitImports под отладкой. Теперь неявные импорты не считаются критичной ошибкой -* #1627: Исправлена ошибка несрабатывания отладчика на коротких скриптах +## Требования по адаптации -## Исправление текущих ошибок +За счет новой базовой платформы, потеряна бинарная совместимость с существующими внешними компонентами (dll). +Требуется перекомпиляция компоненты под новую версию .net, как минимум, под слой совместимости netstandard 2.0 -* #1580, #1626: Улучшено соответствие поведения ТаблицыЗначений поведению 1С -* Исправлена медленная вставка строк в индексированную таблицу значений -* +## Изменения по сравнению с ознакомительной версией 2.0.0-rc.10 -## Новые возможности - -* Доступ к сырому сетевому потоку при вызове http-метода. Позволяет организовать http-streaming -* Использование аннотаций внутри параметров аннотаций +* Уточнена консольная справка по режимам работы +* Исправлена лишняя остановка отладчиком на заголовке цикла Для Каждого +* Исправлены шаги отладчика. Ранее, отладчик ошибочно не останавливался на строке КонецЦикла +* Добавлена поддержка потока в ЗаписьТекста +* Оптимизирована производительность рефлектора +* Оптимизирована производительность компилятора +* Оптимизирована производительность создания таблицы значений +* В отладчике теперь отображаются переменные уровня модуля (ранее отображались только локальные) +* Улучшена совместимость функции СтрНайти с платформой 1С -Большое спасибо @Mr-Rm, @dmpas, @asosnoviy, @nixel2007, @Bayselonarrend за участие в этом релизе! \ No newline at end of file +Большое спасибо @Mr-Rm, @dmpas, @Bayselonarrend за участие в этом релизе! \ No newline at end of file From aef45ceed290190bd5fd57e76e5349e16c6e1072 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 1 Jan 2026 17:29:16 +0300 Subject: [PATCH 17/52] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=20Jenkinsfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 48 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 43640c1b8..fa1d935f5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,6 @@ pipeline { environment { VersionPrefix = '2.0.0' - VersionSuffix = 'rc.11'+"+${BUILD_NUMBER}" outputEnc = '65001' } @@ -240,6 +239,29 @@ pipeline { } } + stage ('Publishing latest') { + when { + anyOf { + branch 'release/latest'; + } + } + agent { label 'master' } + options { skipDefaultCheckout() } + + steps { + cleanWs() + checkout scm // чтобы получить файл release-notes + unstash 'dist' + unstash 'vsix' + + // Положит описание для сайта + publishReleaseNotes('latest') + + // Положит файлы дистрибутива в целевую папку + publishRelease('latest', true) + } + } + stage ('Publishing artifacts to clouds') { when { anyOf { @@ -266,7 +288,7 @@ pipeline { agent { label 'linux' } when { anyOf { - branch 'release/latest' + branch 'release/lts' expression { return env.TAG_NAME && env.TAG_NAME.startsWith('v1.') } @@ -274,7 +296,7 @@ pipeline { } steps { script { - def codename = env.TAG_NAME ? env.TAG_NAME : 'latest' + def codename = env.TAG_NAME ? env.TAG_NAME : 'lts' publishDockerImage('v1', codename) } } @@ -285,6 +307,7 @@ pipeline { when { anyOf { branch 'develop' + branch 'release/latest' expression { return env.TAG_NAME && env.TAG_NAME.startsWith('v2.') } @@ -302,6 +325,20 @@ pipeline { } } +def fullVersionNumber() { + def version = env.VersionPrefix + if (env.VersionSuffix != null && !env.VersionSuffix.isEmpty()) + { + version = version + "-${env.VersionSuffix}" + } + + return version +} + +def underscoredVersion() { + return fullVersionNumber().replaceAll("\\.", "_") +} + def publishRelease(codename, isNumbered) { dir('targetContent') { sh """ @@ -317,7 +354,8 @@ def publishRelease(codename, isNumbered) { """.stripIndent() if (isNumbered) { - def version="${env.VersionPrefix}-${env.VersionSuffix}".replaceAll("\\.", "_") + + def version = underscoredVersion() sh """ TARGET="/var/www/oscript.io/download/versions/${version}/" @@ -330,7 +368,7 @@ def publishRelease(codename, isNumbered) { def publishReleaseNotes(codename) { dir('markdownContent') { - def version="${env.VersionPrefix}-${env.VersionSuffix}".replaceAll("\\.", "_") + def version=underscoredVersion() def targetDir='/var/www/oscript.io/markdown/versions' sh """ From c7c8e6ba34cd7b8503e00d69d6727226ae567abd Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 1 Jan 2026 17:32:53 +0300 Subject: [PATCH 18/52] =?UTF-8?q?=D0=9F=D1=83=D0=B1=D0=BB=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BD=D0=BE=D0=BC=D0=B5=D1=80=D0=BD?= =?UTF-8?q?=D0=BE=D0=B9=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8=20=D0=B2=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=BA=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index fa1d935f5..e90a1243b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -308,14 +308,19 @@ pipeline { anyOf { branch 'develop' branch 'release/latest' - expression { - return env.TAG_NAME && env.TAG_NAME.startsWith('v2.') - } } } steps { script { - def codename = env.TAG_NAME ? env.TAG_NAME : 'dev' + def codename = '' + if (env.VersionSuffix != null && !env.VersionSuffix.isEmpty() { + codename = 'dev' + } + else + { + codename = fullVersionNumber() + } + publishDockerImage('v2', codename) } } From 984043956bb65b9d1f85993fa99731c3c701abac Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 1 Jan 2026 18:08:00 +0300 Subject: [PATCH 19/52] =?UTF-8?q?=D0=A1=D0=B8=D0=BD=D1=82=D0=B0=D0=BA?= =?UTF-8?q?=D1=81=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B0=D1=8F=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index e90a1243b..40ec7f4d1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -313,7 +313,7 @@ pipeline { steps { script { def codename = '' - if (env.VersionSuffix != null && !env.VersionSuffix.isEmpty() { + if (env.VersionSuffix != null && !env.VersionSuffix.isEmpty()) { codename = 'dev' } else From 82bced4295ead45d6208c4df71e371e216af6dd7 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 1 Jan 2026 18:30:47 +0300 Subject: [PATCH 20/52] =?UTF-8?q?=D0=9E=D1=82=D0=BA=D0=BB=D1=8E=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D0=B0=D0=B7=D0=B0=20=D0=BF=D1=83?= =?UTF-8?q?=D0=B1=D0=BB=D0=B8=D0=BA=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BD=D1=83?= =?UTF-8?q?=D0=B3=D0=B5=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 40ec7f4d1..377960cc1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -262,25 +262,25 @@ pipeline { } } - stage ('Publishing artifacts to clouds') { - when { - anyOf { - branch 'release/latest'; - branch 'release/preview'; - } - } - - agent { label 'windows' } - - steps{ - - unstash 'buildResults' - - withCredentials([string(credentialsId: 'NuGetToken', variable: 'NUGET_TOKEN')]) { - bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:PublishNuget /p:NugetToken=$NUGET_TOKEN" - } - } - } +// stage ('Publishing artifacts to clouds') { +// when { +// anyOf { +// branch 'release/latest'; +// branch 'release/preview'; +// } +// } +// +// agent { label 'windows' } +// +// steps{ +// +// unstash 'buildResults' +// +// withCredentials([string(credentialsId: 'NuGetToken', variable: 'NUGET_TOKEN')]) { +// bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:PublishNuget /p:NugetToken=$NUGET_TOKEN" +// } +// } +// } stage ('Publishing docker-images') { parallel { From 155e1caef8733bf03c9715b2e3c3237737f31e3b Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 1 Jan 2026 18:50:55 +0300 Subject: [PATCH 21/52] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=B8=D0=B7=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=B8=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=86=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BE=20=D0=BD=D0=BE=D0=BC=D0=B5=D1=80=D0=B5=20=D0=B1=D0=B8?= =?UTF-8?q?=D0=BB=D0=B4=D0=B0=20=D0=BD=D0=B0=20jenkins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/oscommon.targets | 1 - 1 file changed, 1 deletion(-) diff --git a/src/oscommon.targets b/src/oscommon.targets index d47770e3a..019db5bd6 100644 --- a/src/oscommon.targets +++ b/src/oscommon.targets @@ -26,7 +26,6 @@ 2.0.0 $(VersionPrefix).$(BuildNumber) $(VersionPrefix)-$(VersionSuffix) - $(VersionPrefix)+$(BuildNumber) 1C (BSL) language runtime Copyright (c) 2021 EvilBeaver From 3375cc8da42ff7676310351945bb193cb62dd8cb Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 1 Jan 2026 19:15:13 +0300 Subject: [PATCH 22/52] =?UTF-8?q?=D0=92=D0=B5=D1=80=D0=BD=D1=83=D0=BB=20?= =?UTF-8?q?=D0=BF=D1=83=D0=B1=D0=BB=D0=B8=D0=BA=D0=B0=D1=86=D0=B8=D1=8E=20?= =?UTF-8?q?=D0=BD=D1=83=D0=B3=D0=B5=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 377960cc1..6f2106345 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -262,25 +262,25 @@ pipeline { } } -// stage ('Publishing artifacts to clouds') { -// when { -// anyOf { -// branch 'release/latest'; -// branch 'release/preview'; -// } -// } -// -// agent { label 'windows' } -// -// steps{ -// -// unstash 'buildResults' -// -// withCredentials([string(credentialsId: 'NuGetToken', variable: 'NUGET_TOKEN')]) { -// bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:PublishNuget /p:NugetToken=$NUGET_TOKEN" -// } -// } -// } + stage ('Publishing artifacts to clouds') { + when { + anyOf { + branch 'release/latest'; + branch 'release/preview'; + } + } + + agent { label 'windows' } + + steps{ + + unstash 'buildResults' + + withCredentials([string(credentialsId: 'NuGetToken', variable: 'NUGET_TOKEN')]) { + bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:PublishNuget /p:NugetToken=$NUGET_TOKEN" + } + } + } stage ('Publishing docker-images') { parallel { From f8b9e00f6c9d39ec2c3a03c77eb09722d44b32ef Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 1 Jan 2026 19:16:15 +0300 Subject: [PATCH 23/52] =?UTF-8?q?=D0=9D=D0=BE=D0=BC=D0=B5=D1=80=20=D0=BE?= =?UTF-8?q?=D1=87=D0=B5=D1=80=D0=B5=D0=B4=D0=BD=D0=BE=D0=B9=20=D0=B4=D0=B5?= =?UTF-8?q?=D0=B2-=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 6f2106345..ee290e78c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,7 +4,8 @@ pipeline { agent none environment { - VersionPrefix = '2.0.0' + VersionPrefix = '2.0.1' + VersionSuffix = 'rc.1'+"+${BUILD_NUMBER}" outputEnc = '65001' } From b4e8fb8e224f3a69493fc7025a5ff33252c659ed Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Sun, 11 Jan 2026 12:19:40 +0300 Subject: [PATCH 24/52] =?UTF-8?q?closes=20#1646=20=D0=9E=D1=88=D0=B8=D0=B1?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=B0=20=D0=97?= =?UTF-8?q?=D0=B0=D0=BF=D0=BE=D0=BB=D0=BD=D0=B8=D1=82=D1=8C=D0=97=D0=BD?= =?UTF-8?q?=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=D0=A1=D0=B2=D0=BE=D0=B9?= =?UTF-8?q?=D1=81=D1=82=D0=B2=20=D0=BD=D0=B0=20=D0=A4=D0=B8=D0=BA=D1=81?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD=D0=BE=D0=B9=D0=A1?= =?UTF-8?q?=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Collections/FixedStructureImpl.cs | 5 +++++ tests/fixed-structure.os | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/OneScript.StandardLibrary/Collections/FixedStructureImpl.cs b/src/OneScript.StandardLibrary/Collections/FixedStructureImpl.cs index 146d71eba..a208576d9 100644 --- a/src/OneScript.StandardLibrary/Collections/FixedStructureImpl.cs +++ b/src/OneScript.StandardLibrary/Collections/FixedStructureImpl.cs @@ -85,6 +85,11 @@ public override BslPropertyInfo GetPropertyInfo(int propertyNumber) .Build(); } + public override string GetPropName(int propNum) + { + return _structure.GetPropName(propNum); + } + public override void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) { var binding = _methods.GetCallableDelegate(methodNumber); diff --git a/tests/fixed-structure.os b/tests/fixed-structure.os index 19e00dac9..0374d357f 100644 --- a/tests/fixed-structure.os +++ b/tests/fixed-structure.os @@ -24,6 +24,7 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодСвойство"); ВсеТесты.Добавить("ТестДолжен_ПроверитьОтсутствиеМетодаВставить"); ВсеТесты.Добавить("ТестДолжен_СоздатьСтруктуруПоФиксированнойСтруктуре"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЗаполнитьЗначенияСвойствИзФиксированнойСтруктуры"); Возврат ВсеТесты; КонецФункции @@ -165,3 +166,17 @@ Ожидаем.Что(Структура.Ключ2, "значения элементов совпадут").Равно(ФиксированнаяСтруктура.Ключ2); КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЗаполнитьЗначенияСвойствИзФиксированнойСтруктуры() Экспорт + + Структура1 = Новый ФиксированнаяСтруктура("тест1,тест2,тест3", "тест", Истина); + Структура2 = Новый Структура(Структура1); + + // Проверяем, что метод ЗаполнитьЗначенияСвойств работает с ФиксированнаяСтруктура + ЗаполнитьЗначенияСвойств(Структура2, Структура1); + + Ожидаем.Что(Структура2.тест1, "значения свойства тест1 совпадут").Равно(Структура1.тест1); + Ожидаем.Что(Структура2.тест2, "значения свойства тест2 совпадут").Равно(Структура1.тест2); + Ожидаем.Что(Структура2.тест3, "значения свойства тест3 совпадут").Равно(Структура1.тест3); + +КонецПроцедуры From 877a682efe303bad847b486cb87214ff8ef88c15 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Mon, 12 Jan 2026 13:48:16 +0300 Subject: [PATCH 25/52] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B2=D1=81=D1=82=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BA=D0=BB=D1=8E=D1=87=D0=B0=20Null=20=D0=B2=20?= =?UTF-8?q?=D1=81=D0=BE=D0=BE=D1=82=D0=B2=D0=B5=D1=82=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D0=B8=D0=B5=20fixes=20#1647?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Machine/GenericIValueComparer.cs | 16 ++- tests/collections.os | 128 ++++++++++++++++++ 2 files changed, 139 insertions(+), 5 deletions(-) diff --git a/src/ScriptEngine/Machine/GenericIValueComparer.cs b/src/ScriptEngine/Machine/GenericIValueComparer.cs index 5d913a5dd..e8c53da8a 100644 --- a/src/ScriptEngine/Machine/GenericIValueComparer.cs +++ b/src/ScriptEngine/Machine/GenericIValueComparer.cs @@ -21,8 +21,8 @@ public class GenericIValueComparer : IEqualityComparer, IComparerorderedTypes = new List { BasicTypes.Undefined, BasicTypes.Null, BasicTypes.Boolean, - BasicTypes.Number, BasicTypes.String, BasicTypes.Date, BasicTypes.Type }; - + BasicTypes.Number, BasicTypes.String, BasicTypes.Date, BasicTypes.Type }; + private const int INDEX_OF_TYPE = 6; public GenericIValueComparer() @@ -47,6 +47,9 @@ public int GetHashCode(IValue obj) if (obj is BslUndefinedValue) return obj.GetHashCode(); + if (obj is BslNullValue) + return obj.GetHashCode(); + try { CLR_obj = ContextValuesMarshaller.ConvertToClrObject(obj); @@ -56,6 +59,9 @@ public int GetHashCode(IValue obj) CLR_obj = obj; } + if (CLR_obj == null) + return 0; + return CLR_obj.GetHashCode(); } @@ -67,8 +73,8 @@ private int CompareAsStrings(IValue x, IValue y) private int CompareByPresentations(IValue x, IValue y) { return ((BslValue)x).ToString(_process).CompareTo(((BslValue)y).ToString(_process)); - } - + } + /// /// Сравнение переменных разных типов. Правила сравнения соответствуют 1С v8.3.27: /// Переменные типа "Тип" следуют за всеми прочими; @@ -82,7 +88,7 @@ private int CompareByTypes(IValue x, IValue y) var iy = orderedTypes.IndexOf(y.SystemType); if (ix >= 0) - if (iy >= 0) + if (iy >= 0) return ix - iy; else return ix == INDEX_OF_TYPE ? 1 : -1; diff --git a/tests/collections.os b/tests/collections.os index cb73fe601..516e533b5 100644 --- a/tests/collections.os +++ b/tests/collections.os @@ -7,6 +7,14 @@ ВсеТесты = Новый Массив; ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючНеопределеноВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючNullВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючБулевоВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючЧислоВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючСтрокаВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючДатаВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючУникальныйИдентификаторВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючТипВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРазличныеКлючиВСоответствии"); Возврат ВсеТесты; КонецФункции @@ -18,4 +26,124 @@ юТест.ПроверитьРавенство(1, Соотв.Количество()); юТест.ПроверитьРавенство(Истина, Соотв[Неопределено]); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючNullВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Соотв.Вставить(Null, "Значение для Null"); + юТест.ПроверитьРавенство(1, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для Null", Соотв[Null]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючБулевоВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Соотв.Вставить(Истина, "Значение для Истина"); + Соотв.Вставить(Ложь, "Значение для Ложь"); + + юТест.ПроверитьРавенство(2, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для Истина", Соотв[Истина]); + юТест.ПроверитьРавенство("Значение для Ложь", Соотв[Ложь]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючЧислоВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Соотв.Вставить(42, "Значение для 42"); + Соотв.Вставить(0, "Значение для 0"); + Соотв.Вставить(-100, "Значение для -100"); + Соотв.Вставить(3.14, "Значение для 3.14"); + + юТест.ПроверитьРавенство(4, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для 42", Соотв[42]); + юТест.ПроверитьРавенство("Значение для 0", Соотв[0]); + юТест.ПроверитьРавенство("Значение для -100", Соотв[-100]); + юТест.ПроверитьРавенство("Значение для 3.14", Соотв[3.14]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючСтрокаВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Соотв.Вставить("Ключ1", "Значение1"); + Соотв.Вставить("Ключ2", "Значение2"); + Соотв.Вставить("", "Значение для пустой строки"); + + юТест.ПроверитьРавенство(3, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение1", Соотв["Ключ1"]); + юТест.ПроверитьРавенство("Значение2", Соотв["Ключ2"]); + юТест.ПроверитьРавенство("Значение для пустой строки", Соотв[""]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючДатаВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Дата1 = '20151231'; + Дата2 = '20200101'; + + Соотв.Вставить(Дата1, "Значение для Дата1"); + Соотв.Вставить(Дата2, "Значение для Дата2"); + + юТест.ПроверитьРавенство(2, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для Дата1", Соотв[Дата1]); + юТест.ПроверитьРавенство("Значение для Дата2", Соотв[Дата2]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючУникальныйИдентификаторВСоответствии() Экспорт + + Соотв = Новый Соответствие; + УИД1 = Новый УникальныйИдентификатор; + УИД2 = Новый УникальныйИдентификатор; + + Соотв.Вставить(УИД1, "Значение для УИД1"); + Соотв.Вставить(УИД2, "Значение для УИД2"); + + юТест.ПроверитьРавенство(2, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для УИД1", Соотв[УИД1]); + юТест.ПроверитьРавенство("Значение для УИД2", Соотв[УИД2]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючТипВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Тип1 = Тип("Строка"); + Тип2 = Тип("Число"); + + Соотв.Вставить(Тип1, "Значение для Тип Строка"); + Соотв.Вставить(Тип2, "Значение для Тип Число"); + + юТест.ПроверитьРавенство(2, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для Тип Строка", Соотв[Тип1]); + юТест.ПроверитьРавенство("Значение для Тип Число", Соотв[Тип2]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРазличныеКлючиВСоответствии() Экспорт + + // Проверка, что различные типы значений могут быть одновременно ключами + Соотв = Новый Соответствие; + + Соотв[Неопределено] = "Неопределено"; + Соотв[Null] = "Null"; + Соотв[Истина] = "Истина"; + Соотв[42] = "Число"; + Соотв["Текст"] = "Строка"; + Соотв['20240101'] = "Дата"; + Соотв[Тип("Строка")] = "Тип"; + + юТест.ПроверитьРавенство(7, Соотв.Количество()); + юТест.ПроверитьРавенство("Неопределено", Соотв[Неопределено]); + юТест.ПроверитьРавенство("Null", Соотв[Null]); + юТест.ПроверитьРавенство("Истина", Соотв[Истина]); + юТест.ПроверитьРавенство("Число", Соотв[42]); + юТест.ПроверитьРавенство("Строка", Соотв["Текст"]); + юТест.ПроверитьРавенство("Дата", Соотв['20240101']); + юТест.ПроверитьРавенство("Тип", Соотв[Тип("Строка")]); + КонецПроцедуры \ No newline at end of file From 87eff883cc8d3cf2c37f776bbdf3e470cb1a3f11 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:09:07 +0100 Subject: [PATCH 26/52] Strip null characters from file paths to fix Windows WebDAV compatibility (#1649) * Initial plan * Initial plan for fixing null character handling in file paths Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Add null character stripping in FileContext and FindFiles Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Refactor: Extract StripNullCharacters to PathHelper utility class Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Final update: All checks passed Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Remove accidentally committed opm.ospx file Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Remove nuget.exe and C# test file (BSL tests are sufficient) Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Strip null characters from file paths to fix Windows WebDAV compatibility Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Revert .gitignore change - remove *.ospx pattern Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Remove accidentally re-added opm.ospx file Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --- src/OneScript.StandardLibrary/FileContext.cs | 4 + .../FileOperations.cs | 5 + src/OneScript.StandardLibrary/PathHelper.cs | 30 +++++ tests/null-character-handling.os | 110 ++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 src/OneScript.StandardLibrary/PathHelper.cs create mode 100644 tests/null-character-handling.os diff --git a/src/OneScript.StandardLibrary/FileContext.cs b/src/OneScript.StandardLibrary/FileContext.cs index 739a5da8a..9ba9f0abd 100644 --- a/src/OneScript.StandardLibrary/FileContext.cs +++ b/src/OneScript.StandardLibrary/FileContext.cs @@ -24,6 +24,10 @@ public class FileContext : AutoContext public FileContext(string name) { + // Strip null characters that can be added by Windows WebDAV client + // to maintain compatibility with 1.x behavior + name = PathHelper.StripNullCharacters(name); + if (String.IsNullOrWhiteSpace(name)) { _name = ""; diff --git a/src/OneScript.StandardLibrary/FileOperations.cs b/src/OneScript.StandardLibrary/FileOperations.cs index a3ddb95c5..7d6188851 100644 --- a/src/OneScript.StandardLibrary/FileOperations.cs +++ b/src/OneScript.StandardLibrary/FileOperations.cs @@ -138,6 +138,11 @@ public string GetTempFilename(string ext = null) [ContextMethod("НайтиФайлы", "FindFiles")] public ArrayImpl FindFiles(string dir, string mask = null, bool recursive = false) { + // Strip null characters that can be added by Windows WebDAV client + // to maintain compatibility with 1.x behavior + dir = PathHelper.StripNullCharacters(dir); + mask = PathHelper.StripNullCharacters(mask); + if (mask == null) { // fix 225, 227, 228 diff --git a/src/OneScript.StandardLibrary/PathHelper.cs b/src/OneScript.StandardLibrary/PathHelper.cs new file mode 100644 index 000000000..a7fd9d3c2 --- /dev/null +++ b/src/OneScript.StandardLibrary/PathHelper.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace OneScript.StandardLibrary +{ + /// + /// Utility methods for working with file paths + /// + internal static class PathHelper + { + /// + /// Strips null characters from a path string. + /// This is needed because Windows WebDAV client can add null characters to paths, + /// which causes ArgumentException in System.IO methods. + /// + /// Path that may contain null characters + /// Path with null characters removed, or null if input was null + public static string StripNullCharacters(string path) + { + if (path == null) + return null; + + return path.Replace("\0", ""); + } + } +} diff --git a/tests/null-character-handling.os b/tests/null-character-handling.os new file mode 100644 index 000000000..631f7cc7c --- /dev/null +++ b/tests/null-character-handling.os @@ -0,0 +1,110 @@ +Перем юТест; + +Функция Версия() Экспорт + Возврат "0.1"; +КонецФункции + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_СоздатьФайлСНулевымСимволом"); + ВсеТесты.Добавить("ТестДолжен_ВыполнитьНайтиФайлыСНулевымСимволом"); + ВсеТесты.Добавить("ТестДолжен_ПолучитьСвойстваФайлаСНулевымСимволом"); + + Возврат ВсеТесты; +КонецФункции + +Процедура ТестДолжен_СоздатьФайлСНулевымСимволом() Экспорт + + ВремФайл = ПолучитьИмяВременногоФайла("txt"); + ЗаписьТекста = Новый ЗаписьТекста(ВремФайл); + ЗаписьТекста.ЗаписатьСтроку("тестовое содержимое"); + ЗаписьТекста.Закрыть(); + + Попытка + // Добавляем нулевой символ в путь, как это делает Windows WebDAV клиент + ПутьСНулевымСимволом = ВремФайл + Символ(0); + Файл = Новый Файл(ПутьСНулевымСимволом); + + // Проверяем, что файл доступен и не выбрасывает исключение + юТест.ПроверитьИстину(Файл.Существует(), "Файл должен существовать"); + юТест.ПроверитьРавенство("txt", Прав(Файл.Расширение, 3), "Расширение должно быть txt"); + + Исключение + УдалитьФайлы(ВремФайл); + ВызватьИсключение; + КонецПопытки; + + УдалитьФайлы(ВремФайл); + +КонецПроцедуры + +Процедура ТестДолжен_ВыполнитьНайтиФайлыСНулевымСимволом() Экспорт + + ВремКаталог = КаталогВременныхФайлов(); + ИмяТестовогоФайла = "test_null_char_" + Формат(ТекущаяДата(), "ДФ=yyyyMMddHHmmss") + ".txt"; + ВремФайл = ОбъединитьПути(ВремКаталог, ИмяТестовогоФайла); + + ЗаписьТекста = Новый ЗаписьТекста(ВремФайл); + ЗаписьТекста.ЗаписатьСтроку("тестовое содержимое"); + ЗаписьТекста.Закрыть(); + + Попытка + // Добавляем нулевой символ в путь к каталогу + ПутьКаталогаСНулевымСимволом = ВремКаталог + Символ(0); + + // Проверяем, что НайтиФайлы не выбрасывает исключение + НайденныеФайлы = НайтиФайлы(ПутьКаталогаСНулевымСимволом, ИмяТестовогоФайла); + + // Проверяем, что файл был найден + юТест.ПроверитьРавенство(1, НайденныеФайлы.Количество(), "Должен быть найден один файл"); + юТест.ПроверитьРавенство(ИмяТестовогоФайла, НайденныеФайлы[0].Имя, "Имя файла должно совпадать"); + + Исключение + УдалитьФайлы(ВремФайл); + ВызватьИсключение; + КонецПопытки; + + УдалитьФайлы(ВремФайл); + +КонецПроцедуры + +Процедура ТестДолжен_ПолучитьСвойстваФайлаСНулевымСимволом() Экспорт + + ВремФайл = ПолучитьИмяВременногоФайла("txt"); + ЗаписьТекста = Новый ЗаписьТекста(ВремФайл); + ЗаписьТекста.ЗаписатьСтроку("тестовое содержимое"); + ЗаписьТекста.Закрыть(); + + Попытка + // Добавляем нулевой символ в конец пути + ПутьСНулевымСимволом = ВремФайл + Символ(0); + Файл = Новый Файл(ПутьСНулевымСимволом); + + // Проверяем, что все свойства доступны без исключений + ПолноеИмя = Файл.ПолноеИмя; + юТест.ПроверитьНеравенство("", ПолноеИмя, "ПолноеИмя не должно быть пустым"); + + Имя = Файл.Имя; + юТест.ПроверитьНеравенство("", Имя, "Имя не должно быть пустым"); + + Путь = Файл.Путь; + юТест.ПроверитьНеравенство("", Путь, "Путь не должен быть пустым"); + + Расширение = Файл.Расширение; + юТест.ПроверитьРавенство(".txt", Расширение, "Расширение должно быть .txt"); + + Размер = Файл.Размер(); + юТест.ПроверитьБольше(Размер, 0, "Размер должен быть больше 0"); + + Исключение + УдалитьФайлы(ВремФайл); + ВызватьИсключение; + КонецПопытки; + + УдалитьФайлы(ВремФайл); + +КонецПроцедуры From 5948bb572bebda8c448d948d64c32004f217e053 Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Thu, 22 Jan 2026 18:12:32 +0100 Subject: [PATCH 27/52] Update copilot instructions with commit reminders Added a reminder to check changes before committing and avoid committing binary files unless requested. --- .github/copilot-instructions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 29652043f..96a141791 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -9,3 +9,5 @@ dotnet msbuild Build.csproj /t:"CleanAll;MakeFDD;GatherLibrary;ComposeDistributi ```sh dotnet oscript.dll tests/testrunner.os -runAll tests ``` + +ВСЕГДА проверяй изменения перед коммитом. Не коммить бинарные файлы (exe, ospx, и другие) если тебя об этом явно не попросили. From 8c9206ea2fb88547c23a3180b4d5f7160e009911 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 17:09:17 +0100 Subject: [PATCH 28/52] Use TrimEnd('\0') instead of Replace("\0", "") for null character stripping (#1650) * Initial plan * Initial plan for fixing null character handling Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Fix: Use TrimEnd('\0') instead of Replace("\0", "") for safer null char handling Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Complete PR with all checks passed Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Remove accidentally committed nuget.exe Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> * Remove opm.ospx - should not be in repository Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --- src/OneScript.StandardLibrary/PathHelper.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/OneScript.StandardLibrary/PathHelper.cs b/src/OneScript.StandardLibrary/PathHelper.cs index a7fd9d3c2..444b3cbf7 100644 --- a/src/OneScript.StandardLibrary/PathHelper.cs +++ b/src/OneScript.StandardLibrary/PathHelper.cs @@ -13,18 +13,20 @@ namespace OneScript.StandardLibrary internal static class PathHelper { /// - /// Strips null characters from a path string. - /// This is needed because Windows WebDAV client can add null characters to paths, + /// Strips trailing null characters from a path string. + /// This is needed because Windows WebDAV client can add null characters to the end of paths, /// which causes ArgumentException in System.IO methods. + /// Only trailing null characters are removed to avoid masking potential security issues + /// with null characters in the middle of paths (e.g., "file.txt\0.exe"). /// - /// Path that may contain null characters - /// Path with null characters removed, or null if input was null + /// Path that may contain trailing null characters + /// Path with trailing null characters removed, or null if input was null public static string StripNullCharacters(string path) { if (path == null) return null; - return path.Replace("\0", ""); + return path.TrimEnd('\0'); } } } From b16a0dcd37b3ad4b0d927d35d20bb95bb7edec9c Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Fri, 23 Jan 2026 22:37:29 +0400 Subject: [PATCH 29/52] =?UTF-8?q?=D0=BF=D1=80=D0=B8=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D1=82=D0=B5=D1=82=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20?= =?UTF-8?q?=D1=80=D0=B5=D0=BA=D1=83=D1=80=D1=81=D0=B8=D0=B2=D0=BD=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=81=D0=BF=D1=83=D1=81=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Language/LanguageDef.cs | 26 ++- .../SyntaxAnalysis/DefaultBslParser.cs | 197 ++++++------------ 2 files changed, 90 insertions(+), 133 deletions(-) diff --git a/src/OneScript.Language/LanguageDef.cs b/src/OneScript.Language/LanguageDef.cs index 954101361..b32456fbd 100644 --- a/src/OneScript.Language/LanguageDef.cs +++ b/src/OneScript.Language/LanguageDef.cs @@ -282,7 +282,31 @@ public static Token GetToken(string tokText) public static int GetPriority(Token op) { return _priority[op]; - } + } + + public static int GetBinaryPriority(Token op) + { + return IsBinaryOperator(op) ? _priority[op] : -1; + } + + public static int GetUnaryPriority(Token op) + { + return op switch + { + Token.OpenPar => MAX_OPERATION_PRIORITY + 1, + + Token.Not => _priority[op], + Token.UnaryMinus => _priority[op], + Token.UnaryPlus => _priority[op], + + Token.Minus => _priority[Token.UnaryMinus], + Token.Plus => _priority[Token.UnaryPlus], + + _ => MAX_OPERATION_PRIORITY + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsBuiltInFunction(Token token) diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 1d1a543c9..70e0bf785 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -389,8 +389,8 @@ private void BuildMethodBody() finally { PopContext(); - } - + } + CreateChild(CurrentParent, NodeKind.BlockEnd, _lastExtractedLexem); NextLexem(); } @@ -1237,130 +1237,68 @@ private BslSyntaxNode BuildExpression(NonTerminalNode parent, Token stopToken) return default; } - var op = BuildOrExpression(); + var op = BuildExpression(0); parent.AddChild(op); return op; } - - private BslSyntaxNode BuildOrExpression() - { - var firstArg = BuildAndExpression(); - while (_lastExtractedLexem.Token == Token.Or) - { - var operationLexem = _lastExtractedLexem; - NextLexem(); - var secondArg = BuildAndExpression(); - firstArg = MakeBinaryOperationNode(firstArg, secondArg, operationLexem); - } - return firstArg; - } - - private BslSyntaxNode BuildAndExpression() + private BslSyntaxNode BuildExpression(int prio) { - var firstArg = BuildNotExpression(); - while (_lastExtractedLexem.Token == Token.And) + var firstArg = BuildPrimaryExpression(); + while (LanguageDef.GetBinaryPriority(_lastExtractedLexem.Token) > prio) { var operationLexem = _lastExtractedLexem; NextLexem(); - var secondArg = BuildNotExpression(); - firstArg = MakeBinaryOperationNode(firstArg, secondArg, operationLexem); - } + var secondArg = BuildExpression(LanguageDef.GetBinaryPriority(operationLexem.Token)); - return firstArg; - } - - private BslSyntaxNode BuildNotExpression() - { - if (_lastExtractedLexem.Token == Token.Not) - { - var operation = _lastExtractedLexem; - NextLexem(); - var op = new UnaryOperationNode(operation); - var argument = BuildLogicalComparison(); - op.AddChild(argument); - return op; - } - - return BuildLogicalComparison(); - } - - private BslSyntaxNode BuildLogicalComparison() - { - var firstArg = BuildAdditionExpression(); - while (_lastExtractedLexem.Token == Token.Equal || - _lastExtractedLexem.Token == Token.MoreThan || - _lastExtractedLexem.Token == Token.LessThan || - _lastExtractedLexem.Token == Token.MoreOrEqual || - _lastExtractedLexem.Token == Token.LessOrEqual || - _lastExtractedLexem.Token == Token.NotEqual) - { - var operationLexem = _lastExtractedLexem; - NextLexem(); - var secondArg = BuildAdditionExpression(); firstArg = MakeBinaryOperationNode(firstArg, secondArg, operationLexem); } return firstArg; - } - - private BslSyntaxNode BuildAdditionExpression() + } + + private BslSyntaxNode BuildPrimaryExpression() { - var firstArg = BuildMultiplyExpression(); - while (_lastExtractedLexem.Token == Token.Plus || - _lastExtractedLexem.Token == Token.Minus) - { - var operationLexem = _lastExtractedLexem; - NextLexem(); - var secondArg = BuildMultiplyExpression(); - firstArg = MakeBinaryOperationNode(firstArg, secondArg, operationLexem); - } - - return firstArg; - } - - private BslSyntaxNode BuildMultiplyExpression() - { - var firstArg = BuildUnaryMathExpression(); - while (_lastExtractedLexem.Token == Token.Multiply || - _lastExtractedLexem.Token == Token.Division || - _lastExtractedLexem.Token == Token.Modulo) - { - var operationLexem = _lastExtractedLexem; - NextLexem(); - var secondArg = BuildUnaryMathExpression(); - firstArg = MakeBinaryOperationNode(firstArg, secondArg, operationLexem); - } - - return firstArg; - } - - private BslSyntaxNode BuildUnaryMathExpression() - { - if (_lastExtractedLexem.Token == Token.Plus) - _lastExtractedLexem.Token = Token.UnaryPlus; - else if (_lastExtractedLexem.Token == Token.Minus) - _lastExtractedLexem.Token = Token.UnaryMinus; - else - return BuildParenthesis(); - - // Можно оптимизировать численный литерал до константы - var operation = _lastExtractedLexem; - NextLexem(); - if (_lastExtractedLexem.Type == LexemType.NumberLiteral) - { - if (operation.Token == Token.UnaryMinus) - _lastExtractedLexem.Content = '-' + _lastExtractedLexem.Content; - - return TerminalNode(); - } - - var op = new UnaryOperationNode(operation); - var argument = BuildParenthesis(); - op.AddChild(argument); - return op; - } - + if (_lastExtractedLexem.Token == Token.OpenPar) + { + return BuildParenthesis(); + } + + var operation = _lastExtractedLexem; + var prio = LanguageDef.GetUnaryPriority(operation.Token); + + if (prio == LanguageDef.MAX_OPERATION_PRIORITY) + { + return TerminalNode(); + } + + NextLexem(); + + if (operation.Token == Token.Plus) + operation.Token = Token.UnaryPlus; + else if (operation.Token == Token.Minus) + { + operation.Token = Token.UnaryMinus; + if (_lastExtractedLexem.Type == LexemType.NumberLiteral) //TODO:move it to lexer + { + _lastExtractedLexem.Content = '-' + _lastExtractedLexem.Content; + return TerminalNode(); + } + } + + if (LanguageDef.GetUnaryPriority(_lastExtractedLexem.Token) <= prio) + { + AddError(LocalizedErrors.ExpressionSyntax()); + return default; + } + + var arg = BuildExpression(prio); + var op = new UnaryOperationNode(operation); + op.AddChild(arg); + return op; + } + + private BslSyntaxNode BuildExpressionUpTo(NonTerminalNode parent, Token stopToken) { var node = BuildExpression(parent, stopToken); @@ -1392,36 +1330,31 @@ private void BuildOptionalExpression(NonTerminalNode parent, Token stopToken) return; } - var op = BuildOrExpression(); + var op = BuildExpression(0); parent.AddChild(op); } #region Operators - private static BslSyntaxNode MakeBinaryOperationNode(BslSyntaxNode firstArg, BslSyntaxNode secondArg, in Lexem lexem) + private static BinaryOperationNode MakeBinaryOperationNode(BslSyntaxNode firstArg, BslSyntaxNode secondArg, in Lexem lexem) { var node = new BinaryOperationNode(lexem); node.AddChild(firstArg); node.AddChild(secondArg); return node; - } - + } + private BslSyntaxNode BuildParenthesis() - { - if (_lastExtractedLexem.Token == Token.OpenPar) - { - NextLexem(); - var expr = BuildOrExpression(); - if (_lastExtractedLexem.Token != Token.ClosePar) - { - AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); - } - NextLexem(); - - return BuildDereference(expr); - } - - return TerminalNode(); + { + NextLexem(); + var expr = BuildExpression(0); + if (_lastExtractedLexem.Token != Token.ClosePar) + { + AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); + } + NextLexem(); + + return BuildDereference(expr); } #endregion @@ -1676,7 +1609,7 @@ private Token[] PopStructureToken() return tok; } - private BslSyntaxNode CreateChild(NonTerminalNode parent, NodeKind kind, in Lexem lex) + private static BslSyntaxNode CreateChild(NonTerminalNode parent, NodeKind kind, in Lexem lex) { var child = NodeBuilder.CreateNode(kind, lex); parent.AddChild(child); From c3ee3e3cbb921d7097ce3d889d2eba4eb817e756 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Fri, 23 Jan 2026 23:22:39 +0400 Subject: [PATCH 30/52] =?UTF-8?q?=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B9?= =?UTF-8?q?=20=D0=B8=20=D1=82=D0=B5=D1=80=D0=BC=D0=B8=D0=BD=D0=B0=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=81=D1=82=D1=80=D1=83=D0=BA?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AstNodes/BinaryOperationNode.cs | 8 +++++++ .../AstNodes/UnaryOperationNode.cs | 8 ++++++- .../SyntaxAnalysis/DefaultBslParser.cs | 24 +++++-------------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/BinaryOperationNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/BinaryOperationNode.cs index f4cebbfbf..2582f31f0 100644 --- a/src/OneScript.Language/SyntaxAnalysis/AstNodes/BinaryOperationNode.cs +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/BinaryOperationNode.cs @@ -17,5 +17,13 @@ public BinaryOperationNode(Lexem operation) : base(NodeKind.BinaryOperation, ope { Operation = operation.Token; } + + public BinaryOperationNode(BslSyntaxNode firstArg, BslSyntaxNode secondArg, Lexem operation) + : base(NodeKind.BinaryOperation, operation) + { + Operation = operation.Token; + AddChild(firstArg); + AddChild(secondArg); + } } } \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/UnaryOperationNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/UnaryOperationNode.cs index 05e9b80cd..d55774cf1 100644 --- a/src/OneScript.Language/SyntaxAnalysis/AstNodes/UnaryOperationNode.cs +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/UnaryOperationNode.cs @@ -16,6 +16,12 @@ public class UnaryOperationNode : NonTerminalNode public UnaryOperationNode(Lexem operation) : base(NodeKind.UnaryOperation, operation) { Operation = operation.Token; - } + } + + public UnaryOperationNode(BslSyntaxNode arg, Lexem operation) : base(NodeKind.UnaryOperation, operation) + { + Operation = operation.Token; + AddChild(arg); + } } } \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 70e0bf785..4b776bba6 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -757,9 +757,7 @@ private BslSyntaxNode BuildExpressionAwaitOperator(Lexem lexem) if (argument != default) { CheckAsyncMethod(); - var awaitOperator = new UnaryOperationNode(lexem); - awaitOperator.AddChild(argument); - return awaitOperator; + return new UnaryOperationNode(argument, lexem); } else if (!_isInAsyncMethod) { @@ -1140,7 +1138,7 @@ private BslSyntaxNode BuildGlobalCall(Lexem identifier) private BslSyntaxNode CallOrVariable(Lexem identifier) { - var target = NodeBuilder.CreateNode(NodeKind.Identifier, identifier); + BslSyntaxNode target = new TerminalNode(NodeKind.Identifier, identifier); if (_lastExtractedLexem.Token != Token.OpenPar) { _lastDereferenceIsWritable = true; // одиночный идентификатор @@ -1251,7 +1249,7 @@ private BslSyntaxNode BuildExpression(int prio) NextLexem(); var secondArg = BuildExpression(LanguageDef.GetBinaryPriority(operationLexem.Token)); - firstArg = MakeBinaryOperationNode(firstArg, secondArg, operationLexem); + firstArg = new BinaryOperationNode(firstArg, secondArg, operationLexem); } return firstArg; @@ -1293,9 +1291,7 @@ private BslSyntaxNode BuildPrimaryExpression() } var arg = BuildExpression(prio); - var op = new UnaryOperationNode(operation); - op.AddChild(arg); - return op; + return new UnaryOperationNode(arg, operation); } @@ -1336,14 +1332,6 @@ private void BuildOptionalExpression(NonTerminalNode parent, Token stopToken) #region Operators - private static BinaryOperationNode MakeBinaryOperationNode(BslSyntaxNode firstArg, BslSyntaxNode secondArg, in Lexem lexem) - { - var node = new BinaryOperationNode(lexem); - node.AddChild(firstArg); - node.AddChild(secondArg); - return node; - } - private BslSyntaxNode BuildParenthesis() { NextLexem(); @@ -1375,7 +1363,7 @@ private BslSyntaxNode SelectTerminalNode(in Lexem currentLexem, bool supportAwai BslSyntaxNode node = default; if (LanguageDef.IsLiteral(currentLexem)) { - node = NodeBuilder.CreateNode(NodeKind.Constant, currentLexem); + node = new TerminalNode(NodeKind.Constant, currentLexem); NextLexem(); } else if (LanguageDef.IsUserSymbol(currentLexem)) @@ -1449,7 +1437,7 @@ private BslSyntaxNode BuildDereference(BslSyntaxNode target) NextLexem(); if (_lastExtractedLexem.Token == Token.OpenPar) { - var ident = NodeBuilder.CreateNode(NodeKind.Identifier, identifier); + var ident = new TerminalNode(NodeKind.Identifier, identifier); var call = BuildCall(ident, NodeKind.MethodCall); dotNode.AddChild(call); } From 39da39647f7c8355bc386ea8b1a996f7cfe8a2d5 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sat, 24 Jan 2026 13:35:56 +0400 Subject: [PATCH 31/52] =?UTF-8?q?=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3:=20=D1=80=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=B2=D1=8B=D1=85=D0=BE=D0=B4,=20=D0=BB=D0=B8?= =?UTF-8?q?=D1=88=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=B8=20=D0=B5=D1=89=D0=B5=20=D0=BC?= =?UTF-8?q?=D0=B5=D0=BB=D0=BE=D1=87=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SyntaxAnalysis/DefaultBslParser.cs | 125 ++++++++---------- 1 file changed, 58 insertions(+), 67 deletions(-) diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 4b776bba6..1d76af27b 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -212,60 +212,55 @@ private void BuildVariableDefinition() NextLexem(); - if (IsUserSymbol(_lastExtractedLexem)) - { - if (_inMethodScope) - { - if (_isStatementsDefined) - { - AddError(LocalizedErrors.LateVarDefinition()); - return; - } - } - else - { - if (_isMethodsDefined) - { - AddError(LocalizedErrors.LateVarDefinition()); - return; - } - } - - var symbolicName = _lastExtractedLexem.Content; - CreateChild(variable, NodeKind.Identifier, _lastExtractedLexem); - - NextLexem(); - if (_lastExtractedLexem.Token == Token.Export) - { - if (_inMethodScope) - { - AddError(LocalizedErrors.ExportedLocalVar(symbolicName)); - break; - } - CreateChild(variable, NodeKind.ExportFlag, _lastExtractedLexem); - NextLexem(); - } - - if (_lastExtractedLexem.Token == Token.Comma) - { - continue; - } - - if (_lastExtractedLexem.Token == Token.Semicolon) - { - NextLexem(); - } - else - { - AddError(LocalizedErrors.SemicolonExpected()); - } - - } - else - { - AddError(LocalizedErrors.IdentifierExpected()); - } - + if (!IsUserSymbol(_lastExtractedLexem)) + { + AddError(LocalizedErrors.IdentifierExpected()); + return; + } + + if (_inMethodScope) + { + if (_isStatementsDefined) + { + AddError(LocalizedErrors.LateVarDefinition()); + return; + } + } + else if (_isMethodsDefined) + { + AddError(LocalizedErrors.LateVarDefinition()); + return; + } + + var symbolicName = _lastExtractedLexem.Content; + CreateChild(variable, NodeKind.Identifier, _lastExtractedLexem); + + NextLexem(); + if (_lastExtractedLexem.Token == Token.Export) + { + if (_inMethodScope) + { + AddError(LocalizedErrors.ExportedLocalVar(symbolicName)); + return; + } + CreateChild(variable, NodeKind.ExportFlag, _lastExtractedLexem); + NextLexem(); + } + + if (_lastExtractedLexem.Token == Token.Comma) + { + continue; + } + + if (_lastExtractedLexem.Token == Token.Semicolon) + { + NextLexem(); + } + else + { + AddError(LocalizedErrors.SemicolonExpected()); + } + break; } } @@ -286,9 +281,7 @@ private void ApplyAnnotations(AnnotatableNode annotatable) private void BuildMethodsSection() { if (_lastExtractedLexem.Type != LexemType.Annotation - && _lastExtractedLexem.Token != Token.Procedure - && _lastExtractedLexem.Token != Token.Function - && _lastExtractedLexem.Token != Token.Async) + && !IsStartOfMethod(_lastExtractedLexem)) { return; } @@ -487,12 +480,11 @@ private bool BuildDefaultParameterValue(NonTerminalNode param, NodeKind nodeKind if (LanguageDef.IsLiteral(_lastExtractedLexem)) { - string literalText = _lastExtractedLexem.Content; if (hasSign) { if (_lastExtractedLexem.Type == LexemType.NumberLiteral && signIsMinus) { - literalText = '-' + literalText; + _lastExtractedLexem.Content = '-' + _lastExtractedLexem.Content; } else if (_lastExtractedLexem.Type == LexemType.StringLiteral || _lastExtractedLexem.Type == LexemType.DateLiteral) @@ -502,7 +494,6 @@ private bool BuildDefaultParameterValue(NonTerminalNode param, NodeKind nodeKind } } - _lastExtractedLexem.Content = literalText; CreateChild(param, nodeKind, _lastExtractedLexem); NextLexem(); } @@ -861,8 +852,7 @@ private void BuildWhileStatement() } private void BuildForStatement() - { - var lexem = _lastExtractedLexem; + { NextLexem(); NodeKind loopKind; @@ -902,8 +892,8 @@ private void BuildCountableForStatement(NonTerminalNode loopNode) AddError(LocalizedErrors.IdentifierExpected()); BuildBatchWithContext(loopNode, Token.EndLoop); return; - } - + } + var counter = _lastExtractedLexem; if (!NextExpected(Token.Equal)) { @@ -1073,7 +1063,8 @@ private void BuildEventHandlerOperation(Token token) if (source == null) return; - if ((source.Kind != NodeKind.DereferenceOperation || !_lastDereferenceIsWritable) && source.Kind != NodeKind.IndexAccess) + if ((source.Kind != NodeKind.DereferenceOperation || !_lastDereferenceIsWritable) + && source.Kind != NodeKind.IndexAccess) { AddError(LocalizedErrors.WrongEventName()); return; @@ -1151,7 +1142,7 @@ private BslSyntaxNode CallOrVariable(Lexem identifier) return BuildDereference(target); } - private BslSyntaxNode BuildCall(BslSyntaxNode target, NodeKind callKind) + private CallNode BuildCall(BslSyntaxNode target, NodeKind callKind) { var callNode = new CallNode(callKind, _lastExtractedLexem); callNode.AddChild(target); @@ -1200,7 +1191,7 @@ private void BuildCallArgument(NonTerminalNode argsList) if (_lastExtractedLexem.Token == Token.Comma) { CreateChild(argsList, NodeKind.CallArgument, _lastExtractedLexem); - + BuildLastDefaultArg(argsList); } else if (_lastExtractedLexem.Token != Token.ClosePar) From 269a431ae74ee5f82fd7410c247e09a76c3d9198 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sat, 24 Jan 2026 21:02:48 +0400 Subject: [PATCH 32/52] =?UTF-8?q?=D1=83=D0=BD=D0=B8=D1=84=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D1=83=D0=B7=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SyntaxAnalysis/AstNodes/MethodNode.cs | 2 +- .../AstNodes/MethodSignatureNode.cs | 2 +- .../SyntaxAnalysis/AstNodes/TerminalNode.cs | 4 +-- .../SyntaxAnalysis/DefaultBslParser.cs | 30 +++++++++---------- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodNode.cs index 07aa269a3..7cea61576 100644 --- a/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodNode.cs +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodNode.cs @@ -30,7 +30,7 @@ public MethodNode() : base(NodeKind.Method) public IReadOnlyList VariableDefinitions() { if (VariableSection == default) - return new VariableDefinitionNode[0]; + return System.Array.Empty(); return VariableSection.Children .Cast() diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodSignatureNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodSignatureNode.cs index 1272785b4..cc245493d 100644 --- a/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodSignatureNode.cs +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodSignatureNode.cs @@ -28,7 +28,7 @@ public IEnumerable GetParameters() { var paramList = Children.FirstOrDefault(x => x.Kind == NodeKind.MethodParameters); if (paramList == default) - return new MethodParameterNode[0]; + return System.Array.Empty(); return ((NonTerminalNode) paramList).Children.Cast(); } diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/TerminalNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/TerminalNode.cs index a57fbe4cc..09fe2996f 100644 --- a/src/OneScript.Language/SyntaxAnalysis/AstNodes/TerminalNode.cs +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/TerminalNode.cs @@ -26,8 +26,6 @@ public TerminalNode(NodeKind kind, Lexem lexem) Location = lexem.Location; } - public override IReadOnlyList Children => EmptyChildren; - - private static readonly BslSyntaxNode[] EmptyChildren = new BslSyntaxNode[0]; + public override IReadOnlyList Children => System.Array.Empty(); } } \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 1d76af27b..803ad4e9f 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -373,8 +373,8 @@ private void BuildMethodVariablesSection() private void BuildMethodBody() { - var body = CreateChild(CurrentParent, NodeKind.CodeBatch, _lastExtractedLexem); - PushContext((NonTerminalNode)body); + var body = _nodeContext.AddChild(new CodeBatchNode(_lastExtractedLexem)); + PushContext(body); try { BuildCodeBatch(_isInFunctionScope ? Token.EndFunction : Token.EndProcedure); @@ -652,7 +652,7 @@ private void BuildCodeBatch(params Token[] endTokens) private void DefineLabel(Lexem label) { var node = new LabelNode(label); - _nodeContext.AddChild(node); + CurrentParent.AddChild(node); NextLexem(); } @@ -732,9 +732,9 @@ private void BuildComplexStructureStatement() private void BuildGlobalCallAwaitOperator() { - Debug.Assert(_lastExtractedLexem.Token == Token.Await); - - _nodeContext.AddChild(TerminalNode()); + Debug.Assert(_lastExtractedLexem.Token == Token.Await); + + CurrentParent.AddChild(TerminalNode()); } @@ -775,7 +775,7 @@ private void BuildGotoOperator() gotoNode.AddChild(new LabelNode(_lastExtractedLexem)); NextLexem(); - _nodeContext.AddChild(gotoNode); + CurrentParent.AddChild(gotoNode); } private void CheckAsyncMethod() @@ -834,8 +834,9 @@ private void BuildWhileStatement() var loopNode = _nodeContext.AddChild(new WhileLoopNode(_lastExtractedLexem)); NextLexem(); BuildExpressionUpTo(loopNode, Token.Loop); - var body = CreateChild(loopNode, NodeKind.CodeBatch, _lastExtractedLexem); - PushContext((NonTerminalNode)body); + var body = loopNode.AddNode(new CodeBatchNode(_lastExtractedLexem)); + + PushContext(body); var loopState = _isInLoopScope; try { @@ -912,8 +913,8 @@ private void BuildCountableForStatement(NonTerminalNode loopNode) var limit = new NonTerminalNode(NodeKind.ForLimit, _lastExtractedLexem); BuildExpressionUpTo(limit, Token.Loop); - loopNode.AddChild(limit); - + loopNode.AddChild(limit); + BuildBatchWithContext(loopNode, Token.EndLoop); CreateChild(loopNode, NodeKind.BlockEnd, _lastExtractedLexem); @@ -1190,7 +1191,7 @@ private void BuildCallArgument(NonTerminalNode argsList) { if (_lastExtractedLexem.Token == Token.Comma) { - CreateChild(argsList, NodeKind.CallArgument, _lastExtractedLexem); + argsList.AddChild(new NonTerminalNode(NodeKind.CallArgument, _lastExtractedLexem)); BuildLastDefaultArg(argsList); } @@ -1210,7 +1211,7 @@ private void BuildLastDefaultArg(NonTerminalNode argsList) NextLexem(); if (_lastExtractedLexem.Token == Token.ClosePar) { - CreateChild(argsList, NodeKind.CallArgument, _lastExtractedLexem); + argsList.AddChild(new NonTerminalNode(NodeKind.CallArgument, _lastExtractedLexem)); } } @@ -1588,11 +1589,10 @@ private Token[] PopStructureToken() return tok; } - private static BslSyntaxNode CreateChild(NonTerminalNode parent, NodeKind kind, in Lexem lex) + private static void CreateChild(NonTerminalNode parent, NodeKind kind, in Lexem lex) { var child = NodeBuilder.CreateNode(kind, lex); parent.AddChild(child); - return child; } private bool TryParseNode(Action action) From 2f378fec3a9b13026b3c026826e84652db8013a5 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sun, 25 Jan 2026 00:14:35 +0400 Subject: [PATCH 33/52] =?UTF-8?q?=D0=BF=D1=83=D1=81=D1=82=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BC=D0=B0=D1=81=D1=81=D0=B8=D0=B2=D1=8B=20=D0=B8=20=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D0=B8=D0=B9=20=D0=B2=D1=8B=D1=85=D0=BE=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SyntaxAnalysis/AstNodes/ConditionNode.cs | 2 +- .../SyntaxAnalysis/AstNodes/LineMarkerNode.cs | 2 +- .../SyntaxAnalysis/DefaultBslParser.cs | 79 ++++++++----------- 3 files changed, 36 insertions(+), 47 deletions(-) diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/ConditionNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ConditionNode.cs index 7e44d8112..86a41d118 100644 --- a/src/OneScript.Language/SyntaxAnalysis/AstNodes/ConditionNode.cs +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ConditionNode.cs @@ -26,7 +26,7 @@ public ConditionNode(Lexem startLexem) : base(NodeKind.Condition, startLexem) public IEnumerable GetAlternatives() { if(_alternativesStart == 0) - return new BslSyntaxNode[0]; + return System.Array.Empty(); return Children .Skip(_alternativesStart) diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/LineMarkerNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/LineMarkerNode.cs index d67c85d4e..3399bc17f 100644 --- a/src/OneScript.Language/SyntaxAnalysis/AstNodes/LineMarkerNode.cs +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/LineMarkerNode.cs @@ -18,6 +18,6 @@ public LineMarkerNode(CodeRange location, NodeKind kind) Kind = kind; } - public override IReadOnlyList Children => new BslSyntaxNode[0]; + public override IReadOnlyList Children => System.Array.Empty(); } } \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 803ad4e9f..6191f470f 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -49,20 +49,20 @@ public DefaultBslParser( private IErrorSink ErrorSink { get; } - public IEnumerable Errors => ErrorSink.Errors ?? new CodeError[0]; + public IEnumerable Errors => ErrorSink.Errors ?? Array.Empty(); public BslSyntaxNode ParseStatefulModule() { ModuleNode node; _preprocessorHandlers.OnModuleEnter(); - NextLexem(); - + NextLexem(); + + node = new ModuleNode(_lexer.Iterator.Source, _lastExtractedLexem); + PushContext(node); try { - node = new ModuleNode(_lexer.Iterator.Source, _lastExtractedLexem); - PushContext(node); - ParseModuleSections(); + ParseModuleSections(); } finally { @@ -81,11 +81,9 @@ public BslSyntaxNode ParseCodeBatch(bool allowReturns = false) PushContext(node); try { - if (allowReturns) - { - _inMethodScope = true; - _isInFunctionScope = true; - } + _inMethodScope = allowReturns; + _isInFunctionScope = allowReturns; + BuildModuleBody(); } finally @@ -180,20 +178,16 @@ private void BuildVariableSection() while (true) { BuildAnnotations(); - if (_lastExtractedLexem.Token == Token.VarDef) - { - if (!hasVars) - { - hasVars = true; - parent.AddChild(allVarsSection); - } - - BuildVariableDefinition(); - } - else - { - break; - } + if (_lastExtractedLexem.Token != Token.VarDef) + break; + + if (!hasVars) + { + hasVars = true; + parent.AddChild(allVarsSection); + } + + BuildVariableDefinition(); } } finally @@ -296,21 +290,17 @@ private void BuildMethodsSection() while (true) { BuildAnnotations(); - if (IsStartOfMethod(_lastExtractedLexem)) - { - if (!sectionExist) - { - sectionExist = true; - _isMethodsDefined = true; - parent.AddChild(allMethodsSection); - } - - BuildMethod(); - } - else - { - break; - } + if (!IsStartOfMethod(_lastExtractedLexem)) + break; + + if (!sectionExist) + { + sectionExist = true; + _isMethodsDefined = true; + parent.AddChild(allMethodsSection); + } + + BuildMethod(); } } finally @@ -1160,7 +1150,6 @@ private void BuildCallParameters(NonTerminalNode callNode) { NextLexem(); // съели открывающую скобку WalkCallArguments(node); - NextLexem(); // съели закрывающую скобку } finally @@ -1366,14 +1355,14 @@ private BslSyntaxNode SelectTerminalNode(in Lexem currentLexem, bool supportAwai { node = BuildNewObjectCreation(); } - else if (currentLexem.Token == Token.Question) - { - node = BuildQuestionOperator(); - } else if (LanguageDef.IsBuiltInFunction(currentLexem.Token)) { node = BuildGlobalCall(currentLexem); } + else if (currentLexem.Token == Token.Question) + { + node = BuildQuestionOperator(); + } else if (supportAwait && currentLexem.Token == Token.Await) { node = BuildExpressionAwaitOperator(currentLexem); From 9ae791cf3bc532c53ec69b9c820912be75cd9b4c Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Sun, 25 Jan 2026 02:01:47 +0400 Subject: [PATCH 34/52] =?UTF-8?q?fix:=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=BA=D0=B0=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D0=B8?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D0=B5=D0=B9=20=D0=B2=20=D1=83=D1=81=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=D0=BD=D0=BE=D0=BC=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SyntaxAnalysis/DefaultBslParser.cs | 22 ++++++------------- .../OneScript.Language.Tests/ParserTests.cs | 11 +++++++++- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 6191f470f..fb59f6e6a 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -1374,29 +1374,21 @@ private BslSyntaxNode SelectTerminalNode(in Lexem currentLexem, bool supportAwai private BslSyntaxNode BuildQuestionOperator() { var node = new NonTerminalNode(NodeKind.TernaryOperator, _lastExtractedLexem); - if(!NextExpected(Token.OpenPar)) - AddError(LocalizedErrors.TokenExpected(Token.OpenPar)); + if (!NextExpected(Token.OpenPar)) + AddError(LocalizedErrors.TokenExpected(Token.OpenPar)); + + NextLexem(); if (!TryParseNode(() => { - NextLexem(); - BuildExpression(node, Token.Comma); - NextLexem(); - BuildExpression(node, Token.Comma); - NextLexem(); - BuildExpression(node, Token.ClosePar); + BuildExpressionUpTo(node, Token.Comma); + BuildExpressionUpTo(node, Token.Comma); + BuildExpressionUpTo(node, Token.ClosePar); })) { return default; } - if (_lastExtractedLexem.Token != Token.ClosePar) - { - AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); - return default; - } - NextLexem(); - return BuildDereference(node); } diff --git a/src/Tests/OneScript.Language.Tests/ParserTests.cs b/src/Tests/OneScript.Language.Tests/ParserTests.cs index 23fef46f2..1d8701f64 100644 --- a/src/Tests/OneScript.Language.Tests/ParserTests.cs +++ b/src/Tests/OneScript.Language.Tests/ParserTests.cs @@ -1243,9 +1243,18 @@ Если Ложь Тогда Ф = 2 КонецЕсли"; + CatchParsingError(code); + } + + [Fact] + public void Check_Question_Operator_Delimiters() + { + var code = @"Ф = ?(Истина? 1 ; 2);"; + CatchParsingError(code); } - + + [Fact] public void TestLocalExportVar() { From 7a991610dbfff82400c98535d4daceb6c69d8ed1 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Mon, 26 Jan 2026 00:01:47 +0400 Subject: [PATCH 35/52] =?UTF-8?q?=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD=D0=B0=20de?= =?UTF-8?q?fault=20=D0=BD=D0=B0=20=D1=83=D0=B7=D0=B5=D0=BB=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SyntaxAnalysis/DefaultBslParser.cs | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index fb59f6e6a..83eaccbfc 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -747,8 +747,7 @@ private BslSyntaxNode BuildExpressionAwaitOperator(Lexem lexem) } else { - AddError(LocalizedErrors.ExpressionSyntax()); - return new ErrorTerminalNode(_lastExtractedLexem); + return CreateError(LocalizedErrors.ExpressionSyntax()); } } @@ -1051,8 +1050,6 @@ private void BuildEventHandlerOperation(Token token) NextLexem(); var source = BuildExpressionUpTo(node, Token.Comma); - if (source == null) - return; if ((source.Kind != NodeKind.DereferenceOperation || !_lastDereferenceIsWritable) && source.Kind != NodeKind.IndexAccess) @@ -1212,8 +1209,7 @@ private BslSyntaxNode BuildExpression(NonTerminalNode parent, Token stopToken) { if (_lastExtractedLexem.Token == stopToken) { - AddError(LocalizedErrors.ExpressionExpected()); - return default; + return CreateError(LocalizedErrors.ExpressionExpected()); } var op = BuildExpression(0); @@ -1267,8 +1263,7 @@ private BslSyntaxNode BuildPrimaryExpression() if (LanguageDef.GetUnaryPriority(_lastExtractedLexem.Token) <= prio) { - AddError(LocalizedErrors.ExpressionSyntax()); - return default; + return CreateError(LocalizedErrors.ExpressionSyntax()); } var arg = BuildExpression(prio); @@ -1287,14 +1282,12 @@ private BslSyntaxNode BuildExpressionUpTo(NonTerminalNode parent, Token stopToke { if (_lastExtractedLexem.Token == Token.EndOfText) { - AddError(LocalizedErrors.UnexpectedEof()); + return CreateError(LocalizedErrors.UnexpectedEof()); } else { - AddError(LocalizedErrors.TokenExpected(stopToken), false); + return CreateError(LocalizedErrors.TokenExpected(stopToken), false); } - - node = default; } return node; @@ -1333,9 +1326,9 @@ private BslSyntaxNode TerminalNode() BslSyntaxNode node = SelectTerminalNode(_lastExtractedLexem, true); if (node == default) { - AddError(LocalizedErrors.ExpressionSyntax()); + return CreateError(LocalizedErrors.ExpressionSyntax()); } - + return node; } @@ -1351,7 +1344,7 @@ private BslSyntaxNode SelectTerminalNode(in Lexem currentLexem, bool supportAwai { node = BuildGlobalCall(currentLexem); } - else if(currentLexem.Token == Token.NewObject) + else if (currentLexem.Token == Token.NewObject) { node = BuildNewObjectCreation(); } @@ -1367,7 +1360,7 @@ private BslSyntaxNode SelectTerminalNode(in Lexem currentLexem, bool supportAwai { node = BuildExpressionAwaitOperator(currentLexem); } - + return node; } @@ -1375,7 +1368,7 @@ private BslSyntaxNode BuildQuestionOperator() { var node = new NonTerminalNode(NodeKind.TernaryOperator, _lastExtractedLexem); if (!NextExpected(Token.OpenPar)) - AddError(LocalizedErrors.TokenExpected(Token.OpenPar)); + return CreateError(LocalizedErrors.TokenExpected(Token.OpenPar)); NextLexem(); @@ -1386,7 +1379,7 @@ private BslSyntaxNode BuildQuestionOperator() BuildExpressionUpTo(node, Token.ClosePar); })) { - return default; + return CreateError(LocalizedErrors.ExpressionSyntax()); } return BuildDereference(node); @@ -1402,8 +1395,7 @@ private BslSyntaxNode BuildDereference(BslSyntaxNode target) NextLexem(); if (!LanguageDef.IsValidPropertyName(_lastExtractedLexem)) { - AddError(LocalizedErrors.IdentifierExpected()); - return default; + return CreateError(LocalizedErrors.IdentifierExpected()); } var identifier = _lastExtractedLexem; @@ -1434,10 +1426,9 @@ private BslSyntaxNode BuildIndexerAccess(BslSyntaxNode target) node.AddChild(target); NextLexem(); var expression = BuildExpression(node, Token.CloseBracket); - if (expression == default) + if (expression.Kind == NodeKind.Unknown) { - AddError(LocalizedErrors.ExpressionSyntax()); - return default; + return CreateError(LocalizedErrors.ExpressionSyntax()); } NextLexem(); _lastDereferenceIsWritable = true; @@ -1462,8 +1453,7 @@ private BslSyntaxNode BuildNewObjectCreation() } else { - AddError(LocalizedErrors.IdentifierExpected()); - node = default; + return CreateError(LocalizedErrors.IdentifierExpected()); } return BuildDereference(node); @@ -1554,6 +1544,13 @@ private void AddError(CodeError err, bool doFastForward = true) throw new InternalParseException(err); } + private ErrorTerminalNode CreateError(CodeError error, bool doFastForward = true) + { + var lexem = _lastExtractedLexem; + AddError(error, doFastForward); + return new ErrorTerminalNode(lexem); + } + private bool IsUserSymbol(in Lexem lex) { return LanguageDef.IsUserSymbol(in lex) || (!_isInAsyncMethod && lex.Token == Token.Await); From cf3aff3eff1107205fd52bbff3d0ddb9b1650723 Mon Sep 17 00:00:00 2001 From: Andrey Ovsiankin Date: Mon, 26 Jan 2026 19:42:45 +0300 Subject: [PATCH 36/52] =?UTF-8?q?closes=20#1643=20=D0=97=D0=B0=D0=B3=D1=80?= =?UTF-8?q?=D1=83=D0=B7=D1=87=D0=B8=D0=BA=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B8?= =?UTF-8?q?=D0=BE=D1=82=D0=B5=D0=BA=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D1=8F=D0=B5=D1=82=20?= =?UTF-8?q?=D1=81=D0=B8=D1=82=D1=83=D0=B0=D1=86=D0=B8=D1=8E=20=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=D0=B9=D0=B4=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B1?= =?UTF-8?q?=D0=B8=D0=B1=D0=BB=D0=B8=D0=BE=D1=82=D0=B5=D0=BA=20=D0=B8=20?= =?UTF-8?q?=D1=82=D0=B5=D1=85,=20=D0=BA=D0=BE=D1=82=D0=BE=D1=80=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=BD=D0=B5=20=D1=83=D0=B4=D0=B0=D0=B5=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FileSystemDependencyResolver.cs | 62 +++++++++++++++---- .../LibraryLoader.cs | 4 +- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/ScriptEngine.HostedScript/FileSystemDependencyResolver.cs b/src/ScriptEngine.HostedScript/FileSystemDependencyResolver.cs index abf504ca8..f5fd6b45b 100644 --- a/src/ScriptEngine.HostedScript/FileSystemDependencyResolver.cs +++ b/src/ScriptEngine.HostedScript/FileSystemDependencyResolver.cs @@ -42,6 +42,27 @@ private enum ProcessingState Processed } + private enum LoadStatus + { + NotFound, // Каталог библиотеки не существует + Empty, // Каталог найден, но библиотека не содержит исполняемых файлов + Success // Библиотека успешно загружена + } + + private class LoadResult + { + public PackageInfo Package { get; set; } + public LoadStatus Status { get; set; } + + public static LoadResult NotFound() => new LoadResult { Status = LoadStatus.NotFound }; + public static LoadResult Empty() => new LoadResult { Status = LoadStatus.Empty }; + public static LoadResult Success(PackageInfo package) => new LoadResult + { + Status = LoadStatus.Success, + Package = package + }; + } + #endregion public FileSystemDependencyResolver() @@ -82,17 +103,25 @@ public PackageInfo Resolve(SourceCode module, string libraryName, IBslProcess pr { bool quoted = PrepareQuoted(ref libraryName); - var lib = quoted ? + var result = quoted ? LoadByRelativePath(module, libraryName, process) : LoadByName(libraryName, process); - if (lib == null) + if (result.Status == LoadStatus.NotFound) + { throw new CompilerException($"Библиотека не найдена: '{libraryName}'"); + } + else if (result.Status == LoadStatus.Empty) + { + throw new CompilerException( + $"Библиотека '{libraryName}' найдена, но не содержит исполняемых файлов.\n" + + "Для получения подробной информации установите переменную окружения OS_LIBRARY_LOADER_TRACE=1"); + } - return lib; + return result.Package; } - private PackageInfo LoadByName(string libraryName, IBslProcess process) + private LoadResult LoadByName(string libraryName, IBslProcess process) { foreach (var path in SearchDirectories) { @@ -100,16 +129,20 @@ private PackageInfo LoadByName(string libraryName, IBslProcess process) continue; var libraryPath = Path.Combine(path, libraryName); - var loadAttempt = LoadByPath(libraryPath, process); - if (loadAttempt != null) - return loadAttempt; + var result = LoadByPath(libraryPath, process); + + // Если библиотека найдена (успешно загружена или пуста), сразу возвращаем + // Не ищем дальше, так как это более приоритетный путь + if (result.Status != LoadStatus.NotFound) + return result; } + // Если в SearchDirectories ничего не нашли, проверяем rootPath var rootPath = Path.Combine(LibraryRoot, libraryName); return LoadByPath(rootPath, process); } - private PackageInfo LoadByRelativePath(SourceCode module, string libraryPath, IBslProcess process) + private LoadResult LoadByRelativePath(SourceCode module, string libraryPath, IBslProcess process) { string realPath; @@ -197,11 +230,16 @@ private bool PrepareQuoted(ref string value) return quoted; } - private PackageInfo LoadByPath(string libraryPath, IBslProcess process) + private LoadResult LoadByPath(string libraryPath, IBslProcess process) { - return Directory.Exists(libraryPath) ? - LoadLibraryInternal(libraryPath, process) : - null; + if (!Directory.Exists(libraryPath)) + return LoadResult.NotFound(); + + var package = LoadLibraryInternal(libraryPath, process); + + return package == null + ? LoadResult.Empty() + : LoadResult.Success(package); } private PackageInfo LoadLibraryInternal(string libraryPath, IBslProcess process) diff --git a/src/ScriptEngine.HostedScript/LibraryLoader.cs b/src/ScriptEngine.HostedScript/LibraryLoader.cs index adc76a06b..7a5cea07d 100644 --- a/src/ScriptEngine.HostedScript/LibraryLoader.cs +++ b/src/ScriptEngine.HostedScript/LibraryLoader.cs @@ -279,7 +279,9 @@ private IExecutableModule CompileFile(string path, string ownerPackageId, IBslPr } private static Lazy TraceEnabled = - new Lazy(() => System.Environment.GetEnvironmentVariable("OS_LRE_TRACE") == "1"); + new Lazy(() => + System.Environment.GetEnvironmentVariable("OS_LIBRARY_LOADER_TRACE") == "1" || + System.Environment.GetEnvironmentVariable("OS_LRE_TRACE") == "1"); // для обратной совместимости public static void TraceLoadLibrary(string message) { From d571c76b2b175126718a06d7ece0cc45f8e5b4b6 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Thu, 29 Jan 2026 14:05:58 +0400 Subject: [PATCH 37/52] =?UTF-8?q?fix=20#1654:=20=D1=81=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=BE=D0=BA=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20=D1=87?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B7=20=D0=B7=D0=B0=D0=BF=D1=8F=D1=82=D1=83?= =?UTF-8?q?=D1=8E=20+=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SyntaxAnalysis/DefaultBslParser.cs | 199 +++++++++--------- .../OneScript.Language.Tests/ParserTests.cs | 20 ++ 2 files changed, 122 insertions(+), 97 deletions(-) diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 83eaccbfc..8f136cc19 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -194,6 +194,7 @@ private void BuildVariableSection() { PopContext(); } + } private void BuildVariableDefinition() @@ -413,49 +414,55 @@ private void BuildMethodParameters(MethodSignatureNode signature) NextLexem(); // ( - var expectParameter = false; - while (_lastExtractedLexem.Token != Token.ClosePar) - { - BuildAnnotations(); - var param = new MethodParameterNode(); - paramList.AddChild(param); - ApplyAnnotations(param); - // [Знач] Identifier [= Literal],... - if (_lastExtractedLexem.Token == Token.ByValParam) - { - CreateChild(param, NodeKind.ByValModifier, _lastExtractedLexem); - NextLexem(); - } - - if (!IsUserSymbol(_lastExtractedLexem)) - { - AddError(LocalizedErrors.IdentifierExpected()); - return; - } - CreateChild(param, NodeKind.Identifier, _lastExtractedLexem); - NextLexem(); - if (_lastExtractedLexem.Token == Token.Equal) - { - NextLexem(); - if(!BuildDefaultParameterValue(param, NodeKind.ParameterDefaultValue)) - return; - } - - expectParameter = false; - if (_lastExtractedLexem.Token == Token.Comma) - { - NextLexem(); - expectParameter = true; - } - } - - if (expectParameter) - { - AddError(LocalizedErrors.IdentifierExpected(), false); - } - + if (_lastExtractedLexem.Token != Token.ClosePar) + while (true) + { + BuildMethodParameter(paramList); + + if (_lastExtractedLexem.Token == Token.ClosePar) + { + break; + } + + if (_lastExtractedLexem.Token == Token.Comma) + { + NextLexem(); + } + else + { + AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); + return; + } + } + NextLexem(); // ) + } + private void BuildMethodParameter(NonTerminalNode paramList) + { + BuildAnnotations(); + var param = new MethodParameterNode(); + paramList.AddChild(param); + ApplyAnnotations(param); + // [Знач] Identifier [= Literal],... + if (_lastExtractedLexem.Token == Token.ByValParam) + { + CreateChild(param, NodeKind.ByValModifier, _lastExtractedLexem); + NextLexem(); + } + + if (!IsUserSymbol(_lastExtractedLexem)) + { + AddError(LocalizedErrors.IdentifierExpected()); + return; + } + CreateChild(param, NodeKind.Identifier, _lastExtractedLexem); + NextLexem(); + if (_lastExtractedLexem.Token == Token.Equal) + { + NextLexem(); + BuildDefaultParameterValue(param, NodeKind.ParameterDefaultValue); + } } private bool BuildDefaultParameterValue(NonTerminalNode param, NodeKind nodeKind) @@ -526,6 +533,7 @@ private void BuildAnnotations() _annotations.Add(node); } } + private AnnotationNode BuildAnnotationDefinition() { var node = new AnnotationNode(NodeKind.Annotation, _lastExtractedLexem); NextLexem(); @@ -540,23 +548,31 @@ private void BuildAnnotationParameters(AnnotationNode annotation) return; NextLexem(); - - while (_lastExtractedLexem.Token != Token.EndOfText) + + if (_lastExtractedLexem.Token != Token.ClosePar) + while (true) { - if (_lastExtractedLexem.Token == Token.ClosePar) - { - NextLexem(); - break; - } - - BuildAnnotationParameter(annotation); + BuildAnnotationParameter(annotation); + + if (_lastExtractedLexem.Token == Token.ClosePar) + { + break; + } + if (_lastExtractedLexem.Token == Token.Comma) { NextLexem(); } - } - } - + else + { + AddError(LocalizedErrors.TokenExpected(Token.ClosePar), false); + return; + } + } + + NextLexem(); // ) + } + private void BuildAnnotationParameter(AnnotationNode annotation) { bool success = true; @@ -1146,59 +1162,48 @@ private void BuildCallParameters(NonTerminalNode callNode) try { NextLexem(); // съели открывающую скобку - WalkCallArguments(node); + BuildCallArguments(node); NextLexem(); // съели закрывающую скобку } finally { PopStructureToken(); } - } - - private int WalkCallArguments(NonTerminalNode node) - { - int argCount = 0; - while (_lastExtractedLexem.Token != Token.ClosePar) - { - BuildCallArgument(node); - argCount++; - } - - if (_lastExtractedLexem.Token != Token.ClosePar) - { - AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); - argCount = -1; - } - - return argCount; - } - - private void BuildCallArgument(NonTerminalNode argsList) - { - if (_lastExtractedLexem.Token == Token.Comma) - { - argsList.AddChild(new NonTerminalNode(NodeKind.CallArgument, _lastExtractedLexem)); + } - BuildLastDefaultArg(argsList); - } - else if (_lastExtractedLexem.Token != Token.ClosePar) - { - var node = argsList.AddNode(new NonTerminalNode(NodeKind.CallArgument, _lastExtractedLexem)); - BuildOptionalExpression(node, Token.Comma); - if (_lastExtractedLexem.Token == Token.Comma) - { - BuildLastDefaultArg(argsList); - } - } + private void BuildCallArguments(NonTerminalNode node) + { + if (_lastExtractedLexem.Token != Token.ClosePar) + while (true) + { + BuildOptionalCallArgument(node); + + if (_lastExtractedLexem.Token == Token.ClosePar) + { + break; + } + + if (_lastExtractedLexem.Token == Token.Comma) + { + NextLexem(); + } + else + { + AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); + } + } } - private void BuildLastDefaultArg(NonTerminalNode argsList) - { - NextLexem(); - if (_lastExtractedLexem.Token == Token.ClosePar) + private void BuildOptionalCallArgument(NonTerminalNode argsList) + { + var arg = argsList.AddNode(new NonTerminalNode(NodeKind.CallArgument, _lastExtractedLexem)); + if (_lastExtractedLexem.Token == Token.Comma + || _lastExtractedLexem.Token == Token.ClosePar) { - argsList.AddChild(new NonTerminalNode(NodeKind.CallArgument, _lastExtractedLexem)); - } + return; + } + + arg.AddNode( BuildExpression(0) ); } #endregion @@ -1480,7 +1485,7 @@ private void NewObjectDynamicConstructor(NonTerminalNode node) // есть аргументы после имени NextLexem(); } - WalkCallArguments(callArgs); + BuildCallArguments(callArgs); node.AddChild(callArgs); NextLexem(); } diff --git a/src/Tests/OneScript.Language.Tests/ParserTests.cs b/src/Tests/OneScript.Language.Tests/ParserTests.cs index 1d8701f64..7d965101e 100644 --- a/src/Tests/OneScript.Language.Tests/ParserTests.cs +++ b/src/Tests/OneScript.Language.Tests/ParserTests.cs @@ -1251,9 +1251,29 @@ public void Check_Question_Operator_Delimiters() { var code = @"Ф = ?(Истина? 1 ; 2);"; + CatchParsingError(code); + } + + [Fact] + public void Check_Method_Definition_Delimiters() + { + var code = @"Процедура Проц1(арг1 арг2) + КонецПроцедуры"; + CatchParsingError(code); } + [Fact] + public void Check_Method_Call_Delimiters() + { + var code = @"Процедура Проц1(арг1, арг2) + КонецПроцедуры + Проц1(""1"" 2)"; + + CatchParsingError(code); + } + + [Fact] public void TestLocalExportVar() From e856bb2e3cb298f5d1c11a4fa0f207aa0d7b5f65 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Thu, 29 Jan 2026 20:55:05 +0400 Subject: [PATCH 38/52] =?UTF-8?q?=D0=B2=D1=8B=D1=85=D0=BE=D0=B4=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 8f136cc19..2d1ea47e4 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -729,8 +729,7 @@ private void BuildComplexStructureStatement() } else { - var expected = _tokenStack.Peek(); - AddError(LocalizedErrors.TokenExpected(expected)); + AddError(LocalizedErrors.TokenExpected(_tokenStack.Peek())); } break; } @@ -775,6 +774,7 @@ private void BuildGotoOperator() if (_lastExtractedLexem.Type != LexemType.LabelRef) { AddError(LocalizedErrors.LabelNameExpected()); + return; } gotoNode.AddChild(new LabelNode(_lastExtractedLexem)); @@ -1190,6 +1190,7 @@ private void BuildCallArguments(NonTerminalNode node) else { AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); + return; } } } @@ -1318,6 +1319,7 @@ private BslSyntaxNode BuildParenthesis() if (_lastExtractedLexem.Token != Token.ClosePar) { AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); + return new ErrorTerminalNode(); } NextLexem(); From 43aac757ad609208d47178a8e3e00d2aac86468a Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Fri, 30 Jan 2026 23:23:27 +0400 Subject: [PATCH 39/52] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B8=D0=BC=D0=B5=D1=8E?= =?UTF-8?q?=D1=89=D1=83=D1=8E=D1=81=D1=8F=20=D1=84=D1=83=D0=BD=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 2d1ea47e4..7179ea767 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -1318,8 +1318,7 @@ private BslSyntaxNode BuildParenthesis() var expr = BuildExpression(0); if (_lastExtractedLexem.Token != Token.ClosePar) { - AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); - return new ErrorTerminalNode(); + return CreateError(LocalizedErrors.TokenExpected(Token.ClosePar)); } NextLexem(); From 59cca19e2a19f23698ab3bc07f4df24845842121 Mon Sep 17 00:00:00 2001 From: Mr-Rm Date: Mon, 2 Feb 2026 21:51:14 +0400 Subject: [PATCH 40/52] =?UTF-8?q?fix=20#1652:=20=D0=B0=D0=BD=D0=BD=D0=BE?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D0=B8=20=D1=81=D0=BF=D0=B8=D1=81=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85,=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BD=D0=B5=D0=B4=D0=BE=D0=BF=D1=83=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D0=BC=D1=8B=D1=85=20=D0=BC=D0=B5=D1=81=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SyntaxAnalysis/DefaultBslParser.cs | 257 ++++++++++-------- .../SyntaxAnalysis/LocalizedErrors.cs | 3 + .../OneScript.Language.Tests/ParserTests.cs | 54 +++- tests/annotations.os | 27 ++ 4 files changed, 215 insertions(+), 126 deletions(-) diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 7179ea767..ad0839376 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -21,8 +21,8 @@ public class DefaultBslParser private readonly ILexer _lexer; private readonly PreprocessorHandlers _preprocessorHandlers; - private Lexem _lastExtractedLexem; - + private Lexem _lastExtractedLexem; + private bool _inMethodScope; private bool _isMethodsDefined; private bool _isStatementsDefined; @@ -32,8 +32,8 @@ public class DefaultBslParser private readonly Stack _tokenStack = new Stack(); private bool _isInLoopScope; - private bool _enableException; - + private bool _enableException; + private readonly List _annotations = new List(); public DefaultBslParser( @@ -47,30 +47,30 @@ public DefaultBslParser( _nodeContext = new ParserContext(); } - private IErrorSink ErrorSink { get; } - - public IEnumerable Errors => ErrorSink.Errors ?? Array.Empty(); - + private IErrorSink ErrorSink { get; } + + public IEnumerable Errors => ErrorSink.Errors ?? Array.Empty(); + public BslSyntaxNode ParseStatefulModule() { - ModuleNode node; - + ModuleNode node; + _preprocessorHandlers.OnModuleEnter(); NextLexem(); node = new ModuleNode(_lexer.Iterator.Source, _lastExtractedLexem); PushContext(node); try - { - ParseModuleSections(); + { + ParseModuleSections(); } finally { PopContext(); } - _preprocessorHandlers.OnModuleLeave(); - + _preprocessorHandlers.OnModuleLeave(); + return node; } @@ -105,8 +105,8 @@ public BslSyntaxNode ParseExpression() return module; } - private void PushContext(NonTerminalNode node) => _nodeContext.PushContext(node); - + private void PushContext(NonTerminalNode node) => _nodeContext.PushContext(node); + private NonTerminalNode PopContext() => _nodeContext.PopContext(); private NonTerminalNode CurrentParent => _nodeContext.CurrentParent; @@ -114,13 +114,13 @@ public BslSyntaxNode ParseExpression() private void ParseModuleAnnotation() { if (_lastExtractedLexem.Type != LexemType.PreprocessorDirective) - return; - + return; + var annotationParser = _preprocessorHandlers .Slice(x => x is ModuleAnnotationDirectiveHandler) .Cast() - .ToList(); - + .ToList(); + if (annotationParser.Count == 0) return; @@ -131,7 +131,7 @@ private void ParseModuleAnnotation() foreach (var handler in annotationParser) { handled = handler.ParseAnnotation(ref _lastExtractedLexem, _lexer, _nodeContext); - if(handled) + if (handled) break; } @@ -139,8 +139,8 @@ private void ParseModuleAnnotation() { AddError(LocalizedErrors.DirectiveNotSupported(directive)); } - } - + } + foreach (var handler in annotationParser) { handler.OnModuleLeave(); @@ -150,19 +150,18 @@ private void ParseModuleAnnotation() private void ParseModuleSections() { ParseModuleAnnotation(); - BuildVariableSection(); - BuildMethodsSection(); + BuildVariablesSection(); + BuildMethodsSection(); + if (_annotations.Count != 0) + { + AddError(LocalizedErrors.AnnotationNotAllowed()); + } BuildModuleBody(); - - if (_annotations.Count != 0) - { - AddError(LocalizedErrors.UnexpectedEof()); - } - } - + } + #region Variables - - private void BuildVariableSection() + + private void BuildVariablesSection() { if (_lastExtractedLexem.Token != Token.VarDef && _lastExtractedLexem.Type != LexemType.Annotation) { @@ -177,8 +176,9 @@ private void BuildVariableSection() { while (true) { - BuildAnnotations(); - if (_lastExtractedLexem.Token != Token.VarDef) + BuildAnnotations(); + + if (_lastExtractedLexem.Token != Token.VarDef) break; if (!hasVars) @@ -187,79 +187,88 @@ private void BuildVariableSection() parent.AddChild(allVarsSection); } - BuildVariableDefinition(); + BuildVariablesDefinition(); } } finally { PopContext(); - } - + } + } - private void BuildVariableDefinition() - { + private void BuildVariablesDefinition() + { + if (_inMethodScope) + { + if (_isStatementsDefined) + { + AddError(LocalizedErrors.LateVarDefinition()); + return; + } + } + else if (_isMethodsDefined) + { + AddError(LocalizedErrors.LateVarDefinition()); + return; + } + while (true) { - var variable = _nodeContext.AddChild(new VariableDefinitionNode(_lastExtractedLexem)); - - ApplyAnnotations(variable); - - NextLexem(); - + NextLexem(); // skip opening VarDef or Comma + if (!IsUserSymbol(_lastExtractedLexem)) { - AddError(LocalizedErrors.IdentifierExpected()); + if(_lastExtractedLexem.Type == LexemType.Annotation) + AddError(LocalizedErrors.AnnotationNotAllowed()); + else + AddError(LocalizedErrors.IdentifierExpected()); return; } - if (_inMethodScope) + BuildVariable(); + + if (_lastExtractedLexem.Token == Token.Semicolon) { - if (_isStatementsDefined) - { - AddError(LocalizedErrors.LateVarDefinition()); - return; - } + break; } - else if (_isMethodsDefined) + + if (_lastExtractedLexem.Token != Token.Comma) { - AddError(LocalizedErrors.LateVarDefinition()); + AddError(LocalizedErrors.SemicolonExpected()); return; } + } - var symbolicName = _lastExtractedLexem.Content; - CreateChild(variable, NodeKind.Identifier, _lastExtractedLexem); + NextLexem(); // skip Semicolon + _annotations.Clear(); + } - NextLexem(); - if (_lastExtractedLexem.Token == Token.Export) + private void BuildVariable() + { + var variable = _nodeContext.AddChild(new VariableDefinitionNode(_lastExtractedLexem)); + if (!_inMethodScope) + foreach (var astNode in _annotations) { - if (_inMethodScope) - { - AddError(LocalizedErrors.ExportedLocalVar(symbolicName)); - return; - } - CreateChild(variable, NodeKind.ExportFlag, _lastExtractedLexem); - NextLexem(); - } + variable.AddChild(astNode); + } - if (_lastExtractedLexem.Token == Token.Comma) - { - continue; - } + var symbolicName = _lastExtractedLexem.Content; + CreateChild(variable, NodeKind.Identifier, _lastExtractedLexem); - if (_lastExtractedLexem.Token == Token.Semicolon) - { - NextLexem(); - } - else + NextLexem(); + if (_lastExtractedLexem.Token == Token.Export) + { + if (_inMethodScope) { - AddError(LocalizedErrors.SemicolonExpected()); + AddError(LocalizedErrors.ExportedLocalVar(symbolicName)); + return; } + CreateChild(variable, NodeKind.ExportFlag, _lastExtractedLexem); + NextLexem(); + } + } - break; - } - } - private void ApplyAnnotations(AnnotatableNode annotatable) { foreach (var astNode in _annotations) @@ -269,13 +278,13 @@ private void ApplyAnnotations(AnnotatableNode annotatable) _annotations.Clear(); } - #endregion + #endregion #region Methods private void BuildMethodsSection() { - if (_lastExtractedLexem.Type != LexemType.Annotation + if (_lastExtractedLexem.Type != LexemType.Annotation && !IsStartOfMethod(_lastExtractedLexem)) { return; @@ -313,14 +322,14 @@ private void BuildMethodsSection() private static bool IsStartOfMethod(in Lexem lex) { return lex.Token == Token.Async || lex.Token == Token.Procedure || lex.Token == Token.Function; - } - + } + private void BuildMethod() { Debug.Assert(IsStartOfMethod(_lastExtractedLexem)); - var method = _nodeContext.AddChild(new MethodNode()); - + var method = _nodeContext.AddChild(new MethodNode()); + ApplyAnnotations(method); PushContext(method); if (_lastExtractedLexem.Token == Token.Async) @@ -328,8 +337,8 @@ private void BuildMethod() method.IsAsync = true; _isInAsyncMethod = true; NextLexem(); - } - + } + try { BuildMethodSignature(); @@ -354,7 +363,7 @@ private void BuildMethodVariablesSection() { // для корректной перемотки вперед в случае ошибок в секции переменных PushStructureToken(_isInFunctionScope ? Token.EndFunction : Token.EndProcedure); - BuildVariableSection(); + BuildVariablesSection(); } finally { @@ -383,7 +392,7 @@ private void BuildMethodSignature() { var signature = _nodeContext.AddChild(new MethodSignatureNode(_lastExtractedLexem)); var isFunction = _lastExtractedLexem.Token == Token.Function; - CreateChild(signature, isFunction? NodeKind.Function : NodeKind.Procedure, _lastExtractedLexem); + CreateChild(signature, isFunction ? NodeKind.Function : NodeKind.Procedure, _lastExtractedLexem); _isInFunctionScope = isFunction; NextLexem(); if (!IsUserSymbol(_lastExtractedLexem)) @@ -410,30 +419,30 @@ private void BuildMethodParameters(MethodSignatureNode signature) } var paramList = new NonTerminalNode(NodeKind.MethodParameters, _lastExtractedLexem); - signature.AddChild(paramList); - + signature.AddChild(paramList); + NextLexem(); // ( if (_lastExtractedLexem.Token != Token.ClosePar) - while (true) - { - BuildMethodParameter(paramList); - - if (_lastExtractedLexem.Token == Token.ClosePar) + while (true) { - break; - } + BuildMethodParameter(paramList); - if (_lastExtractedLexem.Token == Token.Comma) - { - NextLexem(); - } - else - { - AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); - return; + if (_lastExtractedLexem.Token == Token.ClosePar) + { + break; + } + + if (_lastExtractedLexem.Token == Token.Comma) + { + NextLexem(); + } + else + { + AddError(LocalizedErrors.TokenExpected(Token.ClosePar)); + return; + } } - } NextLexem(); // ) } @@ -501,15 +510,15 @@ private bool BuildDefaultParameterValue(NonTerminalNode param, NodeKind nodeKind } return true; - } - + } + #endregion - + private void BuildModuleBody() { if (!_lexer.Iterator.MoveToContent()) - return; - + return; + var moduleBody = new NonTerminalNode(NodeKind.ModuleBody, _lastExtractedLexem); var node = moduleBody.AddNode(new CodeBatchNode(_lastExtractedLexem)); PushContext(node); @@ -522,13 +531,19 @@ private void BuildModuleBody() PopContext(); } CurrentParent.AddChild(moduleBody); - } - + } + #region Annotations private void BuildAnnotations() { while (_lastExtractedLexem.Type == LexemType.Annotation) - { + { + if (_inMethodScope) + { + AddError(LocalizedErrors.AnnotationNotAllowed()); + return; + } + var node = BuildAnnotationDefinition(); _annotations.Add(node); } @@ -541,7 +556,6 @@ private AnnotationNode BuildAnnotationDefinition() { return node; } - private void BuildAnnotationParameters(AnnotationNode annotation) { if (_lastExtractedLexem.Token != Token.OpenPar) @@ -636,7 +650,10 @@ private void BuildCodeBatch(params Token[] endTokens) if (_lastExtractedLexem.Type != LexemType.Identifier && _lastExtractedLexem.Token != Token.EndOfText) { - AddError(LocalizedErrors.UnexpectedOperation()); + if (_lastExtractedLexem.Type == LexemType.Annotation) + AddError(LocalizedErrors.AnnotationNotAllowed()); + else + AddError(LocalizedErrors.UnexpectedOperation()); continue; } diff --git a/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs b/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs index 809d3e695..a6e23d267 100644 --- a/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs +++ b/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs @@ -111,6 +111,9 @@ public static CodeError WrongHandlerName() => public static CodeError UnexpectedSymbol(char c) => Create($"Неизвестный символ {c}", $"Unexpected character {c}"); + public static CodeError AnnotationNotAllowed() => + Create("Аннотация неприменима в данном месте", "Annotation is not allowed here"); + public static CodeError DirectiveNotSupported(string directive) => Create($"Директива {directive} не разрешена в данном месте", $"Directive {directive} is not supported here"); diff --git a/src/Tests/OneScript.Language.Tests/ParserTests.cs b/src/Tests/OneScript.Language.Tests/ParserTests.cs index 7d965101e..92d498423 100644 --- a/src/Tests/OneScript.Language.Tests/ParserTests.cs +++ b/src/Tests/OneScript.Language.Tests/ParserTests.cs @@ -168,6 +168,53 @@ Процедура Процедура1() Экспорт .NextChildIs(NodeKind.Annotation); } + [Fact] + public void Check_AnnotationNotAllowed_InList() + { + var code = @" + &Аннотация + Перем Пер1, &Анн Пер2;"; + + CatchParsingError(code, err => err.First().ErrorId.Should().Be("AnnotationNotAllowed")); + } + + [Fact] + public void Check_AnnotationNotAllowed_InMethod() + { + var code = @" + Процедура Процедура1() + &Аннотация + Возврат + КонецПроцедуры"; + + CatchParsingError(code, err => err.Single().ErrorId.Should().Be("AnnotationNotAllowed")); + } + + [Fact] + public void Check_AnnotationNotAllowed_BeforeMethodsEnd() + { + var code = @" + Процедура Процедура1() + Ч = 0; + &Аннотация + КонецПроцедуры"; + + CatchParsingError(code, err => err.Single().ErrorId.Should().Be("AnnotationNotAllowed")); + } + + [Fact] + public void Check_AnnotationNotAllowed_InModuleBody() + { + var code = @" + Процедура Процедура1() + КонецПроцедуры + &Аннотация + Ч = 0"; + + CatchParsingError(code, err => err.Single().ErrorId.Should().Be("AnnotationNotAllowed")); + } + + [Fact] public void Check_Method_Parameters() { @@ -1283,12 +1330,7 @@ public void TestLocalExportVar() Перем Переменная Экспорт; КонецПроцедуры"; - CatchParsingError(code, err => - { - var errors = err.ToArray(); - errors.Should().HaveCount(1); - errors[0].Description.Should().Contain("Локальная переменная не может быть экспортирована"); - }); + CatchParsingError(code, err => err.First().ErrorId.Should().Be("ExportedLocalVar")); } [Fact] diff --git a/tests/annotations.os b/tests/annotations.os index a095cf5c5..b18a75c20 100644 --- a/tests/annotations.os +++ b/tests/annotations.os @@ -15,6 +15,7 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьАннотацииПолейЗагрузитьСценарийИзСтроки"); ВсеТесты.Добавить("ТестДолжен_ПроверитьАннотациюКакЗначениеПараметраАннотации"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучениеАннотацийСпискаПеременных"); Возврат ВсеТесты; @@ -225,3 +226,29 @@ КонецЕсли; КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПолучениеАннотацийСпискаПеременных() Экспорт + + ТекстСценария = "&Аннотация1 + |Перем Поле1 Экспорт, Поле2; + |Перем Поле3; + |"; + + Сценарий = ЗагрузитьСценарийИзСтроки(ТекстСценария); + + Рефлектор = Новый Рефлектор; + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Сценарий, Истина); + + юТест.ПроверитьРавенство(ТаблицаСвойств.Количество(), 3, "Ожидали, что в таблице свойств будет 3 свойства"); + + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Аннотации.Количество(), 1, "Ожидали, что Поле1 имеет 1 аннотацию"); + ИмяАннотации = ТаблицаСвойств[0].Аннотации[0].Имя; + юТест.ПроверитьРавенство(ИмяАннотации, "Аннотация1", "Ожидали, что Поле1 имеет аннотацию Аннотация1"); + + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Аннотации.Количество(), 1, "Ожидали, что Поле2 имеет 1 аннотацию"); + ИмяАннотации = ТаблицаСвойств[1].Аннотации[0].Имя; + юТест.ПроверитьРавенство(ИмяАннотации, "Аннотация1", "Ожидали, что Поле2 имеет аннотацию Аннотация1"); + + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Аннотации.Количество(), 0, "Ожидали, что Поле3 не имеет аннотаций"); + +КонецПроцедуры From 129972fc696836637913ffb9a80cd6405fd817b7 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Wed, 4 Feb 2026 12:34:25 +0300 Subject: [PATCH 41/52] =?UTF-8?q?=D0=91=D0=B0=D0=BC=D0=BF=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index ee290e78c..fa13fb1ec 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,7 @@ pipeline { environment { VersionPrefix = '2.0.1' - VersionSuffix = 'rc.1'+"+${BUILD_NUMBER}" + VersionSuffix = 'rc.2'+"+${BUILD_NUMBER}" outputEnc = '65001' } From 923f1c1031f962424d601306a5b49aca32c8c1cb Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Wed, 4 Feb 2026 12:50:21 +0300 Subject: [PATCH 42/52] =?UTF-8?q?=D0=A1=D1=82=D0=B0=D1=82=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=20=D0=BF=D0=BE=D0=BB=D0=B5=20?= =?UTF-8?q?=D0=B1=D1=8B=D0=BB=D0=BE=20=D0=BD=D0=B5=20readonly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/OneScript.Core/Execution/ForbiddenBslProcess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OneScript.Core/Execution/ForbiddenBslProcess.cs b/src/OneScript.Core/Execution/ForbiddenBslProcess.cs index b0fc36f6b..73fb76973 100644 --- a/src/OneScript.Core/Execution/ForbiddenBslProcess.cs +++ b/src/OneScript.Core/Execution/ForbiddenBslProcess.cs @@ -20,7 +20,7 @@ namespace OneScript.Execution /// public class ForbiddenBslProcess : IBslProcess { - public static IBslProcess Instance = new ForbiddenBslProcess(); + public static readonly IBslProcess Instance = new ForbiddenBslProcess(); private ForbiddenBslProcess() {} From f0b7dd2c1eeb1a1739f89d740bfddddc4ff842ef Mon Sep 17 00:00:00 2001 From: Andrey Ovsiankin Date: Tue, 10 Feb 2026 14:37:47 +0300 Subject: [PATCH 43/52] =?UTF-8?q?fixes=20#1657=20=D0=B8=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BF=D1=80=D0=B8=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D1=82=20=D0=B0=D0=BA=D1=82=D0=B8=D0=B2=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=B8=D1=81=D1=82=D0=BE=D1=87=D0=BD=D0=B8?= =?UTF-8?q?=D0=BA=D0=BE=D0=B2=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8?= =?UTF-8?q?=D0=B3=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/developer_docs.md | 2 +- src/oscript/ConsoleHostBuilder.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/developer_docs.md b/docs/developer_docs.md index 1541add6e..b3c38febe 100644 --- a/docs/developer_docs.md +++ b/docs/developer_docs.md @@ -120,7 +120,7 @@ OneScript.Core — система типов и контекстная моде - LibraryLoader.cs — package-loader.os, подключение .os модулей/классов/макетов; FileSystemDependencyResolver.cs — поиск библиотек, цикл обработки, защита от циклических зависимостей. - Extensions/EngineBuilderExtensions.cs — UseSystemConfigFile/UseEnvironmentVariableConfig/UseEntrypointConfigFile; UseImports/UseFileSystemLibraries/UseNativeRuntime/UseEventHandlers. - Жизненный цикл: - 1) Читает настройки из системного `oscript.cfg`, env, и файла `oscript.cfg` рядом с entrypoint. + 1) Читает настройки из системного `oscript.cfg`, файла `oscript.cfg` рядом с entrypoint, и переменной окружения OSCRIPT_CONFIG (в порядке возрастания приоритета, то есть переменная окружения перезаписывает все предыдущие настройки). 2) Инициализация HostedScriptEngine → глобальные объекты → процесс → компиляция/исполнение модуля. 3) Загрузка библиотек: default или кастомный package-loader.os, последующая регистрация символов и компиляция задержанных модулей. diff --git a/src/oscript/ConsoleHostBuilder.cs b/src/oscript/ConsoleHostBuilder.cs index 8554a70f4..675179567 100644 --- a/src/oscript/ConsoleHostBuilder.cs +++ b/src/oscript/ConsoleHostBuilder.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one @@ -21,8 +21,8 @@ public static IEngineBuilder Create(string codePath) .SetupConfiguration(p => { p.UseSystemConfigFile() - .UseEnvironmentVariableConfig("OSCRIPT_CONFIG") - .UseEntrypointConfigFile(codePath); + .UseEntrypointConfigFile(codePath) + .UseEnvironmentVariableConfig("OSCRIPT_CONFIG"); }); BuildUpWithIoC(builder); From f9930dd91e90700e380d458b08b105aa143acf4a Mon Sep 17 00:00:00 2001 From: Andrey Ovsiankin Date: Tue, 10 Feb 2026 15:05:16 +0300 Subject: [PATCH 44/52] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82=20=D0=BD=D0=B0?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D0=B0=20=D0=B8=D0=B7=20?= =?UTF-8?q?=D0=BE=D0=BA=D1=80=D1=83=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EnvironmentVariableConfigProvider.cs | 38 ++++++ .../Extensions/EngineBuilderExtensions.cs | 12 +- tests/config.os | 124 ++++++++++++++++++ 3 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 src/ScriptEngine.HostedScript/EnvironmentVariableConfigProvider.cs create mode 100644 tests/config.os diff --git a/src/ScriptEngine.HostedScript/EnvironmentVariableConfigProvider.cs b/src/ScriptEngine.HostedScript/EnvironmentVariableConfigProvider.cs new file mode 100644 index 000000000..b9b357ad0 --- /dev/null +++ b/src/ScriptEngine.HostedScript/EnvironmentVariableConfigProvider.cs @@ -0,0 +1,38 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Commons; + +namespace ScriptEngine.HostedScript +{ + public class EnvironmentVariableConfigProvider : IConfigProvider + { + private readonly string _variableName; + + public EnvironmentVariableConfigProvider(string variableName) + { + _variableName = variableName; + } + + public Func> GetProvider() + { + var varName = _variableName; + return () => + { + // Читаем переменную окружения динамически при каждом вызове + var envValue = Environment.GetEnvironmentVariable(varName); + if (string.IsNullOrEmpty(envValue)) + return new Dictionary(); + + var paramList = new FormatParametersList(envValue); + return paramList.ToDictionary(); + }; + } + } +} diff --git a/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs b/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs index 9cfbdaeb9..780402b76 100644 --- a/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs +++ b/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one @@ -60,15 +60,7 @@ public static ConfigurationProviders UseEntrypointConfigFile(this ConfigurationP public static ConfigurationProviders UseEnvironmentVariableConfig(this ConfigurationProviders providers, string varName) { - var env = System.Environment.GetEnvironmentVariable(varName); - if(env == null) - return providers; - - var reader = new FormatStringConfigProvider - { - ValuesString = env - }; - + var reader = new EnvironmentVariableConfigProvider(varName); providers.Add(reader.GetProvider()); return providers; } diff --git a/tests/config.os b/tests/config.os new file mode 100644 index 000000000..73c6144a1 --- /dev/null +++ b/tests/config.os @@ -0,0 +1,124 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + ВсеТесты.Добавить("ТестДолженПроверитьПорядокПрименения_ПеременнаяОкруженияИмеетВысшийПриоритет"); + ВсеТесты.Добавить("ТестДолженПроверитьЧтениеЗначенияПоУмолчанию"); + ВсеТесты.Добавить("ТестДолженПроверитьПереопределениеНесколькихПараметровОдновременно"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолженПроверитьПорядокПрименения_ПеременнаяОкруженияИмеетВысшийПриоритет() Экспорт + + // Получаем исходное значение языка системы + ИсходноеЗначениеЯзыка = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + + // Определяем тестовое значение, отличное от текущего + Если ИсходноеЗначениеЯзыка = "ru" Тогда + ТестовоеЗначение = "en"; + Иначе + ТестовоеЗначение = "ru"; + КонецЕсли; + + // Устанавливаем переменную окружения с новым значением + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", "SystemLanguage=" + ТестовоеЗначение); + + Попытка + // Перечитываем конфигурацию + ОбновитьНастройкиСистемы(); + + // Проверяем, что значение изменилось + НовоеЗначениеЯзыка = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + + юТест.ПроверитьРавенство(ТестовоеЗначение, НовоеЗначениеЯзыка, + "Переменная окружения OSCRIPT_CONFIG должна иметь наивысший приоритет"); + + Исключение + // Очищаем переменную окружения даже в случае ошибки + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + ВызватьИсключение; + КонецПопытки; + + // Очищаем переменную окружения и возвращаем исходное состояние + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + ОбновитьНастройкиСистемы(); + + // Проверяем, что значение вернулось к исходному + ВосстановленноеЗначение = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + юТест.ПроверитьРавенство(ИсходноеЗначениеЯзыка, ВосстановленноеЗначение, + "После очистки переменной окружения значение должно вернуться к исходному"); + +КонецПроцедуры + +Процедура ТестДолженПроверитьЧтениеЗначенияПоУмолчанию() Экспорт + + // Проверяем, что можем прочитать существующий параметр + ЗначениеКодировки = ПолучитьЗначениеСистемнойНастройки("encoding.script"); + + // Значение может быть Неопределено, если не задано в конфигурации + // Это нормальное поведение + юТест.ПроверитьИстину( + ЗначениеКодировки = Неопределено ИЛИ ТипЗнч(ЗначениеКодировки) = Тип("Строка"), + "Значение encoding.script должно быть либо строкой, либо Неопределено"); + + // Проверяем чтение несуществующего параметра + НесуществующийПараметр = ПолучитьЗначениеСистемнойНастройки("несуществующий.параметр.test"); + + юТест.ПроверитьРавенство(Неопределено, НесуществующийПараметр, + "Несуществующий параметр должен возвращать Неопределено"); + +КонецПроцедуры + +Процедура ТестДолженПроверитьПереопределениеНесколькихПараметровОдновременно() Экспорт + + // Получаем исходные значения + ИсходныйЯзык = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + ИсходнаяКодировка = ПолучитьЗначениеСистемнойНастройки("encoding.script"); + + // Устанавливаем несколько параметров через точку с запятой + Если ИсходныйЯзык = "ru" Тогда + НовыйЯзык = "en"; + Иначе + НовыйЯзык = "ru"; + КонецЕсли; + + НоваяКодировка = "utf-8"; + + КонфигурационнаяСтрока = "SystemLanguage=" + НовыйЯзык + ";encoding.script=" + НоваяКодировка; + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", КонфигурационнаяСтрока); + + Попытка + // Перечитываем конфигурацию + ОбновитьНастройкиСистемы(); + + // Проверяем, что оба значения изменились + ЗначениеЯзыка = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + ЗначениеКодировки = ПолучитьЗначениеСистемнойНастройки("encoding.script"); + + юТест.ПроверитьРавенство(НовыйЯзык, ЗначениеЯзыка, + "Язык системы должен измениться на " + НовыйЯзык); + + юТест.ПроверитьРавенство(НоваяКодировка, ЗначениеКодировки, + "Кодировка должна измениться на " + НоваяКодировка); + + Исключение + // Очищаем переменную окружения даже в случае ошибки + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + ВызватьИсключение; + КонецПопытки; + + // Очищаем переменную окружения + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + ОбновитьНастройкиСистемы(); + + // Проверяем, что значения вернулись (или стали Неопределено) + ВосстановленныйЯзык = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + юТест.ПроверитьРавенство(ИсходныйЯзык, ВосстановленныйЯзык, + "Язык системы должен вернуться к исходному значению"); + +КонецПроцедуры From 8207c80a5f4154e7926b64d332530626c4b5de18 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Fri, 13 Feb 2026 16:28:24 +0300 Subject: [PATCH 45/52] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BC=D0=B5=D1=85=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B0=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3?= =?UTF-8?q?=D1=83=D1=80=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CfgFileConfigProvider.cs | 42 +++++----------- .../EngineConfigProvider.cs | 35 ------------- .../EnvironmentVariableConfigProvider.cs | 29 ++++++----- .../Extensions/EngineBuilderExtensions.cs | 6 +-- .../FormatStringConfigProvider.cs | 28 ----------- .../OneScriptLibraryOptions.cs | 15 ++---- .../SystemConfigAccessor.cs | 27 ++++------ .../Hosting/ConfigurationProviders.cs | 14 +++--- .../Hosting/ConfigurationValue.cs | 50 +++++++++++++++++++ .../Hosting/EngineBuilderExtensions.cs | 5 +- .../Hosting/EngineConfiguration.cs | 43 ++++++++++++++++ .../Hosting}/IConfigProvider.cs | 12 +++-- src/ScriptEngine/Hosting/KeyValueConfig.cs | 45 +++++++---------- .../ExplicitImportsTest.cs | 25 +++++++++- 14 files changed, 200 insertions(+), 176 deletions(-) delete mode 100644 src/ScriptEngine.HostedScript/EngineConfigProvider.cs delete mode 100644 src/ScriptEngine.HostedScript/FormatStringConfigProvider.cs create mode 100644 src/ScriptEngine/Hosting/ConfigurationValue.cs create mode 100644 src/ScriptEngine/Hosting/EngineConfiguration.cs rename src/{ScriptEngine.HostedScript => ScriptEngine/Hosting}/IConfigProvider.cs (59%) diff --git a/src/ScriptEngine.HostedScript/CfgFileConfigProvider.cs b/src/ScriptEngine.HostedScript/CfgFileConfigProvider.cs index 68e14bf92..18ee7a842 100644 --- a/src/ScriptEngine.HostedScript/CfgFileConfigProvider.cs +++ b/src/ScriptEngine.HostedScript/CfgFileConfigProvider.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one @@ -8,7 +8,7 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; using System.IO; -using System.Linq; +using ScriptEngine.Hosting; namespace ScriptEngine.HostedScript { @@ -20,10 +20,17 @@ public class CfgFileConfigProvider : IConfigProvider public bool Required { get; set; } - public Func> GetProvider() + public string SourceId => FilePath; + + public IReadOnlyDictionary Load() + { + return (IReadOnlyDictionary)ReadConfigFile(FilePath); + } + + public string ResolveRelativePath(string path) { - var localCopy = FilePath; - return () => ReadConfigFile(localCopy); + var confDir = Path.GetDirectoryName(FilePath); + return Path.Combine(confDir, path); } private IDictionary ReadConfigFile(string configPath) @@ -58,30 +65,7 @@ private IDictionary ReadConfigFile(string configPath) } } - ExpandRelativePaths(conf, configPath); - return conf; } - - private static void ExpandRelativePaths(IDictionary conf, string configFile) - { - string sysDir = null; - conf.TryGetValue(OneScriptLibraryOptions.SYSTEM_LIBRARY_DIR, out sysDir); - - var confDir = System.IO.Path.GetDirectoryName(configFile); - if (sysDir != null && !System.IO.Path.IsPathRooted(sysDir)) - { - sysDir = System.IO.Path.GetFullPath(System.IO.Path.Combine(confDir, sysDir)); - conf[OneScriptLibraryOptions.SYSTEM_LIBRARY_DIR] = sysDir; - } - - string additionals; - if (conf.TryGetValue(OneScriptLibraryOptions.ADDITIONAL_LIBRARIES, out additionals)) - { - var fullPaths = additionals.Split(new[]{";"}, StringSplitOptions.RemoveEmptyEntries) - .Select(x => Path.GetFullPath(Path.Combine(confDir, x))); - conf[OneScriptLibraryOptions.ADDITIONAL_LIBRARIES] = string.Join(";",fullPaths); - } - } } -} \ No newline at end of file +} diff --git a/src/ScriptEngine.HostedScript/EngineConfigProvider.cs b/src/ScriptEngine.HostedScript/EngineConfigProvider.cs deleted file mode 100644 index abb2f8e32..000000000 --- a/src/ScriptEngine.HostedScript/EngineConfigProvider.cs +++ /dev/null @@ -1,35 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using ScriptEngine.Hosting; - -namespace ScriptEngine.HostedScript -{ - public class EngineConfigProvider - { - private readonly ConfigurationProviders _providers; - KeyValueConfig _currentConfig; - - public EngineConfigProvider(ConfigurationProviders providers) - { - _providers = providers; - UpdateConfig(); - } - - private void UpdateConfig() - { - _currentConfig = _providers.CreateConfig(); - } - - public KeyValueConfig ReadConfig() - { - UpdateConfig(); - return _currentConfig; - } - - } -} diff --git a/src/ScriptEngine.HostedScript/EnvironmentVariableConfigProvider.cs b/src/ScriptEngine.HostedScript/EnvironmentVariableConfigProvider.cs index b9b357ad0..b9f3c6ff2 100644 --- a/src/ScriptEngine.HostedScript/EnvironmentVariableConfigProvider.cs +++ b/src/ScriptEngine.HostedScript/EnvironmentVariableConfigProvider.cs @@ -8,6 +8,7 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; using OneScript.Commons; +using ScriptEngine.Hosting; namespace ScriptEngine.HostedScript { @@ -19,20 +20,22 @@ public EnvironmentVariableConfigProvider(string variableName) { _variableName = variableName; } - - public Func> GetProvider() + + public string SourceId => _variableName; + + public IReadOnlyDictionary Load() + { + var envValue = Environment.GetEnvironmentVariable(_variableName); + if (string.IsNullOrEmpty(envValue)) + return new Dictionary(); + + var paramList = new FormatParametersList(envValue); + return paramList.ToDictionary(); + } + + public string ResolveRelativePath(string path) { - var varName = _variableName; - return () => - { - // Читаем переменную окружения динамически при каждом вызове - var envValue = Environment.GetEnvironmentVariable(varName); - if (string.IsNullOrEmpty(envValue)) - return new Dictionary(); - - var paramList = new FormatParametersList(envValue); - return paramList.ToDictionary(); - }; + return path; } } } diff --git a/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs b/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs index 780402b76..185612566 100644 --- a/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs +++ b/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs @@ -25,7 +25,7 @@ public static ConfigurationProviders UseConfigFile(this ConfigurationProviders p FilePath = configFile, Required = required }; - providers.Add(reader.GetProvider()); + providers.Add(reader); } return providers; @@ -61,7 +61,7 @@ public static ConfigurationProviders UseEntrypointConfigFile(this ConfigurationP public static ConfigurationProviders UseEnvironmentVariableConfig(this ConfigurationProviders providers, string varName) { var reader = new EnvironmentVariableConfigProvider(varName); - providers.Add(reader.GetProvider()); + providers.Add(reader); return providers; } @@ -119,4 +119,4 @@ public static IEngineBuilder UseEventHandlers(this IEngineBuilder builder) return builder; } } -} \ No newline at end of file +} diff --git a/src/ScriptEngine.HostedScript/FormatStringConfigProvider.cs b/src/ScriptEngine.HostedScript/FormatStringConfigProvider.cs deleted file mode 100644 index e1ce9f389..000000000 --- a/src/ScriptEngine.HostedScript/FormatStringConfigProvider.cs +++ /dev/null @@ -1,28 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using OneScript.Commons; - -namespace ScriptEngine.HostedScript -{ - public class FormatStringConfigProvider : IConfigProvider - { - public string ValuesString { get; set; } - - public Func> GetProvider() - { - var localCopy = ValuesString; - return () => - { - var paramList = new FormatParametersList(localCopy); - return paramList.ToDictionary(); - }; - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/OneScriptLibraryOptions.cs b/src/ScriptEngine.HostedScript/OneScriptLibraryOptions.cs index 370977ba5..f407c1cc0 100644 --- a/src/ScriptEngine.HostedScript/OneScriptLibraryOptions.cs +++ b/src/ScriptEngine.HostedScript/OneScriptLibraryOptions.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one @@ -6,6 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System.Collections.Generic; +using System.Linq; using ScriptEngine.Hosting; namespace ScriptEngine.HostedScript @@ -17,18 +18,12 @@ public class OneScriptLibraryOptions : OneScriptCoreOptions public OneScriptLibraryOptions(KeyValueConfig config) : base(config) { - SystemLibraryDir = config[SYSTEM_LIBRARY_DIR]; - - var additionalDirsList = config[ADDITIONAL_LIBRARIES]; - if (additionalDirsList != null) - { - var addDirs = additionalDirsList.Split(';'); - AdditionalLibraries = new List(addDirs); - } + SystemLibraryDir = config.GetEntry(SYSTEM_LIBRARY_DIR)?.ResolvePath(); + AdditionalLibraries = config.GetEntry(ADDITIONAL_LIBRARIES)?.ResolvePathList(';').ToList(); } public string SystemLibraryDir { get; set; } public IEnumerable AdditionalLibraries { get; set; } } -} \ No newline at end of file +} diff --git a/src/ScriptEngine.HostedScript/SystemConfigAccessor.cs b/src/ScriptEngine.HostedScript/SystemConfigAccessor.cs index 619e6f6f6..996ee71fa 100644 --- a/src/ScriptEngine.HostedScript/SystemConfigAccessor.cs +++ b/src/ScriptEngine.HostedScript/SystemConfigAccessor.cs @@ -15,13 +15,11 @@ namespace ScriptEngine.HostedScript.Library [GlobalContext(Category = "Работа с настройками системы")] public class SystemConfigAccessor : GlobalContextBase { - private KeyValueConfig _config; - private readonly ConfigurationProviders _providers; + private readonly EngineConfiguration _activeConfig; - public SystemConfigAccessor(ConfigurationProviders providers) + public SystemConfigAccessor(EngineConfiguration activeConfig) { - _providers = providers; - Refresh(); + _activeConfig = activeConfig; } /// @@ -30,7 +28,7 @@ public SystemConfigAccessor(ConfigurationProviders providers) [ContextMethod("ОбновитьНастройкиСистемы", "RefreshSystemConfig")] public void Refresh() { - _config = _providers.CreateConfig(); + _activeConfig.Reload(); } /// @@ -41,21 +39,16 @@ public void Refresh() [ContextMethod("ПолучитьЗначениеСистемнойНастройки", "GetSystemOptionValue")] public IValue GetSystemOptionValue(string optionKey) { - string value = null; - if (_config != null) - { - value = _config[optionKey]; - } - - if (value != null) - return ValueFactory.Create(value); + var cfg = _activeConfig.GetConfig(); - return ValueFactory.Create(); + var value = cfg[optionKey]; + + return value != null ? ValueFactory.Create(value) : ValueFactory.Create(); } - public static IAttachableContext CreateInstance(ConfigurationProviders providers) + public static IAttachableContext CreateInstance(EngineConfiguration configHolder) { - return new SystemConfigAccessor(providers); + return new SystemConfigAccessor(configHolder); } } } diff --git a/src/ScriptEngine/Hosting/ConfigurationProviders.cs b/src/ScriptEngine/Hosting/ConfigurationProviders.cs index f5b63219a..f1fbebeec 100644 --- a/src/ScriptEngine/Hosting/ConfigurationProviders.cs +++ b/src/ScriptEngine/Hosting/ConfigurationProviders.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one @@ -7,16 +7,17 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; +using System.Linq; namespace ScriptEngine.Hosting { public class ConfigurationProviders { - private List>> _providers = new List>>(); + private readonly List _providers = new List(); - public void Add(Func> configGetter) + public void Add(IConfigProvider source) { - _providers.Add(configGetter); + _providers.Add(source); } public KeyValueConfig CreateConfig() @@ -24,10 +25,11 @@ public KeyValueConfig CreateConfig() var cfg = new KeyValueConfig(); foreach (var provider in _providers) { - cfg.Merge(provider()); + var values = provider.Load(); + cfg.Merge((IDictionary)values, provider); } return cfg; } } -} \ No newline at end of file +} diff --git a/src/ScriptEngine/Hosting/ConfigurationValue.cs b/src/ScriptEngine/Hosting/ConfigurationValue.cs new file mode 100644 index 000000000..e8765f9a9 --- /dev/null +++ b/src/ScriptEngine/Hosting/ConfigurationValue.cs @@ -0,0 +1,50 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace ScriptEngine.Hosting +{ + public class ConfigurationValue + { + public string RawValue { get; } + public IConfigProvider Source { get; } + + public ConfigurationValue(string rawValue, IConfigProvider source) + { + RawValue = rawValue; + Source = source; + } + + /// + /// Разрешает значение как путь относительно источника конфигурации + /// + public string ResolvePath() + { + return string.IsNullOrEmpty(RawValue) ? RawValue : Source.ResolveRelativePath(RawValue.Trim()); + } + + /// + /// Разрешает значение как список путей + /// + public IEnumerable ResolvePathList(char separator = ';') + { + if (string.IsNullOrEmpty(RawValue)) + return Enumerable.Empty(); + + return RawValue.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries) + .Select(path => + { + var trimmed = path.Trim(); + return Source.ResolveRelativePath(trimmed); + }); + } + } +} diff --git a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs index 47a03759d..37225644d 100644 --- a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs +++ b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs @@ -73,10 +73,11 @@ public static IEngineBuilder SetDefaultOptions(this IEngineBuilder builder) return new PreprocessorHandlers(providers); }); + services.RegisterSingleton(); services.Register(sp => { - var providers = sp.Resolve(); - return providers.CreateConfig(); + var holder = sp.Resolve(); + return holder.GetConfig(); }); services.Register(); diff --git a/src/ScriptEngine/Hosting/EngineConfiguration.cs b/src/ScriptEngine/Hosting/EngineConfiguration.cs new file mode 100644 index 000000000..46b80daf9 --- /dev/null +++ b/src/ScriptEngine/Hosting/EngineConfiguration.cs @@ -0,0 +1,43 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine.Hosting +{ + /// + /// Предназначен для хранения стабильного конфига + /// который не изменяется и не вычитывает провайдеры при каждом обращении к опциям. + /// + public class EngineConfiguration + { + private KeyValueConfig _config; + private readonly ConfigurationProviders _providers; + + private readonly object _refreshLock = new object(); + + public EngineConfiguration(ConfigurationProviders providers) + { + _providers = providers; + _config = _providers.CreateConfig(); + } + + public KeyValueConfig GetConfig() + { + lock (_refreshLock) + { + return _config; + } + } + + public void Reload() + { + lock (_refreshLock) + { + _config = _providers.CreateConfig(); + } + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/IConfigProvider.cs b/src/ScriptEngine/Hosting/IConfigProvider.cs similarity index 59% rename from src/ScriptEngine.HostedScript/IConfigProvider.cs rename to src/ScriptEngine/Hosting/IConfigProvider.cs index cd07f0b7e..1b1271d48 100644 --- a/src/ScriptEngine.HostedScript/IConfigProvider.cs +++ b/src/ScriptEngine/Hosting/IConfigProvider.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one @@ -8,10 +8,14 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; -namespace ScriptEngine.HostedScript +namespace ScriptEngine.Hosting { public interface IConfigProvider { - Func> GetProvider(); + string SourceId { get; } + + IReadOnlyDictionary Load(); + + string ResolveRelativePath(string path); } -} \ No newline at end of file +} diff --git a/src/ScriptEngine/Hosting/KeyValueConfig.cs b/src/ScriptEngine/Hosting/KeyValueConfig.cs index e754902ae..909d35fcb 100644 --- a/src/ScriptEngine/Hosting/KeyValueConfig.cs +++ b/src/ScriptEngine/Hosting/KeyValueConfig.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one @@ -12,43 +12,34 @@ namespace ScriptEngine.Hosting { public class KeyValueConfig { - private readonly Dictionary _values = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly Dictionary _values = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - public KeyValueConfig() - { - } - - public KeyValueConfig(IDictionary source) - { - Merge(source); - } - - public void Merge(IDictionary source) + public void Merge(IDictionary source, IConfigProvider sourceProvider) { foreach (var keyValue in source) { - this[keyValue.Key] = keyValue.Value; + if (string.IsNullOrWhiteSpace(keyValue.Key)) + throw BadKeyException(keyValue.Key); + + _values[keyValue.Key] = new ConfigurationValue(keyValue.Value, sourceProvider); } } - public string this[string key] + public ConfigurationValue GetEntry(string key) { - get - { - if (string.IsNullOrWhiteSpace(key)) - throw BadKeyException(key); + if (string.IsNullOrWhiteSpace(key)) + throw BadKeyException(key); - _values.TryGetValue(key, out var value); - - return value; + _values.TryGetValue(key, out var value); + return value; + } - } - private set + public string this[string key] + { + get { - if (String.IsNullOrWhiteSpace(key)) - throw BadKeyException(key); - - _values[key] = value; + var entry = GetEntry(key); + return entry?.RawValue; } } diff --git a/src/Tests/OneScript.Core.Tests/ExplicitImportsTest.cs b/src/Tests/OneScript.Core.Tests/ExplicitImportsTest.cs index 1e6b79482..586c4e0a4 100644 --- a/src/Tests/OneScript.Core.Tests/ExplicitImportsTest.cs +++ b/src/Tests/OneScript.Core.Tests/ExplicitImportsTest.cs @@ -216,10 +216,10 @@ private IRuntimeContextInstance CompileUserScript( .Create() .SetupConfiguration(p => { - p.Add(() => new Dictionary + p.Add(new DictionaryConfigProvider(new Dictionary { { "lang.explicitImports", configValue } - }); + })); }) .SetDefaultOptions(); @@ -314,6 +314,27 @@ public PackageInfo Resolve(SourceCode module, string libraryName, IBslProcess pr return null; } } + + private class DictionaryConfigProvider : IConfigProvider + { + private readonly Dictionary _config; + public string SourceId => "dictionary"; + + public DictionaryConfigProvider(Dictionary config) + { + _config = config; + } + + public IReadOnlyDictionary Load() + { + return _config; + } + + public string ResolveRelativePath(string path) + { + return path; + } + } } } From dd7203eec144a6940f22cabe6dea391e474e6d7d Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 5 Mar 2026 20:21:23 +0300 Subject: [PATCH 46/52] =?UTF-8?q?=D0=9E=D0=BF=D0=B8=D1=81=D0=B0=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=202.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/release-notes.md | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/install/release-notes.md b/install/release-notes.md index dd8193e6a..8eeb7d412 100644 --- a/install/release-notes.md +++ b/install/release-notes.md @@ -1,23 +1,15 @@ -# Версия 2.0.0 +# Версия 2.0.1 -Версия нового поколения, основанная на современной платформе .NET. -За счет этого обеспечивается кроссплатформенность без использования Mono на Linux и MacOS. +## Исправление ошибок -## Требования по адаптации +* #1646 Ошибка метода ЗаполнитьЗначенияСвойств на ФиксированнойСтруктуре +* #1647 Исправлена вставка ключа Null в соответствие +* #1649 Исправлена обработка символов '\0' в каталогах WebDAV +* #1654 В методе можно было объявлять параметры, не разделяя их запятой +* #1652 Исправлена обработка аннотаций для списка переменных, объявленных через запятую +* #1657 Исправлен приоритет активации источников в конфиге -За счет новой базовой платформы, потеряна бинарная совместимость с существующими внешними компонентами (dll). -Требуется перекомпиляция компоненты под новую версию .net, как минимум, под слой совместимости netstandard 2.0 +## Рефакторинг и оптимизация -## Изменения по сравнению с ознакомительной версией 2.0.0-rc.10 - -* Уточнена консольная справка по режимам работы -* Исправлена лишняя остановка отладчиком на заголовке цикла Для Каждого -* Исправлены шаги отладчика. Ранее, отладчик ошибочно не останавливался на строке КонецЦикла -* Добавлена поддержка потока в ЗаписьТекста -* Оптимизирована производительность рефлектора -* Оптимизирована производительность компилятора -* Оптимизирована производительность создания таблицы значений -* В отладчике теперь отображаются переменные уровня модуля (ранее отображались только локальные) -* Улучшена совместимость функции СтрНайти с платформой 1С - -Большое спасибо @Mr-Rm, @dmpas, @Bayselonarrend за участие в этом релизе! \ No newline at end of file +* В парсере применен приоритет операторов вместо рекурсивного спуска (улучшена скорость компиляции) +* Загрузчик библиотек теперь разделяет ситуацию ненайденных библиотек и тех, которые не удается загрузить (улучшена диагностика ненайденных библиотек) From 9d176ce1d39982b4668840f8277666ce93c9de7a Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 5 Mar 2026 20:25:12 +0300 Subject: [PATCH 47/52] =?UTF-8?q?=D0=B1=D0=B0=D0=BC=D0=BF=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D0=B8=D0=B8=20dev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index fa13fb1ec..706ed496d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,8 +4,8 @@ pipeline { agent none environment { - VersionPrefix = '2.0.1' - VersionSuffix = 'rc.2'+"+${BUILD_NUMBER}" + VersionPrefix = '2.0.2' + VersionSuffix = 'rc.1'+"+${BUILD_NUMBER}" outputEnc = '65001' } From 8d4c483505995ffb4e3f6c268a034f074732ae85 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Thu, 12 Mar 2026 21:47:50 +0300 Subject: [PATCH 48/52] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D1=82=D0=B5=D1=81=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ScriptEngine/Hosting/ConfigurationProviders.cs | 5 ++++- tests/config.os | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ScriptEngine/Hosting/ConfigurationProviders.cs b/src/ScriptEngine/Hosting/ConfigurationProviders.cs index f1fbebeec..9dae5ec81 100644 --- a/src/ScriptEngine/Hosting/ConfigurationProviders.cs +++ b/src/ScriptEngine/Hosting/ConfigurationProviders.cs @@ -26,7 +26,10 @@ public KeyValueConfig CreateConfig() foreach (var provider in _providers) { var values = provider.Load(); - cfg.Merge((IDictionary)values, provider); + if (values != null && values.Count > 0) + { + cfg.Merge((IDictionary)values, provider); + } } return cfg; diff --git a/tests/config.os b/tests/config.os index 73c6144a1..e3d33f62e 100644 --- a/tests/config.os +++ b/tests/config.os @@ -118,7 +118,12 @@ // Проверяем, что значения вернулись (или стали Неопределено) ВосстановленныйЯзык = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + ВосстановленнаяКодировка = ПолучитьЗначениеСистемнойНастройки("encoding.script"); + юТест.ПроверитьРавенство(ИсходныйЯзык, ВосстановленныйЯзык, "Язык системы должен вернуться к исходному значению"); + + юТест.ПроверитьРавенство(ИсходнаяКодировка, ВосстановленнаяКодировка, + "Кодировка должна вернуться к исходному значению"); КонецПроцедуры From 2e326de247a7f71c1dca57714983e69bb6cb8184 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Tue, 24 Mar 2026 22:05:16 +0300 Subject: [PATCH 49/52] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BA=D0=BE=D0=BD=D1=81=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B8=20=D0=B2=D0=B5?= =?UTF-8?q?=D0=B1-=D0=BF=D1=80=D0=BE=D1=86=D0=B5=D1=81=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/VSCode.DebugAdapter/ConsoleProcess.cs | 13 ++++++++++--- src/VSCode.DebugAdapter/DebugeeProcess.cs | 12 ++++-------- src/VSCode.DebugAdapter/ServerProcess.cs | 12 ++++++++---- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/VSCode.DebugAdapter/ConsoleProcess.cs b/src/VSCode.DebugAdapter/ConsoleProcess.cs index a1a740278..99444187e 100644 --- a/src/VSCode.DebugAdapter/ConsoleProcess.cs +++ b/src/VSCode.DebugAdapter/ConsoleProcess.cs @@ -5,10 +5,12 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using Newtonsoft.Json.Linq; +using EvilBeaver.DAP.Dto.Requests; +using EvilBeaver.DAP.Dto.Serialization; using Serilog; namespace VSCode.DebugAdapter @@ -30,10 +32,15 @@ public ConsoleProcess(PathHandlingStrategy pathHandling) : base(pathHandling) public string RuntimeArguments { get; set; } public IDictionary Environment { get; set; } = new Dictionary(); + + public override void Init(LaunchRequestArguments args) + { + var options = args.DeserializeAdditionalProperties(); + InitInternal(options); + } - protected override void InitInternal(JObject args) + private void InitInternal(ConsoleLaunchOptions options) { - var options = args.ToObject(); if (options.Program == null) { throw new InvalidDebugeeOptionsException(1001, "Property 'program' is missing or empty."); diff --git a/src/VSCode.DebugAdapter/DebugeeProcess.cs b/src/VSCode.DebugAdapter/DebugeeProcess.cs index e27ba8738..148103ec9 100644 --- a/src/VSCode.DebugAdapter/DebugeeProcess.cs +++ b/src/VSCode.DebugAdapter/DebugeeProcess.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one @@ -11,6 +11,7 @@ This Source Code Form is subject to the terms of the using System.Linq; using System.Runtime.InteropServices; using System.Text; +using EvilBeaver.DAP.Dto.Requests; using Newtonsoft.Json.Linq; using Serilog; using VSCode.DebugAdapter.Transport; @@ -118,11 +119,8 @@ public void InitAttached() } - public void Init(JObject args) - { - InitInternal(args); - } - + public abstract void Init(LaunchRequestArguments args); + public void InitPathsMapper(JObject args) { if (args == null) @@ -151,8 +149,6 @@ public void InitPathsMapper(JObject args) protected abstract Process CreateProcess(); - protected abstract void InitInternal(JObject args); - protected string ConvertClientPathToDebugger(string clientPath) { return _strategy.ConvertClientPathToDebugger(clientPath); diff --git a/src/VSCode.DebugAdapter/ServerProcess.cs b/src/VSCode.DebugAdapter/ServerProcess.cs index 844fea9d6..644bf20aa 100644 --- a/src/VSCode.DebugAdapter/ServerProcess.cs +++ b/src/VSCode.DebugAdapter/ServerProcess.cs @@ -8,7 +8,8 @@ This Source Code Form is subject to the terms of the using System.Collections.Generic; using System.Diagnostics; using System.IO; -using Newtonsoft.Json.Linq; +using EvilBeaver.DAP.Dto.Requests; +using EvilBeaver.DAP.Dto.Serialization; namespace VSCode.DebugAdapter { @@ -51,10 +52,13 @@ protected override Process CreateProcess() return process; } - protected override void InitInternal(JObject args) + public override void Init(LaunchRequestArguments args) + { + InitInternal(args.DeserializeAdditionalProperties()); + } + + private void InitInternal(WebLaunchOptions options) { - var options = args.ToObject(); - // validate argument 'cwd' var workingDirectory = options.AppDir; if (workingDirectory != null) From 6b09491496c6009991bd75a4f70a27f6632fca15 Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Tue, 24 Mar 2026 22:13:30 +0300 Subject: [PATCH 50/52] =?UTF-8?q?=D0=9D=D0=B0=D1=87=D0=B0=D0=BB=D0=BE=20?= =?UTF-8?q?=D0=B4=D0=BE=20=D0=BC=D0=B0=D1=81=D1=88=D1=82=D0=B0=D0=B1=D0=BD?= =?UTF-8?q?=D0=BE=D0=B9=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC=D0=B0=D1=82=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B2=D0=B0=D0=B9=D0=B1=D0=BE?= =?UTF-8?q?=D0=BA=D0=BE=D0=B4=D0=B8=D0=BD=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/VSCode.DebugAdapter/DebugeeProcess.cs | 8 +- .../OneScriptDebugAdapter.cs | 214 ++++++++++++++++++ .../OscriptDebugEventsListener.cs | 48 +++- src/VSCode.DebugAdapter/Program.cs | 8 + src/VSCode.DebugAdapter/ProtocolExtensions.cs | 18 +- src/VSCode.DebugAdapter/Utilities.cs | 10 + .../VSCode.DebugAdapter.csproj | 8 +- src/VSCode.DebugAdapter/WorkspaceMapper.cs | 1 - 8 files changed, 294 insertions(+), 21 deletions(-) create mode 100644 src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs diff --git a/src/VSCode.DebugAdapter/DebugeeProcess.cs b/src/VSCode.DebugAdapter/DebugeeProcess.cs index 148103ec9..b651b5bca 100644 --- a/src/VSCode.DebugAdapter/DebugeeProcess.cs +++ b/src/VSCode.DebugAdapter/DebugeeProcess.cs @@ -121,17 +121,17 @@ public void InitAttached() public abstract void Init(LaunchRequestArguments args); - public void InitPathsMapper(JObject args) + public void InitPathsMapper(AttachRequestArguments args) { - if (args == null) + if (!args.AdditionalData?.ContainsKey("pathsMapping") ?? false) { PathsMapper = null; return; } - + try { - var mappingToken = args["pathsMapping"]; + var mappingToken = args.AdditionalData?["pathsMapping"]; if (mappingToken == null || mappingToken.Type == JTokenType.Null) { PathsMapper = null; diff --git a/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs b/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs new file mode 100644 index 000000000..e2c807f1a --- /dev/null +++ b/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs @@ -0,0 +1,214 @@ +// /*---------------------------------------------------------- +// This Source Code Form is subject to the terms of the +// Mozilla Public License, v.2.0. If a copy of the MPL +// was not distributed with this file, You can obtain one +// at http://mozilla.org/MPL/2.0/. +// ----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using EvilBeaver.DAP.Dto.Base; +using EvilBeaver.DAP.Dto.Events; +using EvilBeaver.DAP.Dto.Requests; +using EvilBeaver.DAP.Dto.Serialization; +using EvilBeaver.DAP.Dto.Types; +using EvilBeaver.DAP.Server; +using Microsoft.Extensions.Logging; +using VSCode.DebugAdapter.Transport; + +namespace VSCode.DebugAdapter +{ + internal class OneScriptDebugAdapter : DebugAdapterBase + { + private DebugeeProcess _debuggee; + private bool _startupPerformed = false; + private ThreadStateContainer _threadState = new ThreadStateContainer(); + + private ILogger Log { get; } + + public OneScriptDebugAdapter(ILogger logger) + { + Log = logger; + } + + protected override async Task OnInitializeAsync(InitializeRequest request, CancellationToken ct) + { + await EventsChannel.SendEventAsync(new InitializedEvent(), ct); + + return new InitializeResponse() + { + Body = new Capabilities + { + SupportsConditionalBreakpoints = true, + SupportsFunctionBreakpoints = false, + SupportsConfigurationDoneRequest = true, + SupportsExceptionFilterOptions = true, + ExceptionBreakpointFilters = new [] + { + new ExceptionBreakpointsFilter + { + Filter = "uncaught", + Label = "Необработанные исключения", + Description = "Остановка при возникновении необработанного исключения", + SupportsCondition = true, + ConditionDescription = "Искомая подстрока текста исключения" + }, + new ExceptionBreakpointsFilter + { + Filter = "all", + Label = "Все исключения", + Description = "Остановка при возникновении любого исключения", + SupportsCondition = true, + ConditionDescription = "Искомая подстрока текста исключения" + } + }, + SupportsEvaluateForHovers = true, + SupportTerminateDebuggee = true + } + }; + } + + public override Task SetBreakpointsAsync(SetBreakpointsRequest request, CancellationToken ct) + { + if (request.Arguments.SourceModified == true) + { + throw new ErrorResponseException("Нельзя установить точку останова на модифицированный файл."); + } + + Debug.Assert(request.Arguments.Source.Path != null, "request.Arguments.Source.Path != null"); + var path = ToNativePath(request.Arguments.Source.Path); + + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + // vscode иногда передает путь, где диск - маленькая буква + path = Utilities.NormalizeDriveLetter(path); + } + + var useConditions = _debuggee.ProtocolVersion >= ProtocolVersions.Version2; + + Debug.Assert(request.Arguments.Breakpoints != null, "request.Arguments.Breakpoints != null"); + var breaks = request.Arguments.Breakpoints + .Select(srcBreakpoint => new OneScript.DebugProtocol.Breakpoint + { + Line = srcBreakpoint.Line, + Source = path, + Condition = useConditions ? srcBreakpoint.Condition ?? string.Empty : string.Empty + }).ToList(); + + var confirmedBreaks = _debuggee.SetBreakpoints(breaks); + var confirmedDapBreaks = new List(confirmedBreaks.Length); + confirmedDapBreaks.AddRange(confirmedBreaks + .Select(t => new Breakpoint + { + Line = t.Line, + Verified = true + }) + ); + + return Task.FromResult(new SetBreakpointsResponse + { + Body = new SetBreakpointsResponseBody + { + Breakpoints = confirmedDapBreaks.ToArray(), + } + }); + } + + public override async Task LaunchAsync(LaunchRequest request, CancellationToken ct) + { + try + { + Log.LogDebug("Initializing process settings"); + var pathStrategy = new PathHandlingStrategy + { + ClientLinesStartAt1 = Client.LinesStartAt1, + ClientPathsAreUri = Client.PathFormat == "uri", + DebuggerLinesStartAt1 = true, + DebuggerPathsAreUri = false + }; + + _debuggee = DebugeeFactory.CreateProcess(Client.AdapterId, pathStrategy); + _debuggee.Init(request.Arguments); + } + catch (InvalidDebugeeOptionsException e) + { + Log.LogError(e, "Wrong options received {ErrorCode}: {Message}", e.ErrorCode, e.Message); + throw new ErrorResponseException(e.Message); + } + + SubscribeForDebuggeeProcessEvents(); + + try + { + Log.LogDebug("Starting debuggee"); + _debuggee.Start(); + Log.LogInformation("Debuggee started"); + } + catch (Exception e) + { + Log.LogError(e, "Can't launch debuggee"); + throw new ErrorResponseException($"Can't launch debuggee ({e.Message})."); + } + + DebugClientFactory debugClientFactory; + try + { + debugClientFactory = ConnectDebugServer(); + } + catch (Exception e) + { + _debuggee.Kill(); + await EventsChannel.SendEventAsync(new TerminatedEvent(), ct); + Log.LogError(e, "Can't connect to debug server"); + throw new ErrorResponseException("Can't connect: " + e.ToString()); + } + + _debuggee.SetClient(debugClientFactory.CreateDebugClient()); + + return new LaunchResponse(); + } + + private void SubscribeForDebuggeeProcessEvents() + { + _debuggee.OutputReceived += (s, e) => + { + Log.LogDebug("Output received {Output}", e.Content); + + if (string.IsNullOrEmpty(e.Content)) + return; + + var data = e.Content; + if (data[data.Length - 1] != '\n') + { + data += '\n'; + } + + EventsChannel.SendEventAsync(new OutputEvent + { + Body = new OutputEventBody + { + Category = e.Category, + Output = data + } + }); + }; + + _debuggee.ProcessExited += (s, e) => + { + Log.LogInformation("Debuggee has exited"); + EventsChannel.SendEventAsync(new TerminatedEvent()); + }; + } + + private DebugClientFactory ConnectDebugServer() + { + var tcpConnection = ConnectionFactory.Connect(_debuggee.DebugPort); + var listener = new OscriptDebugEventsListener(EventsChannel, _threadState); + return new DebugClientFactory(tcpConnection, listener); + } + } +} \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/OscriptDebugEventsListener.cs b/src/VSCode.DebugAdapter/OscriptDebugEventsListener.cs index 1fa55e419..d583596db 100644 --- a/src/VSCode.DebugAdapter/OscriptDebugEventsListener.cs +++ b/src/VSCode.DebugAdapter/OscriptDebugEventsListener.cs @@ -1,25 +1,26 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System.Runtime.CompilerServices; +using EvilBeaver.DAP.Dto.Events; +using EvilBeaver.DAP.Server; using OneScript.DebugProtocol; using Serilog; -using VSCodeDebug; namespace VSCode.DebugAdapter { public class OscriptDebugEventsListener : IDebugEventListener { - private readonly DebugSession _session; + private readonly IClientChannel _channel; private readonly ThreadStateContainer _threadState; private readonly ILogger Log = Serilog.Log.ForContext(); - public OscriptDebugEventsListener(DebugSession session, ThreadStateContainer threadState) + public OscriptDebugEventsListener(IClientChannel channel, ThreadStateContainer threadState) { - _session = session; + _channel = channel; _threadState = threadState; } @@ -27,7 +28,15 @@ public void ThreadStopped(int threadId, ThreadStopReason reason) { LogEventOccured(); _threadState.Reset(); - _session.SendEvent(new StoppedEvent(threadId, reason.ToString())); + _channel.SendEventAsync(new StoppedEvent + { + Body = new StoppedEventBody + { + ThreadId = threadId, + Reason = reason.ToString(), + AllThreadsStopped = true + } + }); } public void ThreadStoppedEx(int threadId, ThreadStopReason reason, string errorMessage) @@ -38,13 +47,27 @@ public void ThreadStoppedEx(int threadId, ThreadStopReason reason, string errorM if (!string.IsNullOrEmpty(errorMessage)) SendOutput("stderr", errorMessage); - _session.SendEvent(new StoppedEvent(threadId, reason.ToString())); + _channel.SendEventAsync(new StoppedEvent + { + Body = new StoppedEventBody + { + ThreadId = threadId, + Reason = reason.ToString(), + AllThreadsStopped = true + } + }); } public void ProcessExited(int exitCode) { LogEventOccured(); - _session.SendEvent(new ExitedEvent(exitCode)); + _channel.SendEventAsync(new ExitedEvent + { + Body = new ExitedEventBody + { + ExitCode = exitCode + } + }); } private void SendOutput(string category, string data) @@ -55,7 +78,14 @@ private void SendOutput(string category, string data) { data += '\n'; } - _session.SendEvent(new OutputEvent(category, data)); + _channel.SendEventAsync(new OutputEvent + { + Body = new OutputEventBody + { + Category = category, + Output = data + } + }); } } diff --git a/src/VSCode.DebugAdapter/Program.cs b/src/VSCode.DebugAdapter/Program.cs index da026d34d..0966206d7 100644 --- a/src/VSCode.DebugAdapter/Program.cs +++ b/src/VSCode.DebugAdapter/Program.cs @@ -9,7 +9,10 @@ This Source Code Form is subject to the terms of the using System.IO; using System.Linq; using System.Net.Sockets; +using EvilBeaver.DAP.Server; +using EvilBeaver.DAP.Server.Transport; using Serilog; +using Serilog.Extensions.Logging; namespace VSCode.DebugAdapter { @@ -47,6 +50,11 @@ private static void StartSession(Stream input, Stream output) .WriteTo.File(file, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] {Message:lj} ({SourceContext}){NewLine}{Exception}") .CreateLogger(); } + + var factory = new SerilogLoggerFactory(Log.Logger); + var adapter = new OneScriptDebugAdapter(factory.CreateLogger("OneScriptDebugAdapter")); + var dapServer = new DapServer(new BufferedTransport(input, output), adapter, factory); + var session = new OscriptDebugSession(); diff --git a/src/VSCode.DebugAdapter/ProtocolExtensions.cs b/src/VSCode.DebugAdapter/ProtocolExtensions.cs index 21fc5fbfb..c013b2bcd 100644 --- a/src/VSCode.DebugAdapter/ProtocolExtensions.cs +++ b/src/VSCode.DebugAdapter/ProtocolExtensions.cs @@ -5,7 +5,8 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using VSCodeDebug; +using System.IO; +using EvilBeaver.DAP.Dto.Types; using StackFrame = OneScript.DebugProtocol.StackFrame; namespace VSCode.DebugAdapter @@ -23,14 +24,19 @@ public static Source GetSource(this StackFrame frame) { if (frame.IsStringModule()) { - return new Source(frame.Source, null) + return new Source { - origin = frame.Source, - presentationHint = "deemphasize" + Name = frame.Source, + Origin = frame.Source, + PresentationHint = "deemphasize" }; } - return new Source(frame.Source); + return new Source + { + Name = Path.GetFileName(frame.Source), + Path = frame.Source + }; } } -} \ No newline at end of file +} diff --git a/src/VSCode.DebugAdapter/Utilities.cs b/src/VSCode.DebugAdapter/Utilities.cs index a98912f4b..e628ceb27 100644 --- a/src/VSCode.DebugAdapter/Utilities.cs +++ b/src/VSCode.DebugAdapter/Utilities.cs @@ -6,6 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; using System.Collections.Generic; +using System.IO; using System.Reflection; using System.Text; using System.Text.RegularExpressions; @@ -66,5 +67,14 @@ public static Encoding GetEncodingFromOptions(string optionsValue) return Encoding.GetEncoding(optionsValue); } + + public static string NormalizeDriveLetter(string path) + { + if (Path.IsPathRooted(path)) + return path[0].ToString().ToUpperInvariant() + path.Substring(1); + else + return path; + + } } } diff --git a/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj b/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj index 7ca054e8a..d342b399d 100644 --- a/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj +++ b/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj @@ -22,9 +22,10 @@ + - + @@ -43,4 +44,9 @@ + + + + + \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/WorkspaceMapper.cs b/src/VSCode.DebugAdapter/WorkspaceMapper.cs index e9d60018a..d16590507 100644 --- a/src/VSCode.DebugAdapter/WorkspaceMapper.cs +++ b/src/VSCode.DebugAdapter/WorkspaceMapper.cs @@ -12,7 +12,6 @@ namespace VSCode.DebugAdapter { public class WorkspaceMapper { - private Workspace _localWorkspace; private Workspace _remoteWorkspace; From 8d06083db6f10e4a9de636e6b0771a874a36d1fd Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Tue, 24 Mar 2026 22:26:34 +0300 Subject: [PATCH 51/52] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=BE=D1=81=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BD=D0=BE=D0=B2=D1=83=D1=8E=20=D0=B1=D0=B8?= =?UTF-8?q?=D0=B1=D0=BB=D0=B8=D0=BE=D1=82=D0=B5=D0=BA=D1=83=20=D1=81=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BC=D0=BE=D1=89=D1=8C=D1=8E=20=D0=98=D0=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/agents/coder.md | 7 + .cursor/agents/planner.md | 25 + src/VSCode.DebugAdapter/AttachOptions.cs | 15 + src/VSCode.DebugAdapter/DebugSession.cs | 502 ----------------- src/VSCode.DebugAdapter/DebugeeFactory.cs | 8 + .../OneScriptDebugAdapter.cs | 311 ++++++++++- .../OscriptDebugSession.cs | 524 ------------------ src/VSCode.DebugAdapter/Program.cs | 7 +- src/VSCode.DebugAdapter/Protocol.cs | 270 --------- 9 files changed, 365 insertions(+), 1304 deletions(-) create mode 100644 .cursor/agents/coder.md create mode 100644 .cursor/agents/planner.md create mode 100644 src/VSCode.DebugAdapter/AttachOptions.cs delete mode 100644 src/VSCode.DebugAdapter/DebugSession.cs delete mode 100644 src/VSCode.DebugAdapter/OscriptDebugSession.cs delete mode 100644 src/VSCode.DebugAdapter/Protocol.cs diff --git a/.cursor/agents/coder.md b/.cursor/agents/coder.md new file mode 100644 index 000000000..8e2381233 --- /dev/null +++ b/.cursor/agents/coder.md @@ -0,0 +1,7 @@ +--- +name: coder +model: claude-4.6-sonnet-medium +description: Use this agent to implement coding tasks by provided specs only when it's explicitly specified +--- + +Ты профессиональный разработчик ПО высокого уровня. Тебе поступает на вход файл со спецификацией задачи. Реализуй эту задачу. \ No newline at end of file diff --git a/.cursor/agents/planner.md b/.cursor/agents/planner.md new file mode 100644 index 000000000..ba817997c --- /dev/null +++ b/.cursor/agents/planner.md @@ -0,0 +1,25 @@ +--- +name: planner +model: gemini-3-flash +description: Use this agent only when explicitly specified to sequentially run coding subagents when you have numbered list of tasks +readonly: true +--- + +На вход тебе подается каталог, в котором лежат файлы .md со списком задач и файл main-task со спецификацией задачи в целом. +Файлы подзадач пронумерованы префиксом вида 01, 02 и т.д. + +Твоя задача - запустить субагент сoder, передав ему очередную задачу по порядку, дождаться изменений от субагента и запустить новый субагент, уже со следующей задачей из перечня. Ты сам не должен писать код, ты находишься в Ask Mode, твоя задача запускать субагент coder, передавая ему очередную задачу (файл с описанием) + +И так до тех пор, пока перечень задач не будет выполнен целиком. + +Если какая-то задача не может быть выполнена и субагент выдает ошибку или вопрос - прерывай цикл выполнения, задай мне вопрос или сообщи текст ошибки и жди дальнейших инструкций. + +Итого, твои действия: + +1. изучить конечную цель (файл main-task) +2. взять файл с описанием задачи 01-* и сформулировать задачу агенту coder по одному и только одному файлу плана. +3. запустить агент coder, передав ему весь нужный контекст +4. получить ответ от агента coder и если он выполнил задачу успешно, перейти к следующему файлу с задачей (02-*) +5. если агент не смог выполнить задачу - прервать цикл +6. продолжать брать следующие по порядку задачи и снова запускать агент coder, передавая ему только одну задачу из файлов-планов по порядку +7. когда все файлы задач будут реализованы - завершиться. \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/AttachOptions.cs b/src/VSCode.DebugAdapter/AttachOptions.cs new file mode 100644 index 000000000..cd9c0c57f --- /dev/null +++ b/src/VSCode.DebugAdapter/AttachOptions.cs @@ -0,0 +1,15 @@ +// /*---------------------------------------------------------- +// This Source Code Form is subject to the terms of the +// Mozilla Public License, v.2.0. If a copy of the MPL +// was not distributed with this file, You can obtain one +// at http://mozilla.org/MPL/2.0/. +// ----------------------------------------------------------*/ + +namespace VSCode.DebugAdapter +{ + internal class AttachOptions + { + public int DebugPort { get; set; } = 2801; + public WorkspaceMapper PathsMapping { get; set; } + } +} \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/DebugSession.cs b/src/VSCode.DebugAdapter/DebugSession.cs deleted file mode 100644 index ff119d2d1..000000000 --- a/src/VSCode.DebugAdapter/DebugSession.cs +++ /dev/null @@ -1,502 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.IO; -using VSCode.DebugAdapter; - -namespace VSCodeDebug -{ - // ---- Types ------------------------------------------------------------------------- - - public class Message { - public int id { get; } - public string format { get; } - public dynamic variables { get; } - public dynamic showUser { get; } - public dynamic sendTelemetry { get; } - - public Message(int id, string format, dynamic variables = null, bool user = true, bool telemetry = false) { - this.id = id; - this.format = format; - this.variables = variables; - this.showUser = user; - this.sendTelemetry = telemetry; - } - } - - public class StackFrame - { - public int id { get; } - public Source source { get; } - public int line { get; } - public int column { get; } - public string name { get; } - - public StackFrame(int id, string name, Source source, int line, int column) { - this.id = id; - this.name = name; - this.source = source; - this.line = line; - this.column = column; - } - } - - public class Scope - { - public string name { get; } - public int variablesReference { get; } - public bool expensive { get; } - - public Scope(string name, int variablesReference, bool expensive = false) { - this.name = name; - this.variablesReference = variablesReference; - this.expensive = expensive; - } - } - - public class Variable - { - public string name { get; } - public string value { get; } - public string type { get; } - public int variablesReference { get; } - - public Variable(string name, string value, string type, int variablesReference = 0) { - this.name = name; - this.value = value; - this.type = type; - this.variablesReference = variablesReference; - } - } - - public class Thread - { - public int id { get; } - public string name { get; } - - public Thread(int id, string name) { - this.id = id; - if (name == null || name.Length == 0) { - this.name = string.Format("Thread #{0}", id); - } - else { - this.name = name; - } - } - } - - public class Source - { - public string name { get; } - public string path { get; } - public int sourceReference { get; } - - public string origin { get; set; } - - public string presentationHint { get; set; } - - public Source(string name, string path, int sourceReference = 0) { - this.name = name; - this.path = path; - this.sourceReference = sourceReference; - } - - public Source(string path, int sourceReference = 0) { - this.name = Path.GetFileName(path); - this.path = path; - this.sourceReference = sourceReference; - } - } - - public class Breakpoint - { - public bool verified { get; } - public int line { get; } - - public Breakpoint(bool verified) - { - this.verified = verified; - line = 0; - } - - public Breakpoint(bool verified, int line) { - this.verified = verified; - this.line = line; - } - } - - // ---- Events ------------------------------------------------------------------------- - - public class InitializedEvent : Event - { - public InitializedEvent() - : base("initialized") { } - } - - public class StoppedEvent : Event - { - public StoppedEvent(int tid, string reasn, string txt = null) - : base("stopped", new { - threadId = tid, - reason = reasn, - text = txt - }) { } - } - - public class ExitedEvent : Event - { - public ExitedEvent(int exCode) - : base("exited", new { exitCode = exCode } ) { } - } - - public class TerminatedEvent : Event - { - public TerminatedEvent() - : base("terminated") { } - } - - public class ThreadEvent : Event - { - public ThreadEvent(string reasn, int tid) - : base("thread", new { - reason = reasn, - threadId = tid - }) { } - } - - public class OutputEvent : Event - { - public OutputEvent(string cat, string outpt) - : base("output", new { - category = cat, - output = outpt - }) { } - } - - // ---- Response ------------------------------------------------------------------------- - - public class Capabilities : ResponseBody { - - public bool supportsConfigurationDoneRequest; - public bool supportsFunctionBreakpoints; - public bool supportsConditionalBreakpoints; - public bool supportsEvaluateForHovers; - public bool supportsExceptionFilterOptions; - public dynamic[] exceptionBreakpointFilters; - public bool supportTerminateDebuggee; - } - - public class ErrorResponseBody : ResponseBody { - - public Message error { get; } - - public ErrorResponseBody(Message error) { - this.error = error; - } - } - - public class StackTraceResponseBody : ResponseBody - { - public StackFrame[] stackFrames { get; } - - public StackTraceResponseBody(IEnumerable frames = null) { - if (frames == null) - stackFrames = new StackFrame[0]; - else - stackFrames = frames.ToArray(); - } - } - - public class ScopesResponseBody : ResponseBody - { - public Scope[] scopes { get; } - - public ScopesResponseBody(IList scps = null) { - if (scps == null) - scopes = new Scope[0]; - else - scopes = scps.ToArray(); - } - } - - public class VariablesResponseBody : ResponseBody - { - public Variable[] variables { get; } - - public VariablesResponseBody(IList vars = null) { - if (vars == null) - variables = new Variable[0]; - else - variables = vars.ToArray(); - } - } - - public class ThreadsResponseBody : ResponseBody - { - public Thread[] threads { get; } - - public ThreadsResponseBody(List vars = null) { - if (vars == null) - threads = new Thread[0]; - else - threads = vars.ToArray(); - } - } - - public class EvaluateResponseBody : ResponseBody - { - public string result { get; } - - public string type { get; set; } - - public int variablesReference { get; } - - - public EvaluateResponseBody(string value, int reff = 0) { - result = value; - variablesReference = reff; - } - } - - public class SetBreakpointsResponseBody : ResponseBody - { - public Breakpoint[] breakpoints { get; } - - public SetBreakpointsResponseBody(List bpts = null) { - if (bpts == null) - breakpoints = Array.Empty(); - else - breakpoints = bpts.ToArray(); - } - } - - public class SetExceptionBreakpointsResponseBody : ResponseBody - { - public Breakpoint[] breakpoints { get; } - - public SetExceptionBreakpointsResponseBody(List bpts = null) - { - if (bpts == null) - breakpoints = Array.Empty(); - else - breakpoints = bpts.ToArray(); - } - } - - // ---- The Session -------------------------------------------------------- - - public abstract class DebugSession : ProtocolServer - { - private bool _clientLinesStartAt1 = true; - private bool _clientPathsAreUri = true; - - public PathHandlingStrategy PathStrategy { get; } - - public DebugSession(bool debuggerLinesStartAt1, bool debuggerPathsAreURI = false) - { - PathStrategy = new PathHandlingStrategy - { - DebuggerLinesStartAt1 = debuggerLinesStartAt1, - DebuggerPathsAreUri = debuggerPathsAreURI - }; - } - - public void SendResponse(Response response, dynamic body = null) - { - if (body != null) { - response.SetBody(body); - } - SendMessage(response); - } - - public void SendErrorResponse(Response response, int id, string format, dynamic arguments = null, bool user = true, bool telemetry = false) - { - var msg = new Message(id, format, arguments, user, telemetry); - var message = Utilities.ExpandVariables(msg.format, msg.variables); - response.SetErrorBody(message, new ErrorResponseBody(msg)); - SendMessage(response); - } - - protected override void DispatchRequest(string command, dynamic args, Response response) - { - if (args == null) { - args = new { }; - } - - try { - switch (command) { - - case "initialize": - if (args.linesStartAt1 != null) { - _clientLinesStartAt1 = (bool)args.linesStartAt1; - } - var pathFormat = (string)args.pathFormat; - if (pathFormat != null) { - switch (pathFormat) { - case "uri": - _clientPathsAreUri = true; - break; - case "path": - _clientPathsAreUri = false; - break; - default: - SendErrorResponse(response, 1015, "initialize: bad value '{_format}' for pathFormat", new { _format = pathFormat }); - return; - } - } - - SetPathStrategy(); - Initialize(response, args); - break; - - case "launch": - Launch(response, args); - break; - - case "attach": - Attach(response, args); - break; - - case "disconnect": - Disconnect(response, args); - break; - - case "next": - Next(response, args); - break; - - case "continue": - Continue(response, args); - break; - - case "stepIn": - StepIn(response, args); - break; - - case "stepOut": - StepOut(response, args); - break; - - case "pause": - Pause(response, args); - break; - - case "stackTrace": - StackTrace(response, args); - break; - - case "scopes": - Scopes(response, args); - break; - - case "variables": - Variables(response, args); - break; - - case "source": - Source(response, args); - break; - - case "threads": - Threads(response, args); - break; - - case "setBreakpoints": - SetBreakpoints(response, args); - break; - - case "setFunctionBreakpoints": - SetFunctionBreakpoints(response, args); - break; - - case "setExceptionBreakpoints": - SetExceptionBreakpoints(response, args); - break; - - case "evaluate": - Evaluate(response, args); - break; - case "configurationDone": - ConfigurationDone(response, args); - break; - - default: - SendErrorResponse(response, 1014, "unrecognized request: {_request}", new { _request = command }); - break; - } - } - catch (Exception e) { - OnRequestError(e); - SendErrorResponse(response, 1104, "error while processing request '{_request}' (exception: {_exception})", new { _request = command, _exception = e.Message }); - } - - if (command == "disconnect") { - Stop(); - } - } - - private void SetPathStrategy() - { - PathStrategy.ClientLinesStartAt1 = _clientLinesStartAt1; - PathStrategy.ClientPathsAreUri = _clientPathsAreUri; - } - - protected string ConvertClientPathToDebugger(string path) - { - return PathStrategy.ConvertClientPathToDebugger(path); - } - - public abstract void ConfigurationDone(Response response, dynamic args); - - public abstract void Initialize(Response response, dynamic args); - - public abstract void Launch(Response response, dynamic arguments); - - public abstract void Attach(Response response, dynamic arguments); - - public abstract void Disconnect(Response response, dynamic arguments); - - public virtual void SetFunctionBreakpoints(Response response, dynamic arguments) - { - } - - public virtual void SetExceptionBreakpoints(Response response, dynamic arguments) - { - } - - protected virtual void OnRequestError(Exception e) - { - } - - public abstract void SetBreakpoints(Response response, dynamic arguments); - - public abstract void Continue(Response response, dynamic arguments); - - public abstract void Next(Response response, dynamic arguments); - - public abstract void StepIn(Response response, dynamic arguments); - - public abstract void StepOut(Response response, dynamic arguments); - - public abstract void Pause(Response response, dynamic arguments); - - public abstract void StackTrace(Response response, dynamic arguments); - - public abstract void Scopes(Response response, dynamic arguments); - - public abstract void Variables(Response response, dynamic arguments); - - public virtual void Source(Response response, dynamic arguments) - { - SendErrorResponse(response, 1020, "Source not supported"); - } - - public abstract void Threads(Response response, dynamic arguments); - - public abstract void Evaluate(Response response, dynamic arguments); - } -} diff --git a/src/VSCode.DebugAdapter/DebugeeFactory.cs b/src/VSCode.DebugAdapter/DebugeeFactory.cs index d494b21fd..f25d51387 100644 --- a/src/VSCode.DebugAdapter/DebugeeFactory.cs +++ b/src/VSCode.DebugAdapter/DebugeeFactory.cs @@ -25,5 +25,13 @@ public static DebugeeProcess CreateProcess(string adapterId, PathHandlingStrateg throw new ArgumentOutOfRangeException(nameof(adapterId), adapterId, "Unsupported debugger"); } + + public static DebugeeProcess CreateAttachableProcess(string adapterId, PathHandlingStrategy pathStrategy) + { + // Для аттача нам не важно, консольный это процесс или веб-сервер, + // так как мы не запускаем его, а только подключаемся. + // Но нам нужен экземпляр DebugeeProcess. ConsoleProcess подойдет. + return new ConsoleProcess(pathStrategy); + } } } \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs b/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs index e2c807f1a..5500d4898 100644 --- a/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs +++ b/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs @@ -11,7 +11,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using EvilBeaver.DAP.Dto.Base; using EvilBeaver.DAP.Dto.Events; using EvilBeaver.DAP.Dto.Requests; using EvilBeaver.DAP.Dto.Serialization; @@ -25,8 +24,7 @@ namespace VSCode.DebugAdapter internal class OneScriptDebugAdapter : DebugAdapterBase { private DebugeeProcess _debuggee; - private bool _startupPerformed = false; - private ThreadStateContainer _threadState = new ThreadStateContainer(); + private readonly ThreadStateContainer _threadState = new ThreadStateContainer(); private ILogger Log { get; } @@ -72,6 +70,313 @@ protected override async Task OnInitializeAsync(InitializeRe }; } + public override Task ConfigurationDoneAsync(ConfigurationDoneRequest request, CancellationToken ct) + { + if (_debuggee == null) + { + Log.LogDebug("Config Done. Process is not started"); + return Task.FromResult(new ConfigurationDoneResponse()); + } + + Log.LogDebug("Config Done. Process is started, sending Execute"); + _debuggee.BeginExecution(-1); + + return Task.FromResult(new ConfigurationDoneResponse()); + } + + public override Task DisconnectAsync(DisconnectRequest request, CancellationToken ct) + { + Log.LogDebug("Disconnect requested, terminate={Terminate}", request.Arguments?.TerminateDebuggee); + bool terminateDebuggee = request.Arguments?.TerminateDebuggee == true; + + _debuggee?.HandleDisconnect(terminateDebuggee); + + return Task.FromResult(new DisconnectResponse()); + } + + public override Task ContinueAsync(ContinueRequest request, CancellationToken ct) + { + _debuggee.BeginExecution(-1); + return Task.FromResult(new ContinueResponse()); + } + + public override Task NextAsync(NextRequest request, CancellationToken ct) + { + lock (_debuggee) + { + if (!_debuggee.HasExited) + _debuggee.Next(request.Arguments.ThreadId); + } + return Task.FromResult(new NextResponse()); + } + + public override Task StepInAsync(StepInRequest request, CancellationToken ct) + { + lock (_debuggee) + { + if (!_debuggee.HasExited) + _debuggee.StepIn(request.Arguments.ThreadId); + } + return Task.FromResult(new StepInResponse()); + } + + public override Task StepOutAsync(StepOutRequest request, CancellationToken ct) + { + lock (_debuggee) + { + if (!_debuggee.HasExited) + _debuggee.StepOut(request.Arguments.ThreadId); + } + return Task.FromResult(new StepOutResponse()); + } + + public override Task ThreadsAsync(ThreadsRequest request, CancellationToken ct) + { + var processThreads = _debuggee.GetThreads(); + var threads = new EvilBeaver.DAP.Dto.Types.Thread[processThreads.Length]; + for (int i = 0; i < processThreads.Length; i++) + { + threads[i] = new EvilBeaver.DAP.Dto.Types.Thread + { + Id = processThreads[i], + Name = $"Thread {processThreads[i]}" + }; + } + + return Task.FromResult(new ThreadsResponse + { + Body = new ThreadsResponseBody { Threads = threads } + }); + } + + public override Task StackTraceAsync(StackTraceRequest request, CancellationToken ct) + { + var args = request.Arguments; + var firstFrameIdx = args.StartFrame ?? 0; + var limit = args.Levels ?? 0; + var threadId = args.ThreadId; + + var processFrames = _debuggee.GetStackTrace(threadId, firstFrameIdx, limit); + var frames = new EvilBeaver.DAP.Dto.Types.StackFrame[processFrames.Length]; + for (int i = 0; i < processFrames.Length; i++) + { + frames[i] = new EvilBeaver.DAP.Dto.Types.StackFrame + { + Id = _threadState.RegisterFrame(processFrames[i]), + Name = processFrames[i].MethodName, + Source = processFrames[i].GetSource(), + Line = processFrames[i].LineNumber, + Column = 0 + }; + } + + return Task.FromResult(new StackTraceResponse + { + Body = new StackTraceResponseBody + { + StackFrames = frames, + TotalFrames = frames.Length + } + }); + } + + public override Task ScopesAsync(ScopesRequest request, CancellationToken ct) + { + int frameId = request.Arguments.FrameId; + var frame = _threadState.GetFrameById(frameId); + if (frame == null) + { + throw new ErrorResponseException("No active stackframe"); + } + + var scopes = new List(); + + var localProvider = new LocalScopeProvider(frame.ThreadId, frame.Index); + var localHandle = _threadState.RegisterVariablesProvider(localProvider); + scopes.Add(new Scope + { + Name = "Локальные переменные", + VariablesReference = localHandle + }); + + if (_debuggee.ProtocolVersion >= ProtocolVersions.Version4) + { + var moduleProvider = new ModuleScopeProvider(frame.ThreadId, frame.Index); + var moduleHandle = _threadState.RegisterVariablesProvider(moduleProvider); + scopes.Add(new Scope + { + Name = "Переменные модуля", + VariablesReference = moduleHandle + }); + } + + return Task.FromResult(new ScopesResponse + { + Body = new ScopesResponseBody { Scopes = scopes.ToArray() } + }); + } + + public override Task VariablesAsync(VariablesRequest request, CancellationToken ct) + { + int varsHandle = request.Arguments.VariablesReference; + var provider = _threadState.GetVariablesProviderById(varsHandle); + if (provider == null) + { + throw new ErrorResponseException("Invalid variables reference"); + } + + var variables = _debuggee.FetchVariables(provider); + var responseArray = new Variable[variables.Length]; + + for (int i = 0; i < responseArray.Length; i++) + { + var variable = variables[i]; + int childHandle = 0; + + if (variable.IsStructured) + { + var childProvider = provider.CreateChildProvider(i); + childHandle = _threadState.RegisterVariablesProvider(childProvider); + } + + responseArray[i] = new Variable + { + Name = variable.Name, + Value = variable.Presentation, + Type = variable.TypeName, + VariablesReference = childHandle + }; + } + + return Task.FromResult(new VariablesResponse + { + Body = new VariablesResponseBody { Variables = responseArray } + }); + } + + public override Task EvaluateAsync(EvaluateRequest request, CancellationToken ct) + { + var args = request.Arguments; + int frameId = args.FrameId ?? 0; + var frame = _threadState.GetFrameById(frameId); + if (frame == null) + { + throw new ErrorResponseException("No active stackframe"); + } + + var expression = args.Expression; + var context = args.Context; + + int id = 0; + OneScript.DebugProtocol.Variable evalResult; + try + { + evalResult = _debuggee.Evaluate(frame, expression); + if (evalResult.IsStructured) + { + var provider = new EvaluatedExpressionProvider(expression, frame.ThreadId, frame.Index); + id = _threadState.RegisterVariablesProvider(provider); + } + } + catch (Exception e) + { + evalResult = new OneScript.DebugProtocol.Variable() { Presentation = e.Message, Name = "$evalFault" }; + } + + if (evalResult.Name.Equals("$evalFault") && "hover".Equals(context)) + { + evalResult.Presentation = $"err: {expression}"; + } + + return Task.FromResult(new EvaluateResponse + { + Body = new EvaluateResponseBody + { + Result = evalResult.Presentation, + Type = evalResult.TypeName, + VariablesReference = id + } + }); + } + + public override Task SetExceptionBreakpointsAsync(SetExceptionBreakpointsRequest request, CancellationToken ct) + { + var args = request.Arguments; + var filters = new List<(string Id, string Condition)>(); + var acceptedFilters = new List(); + + if (args.Filters != null) + { + foreach (var filter in args.Filters) + { + filters.Add((filter, "")); + acceptedFilters.Add(new Breakpoint { Verified = true }); + } + } + + if (args.FilterOptions != null) + { + foreach (var filterOption in args.FilterOptions) + { + filters.Add((filterOption.FilterId, filterOption.Condition ?? "")); + acceptedFilters.Add(new Breakpoint { Verified = true }); + } + } + + _debuggee.SetExceptionsBreakpoints(filters.ToArray()); + + return Task.FromResult(new SetExceptionBreakpointsResponse + { + Body = new SetExceptionBreakpointsResponseBody + { + Breakpoints = acceptedFilters.ToArray() + } + }); + } + + public override Task AttachAsync(AttachRequest request, CancellationToken ct) + { + var options = request.Arguments.DeserializeAdditionalProperties(); + + var pathStrategy = new PathHandlingStrategy + { + ClientLinesStartAt1 = Client.LinesStartAt1, + ClientPathsAreUri = Client.PathFormat == "uri", + DebuggerLinesStartAt1 = true, + DebuggerPathsAreUri = false + }; + + _debuggee = DebugeeFactory.CreateAttachableProcess(Client.AdapterId, pathStrategy); + _debuggee.DebugPort = options.DebugPort; + _debuggee.PathsMapper = options.PathsMapping; + + SubscribeForDebuggeeProcessEvents(); + + DebugClientFactory debugClientFactory; + try + { + debugClientFactory = ConnectDebugServer(); + } + catch (Exception e) + { + Log.LogError(e, "Can't connect debuggee"); + throw new ErrorResponseException("Can't connect: " + e.ToString()); + } + + _debuggee.SetClient(debugClientFactory.CreateDebugClient()); + try + { + _debuggee.InitAttached(); + } + catch (Exception e) + { + Log.LogError(e, "Attach failed"); + throw new ErrorResponseException("Attach failed: " + e.ToString()); + } + + return Task.FromResult(new AttachResponse()); + } + public override Task SetBreakpointsAsync(SetBreakpointsRequest request, CancellationToken ct) { if (request.Arguments.SourceModified == true) diff --git a/src/VSCode.DebugAdapter/OscriptDebugSession.cs b/src/VSCode.DebugAdapter/OscriptDebugSession.cs deleted file mode 100644 index 664e4a3e4..000000000 --- a/src/VSCode.DebugAdapter/OscriptDebugSession.cs +++ /dev/null @@ -1,524 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.CompilerServices; -using OneScript.DebugProtocol; -using Serilog; -using VSCode.DebugAdapter.Transport; -using VSCodeDebug; - - -namespace VSCode.DebugAdapter -{ - internal class OscriptDebugSession : DebugSession - { - private DebugeeProcess _debuggee; - private bool _startupPerformed = false; - private ThreadStateContainer _threadState = new ThreadStateContainer(); - - private readonly ILogger Log = Serilog.Log.ForContext(); - - public OscriptDebugSession() : base(true, false) - { - } - - private string AdapterID { get; set; } - - public override void Initialize(Response response, dynamic args) - { - LogCommandReceived(); - AdapterID = (string) args.adapterID; - - _debuggee = DebugeeFactory.CreateProcess(AdapterID, PathStrategy); - - SendResponse(response, new Capabilities - { - supportsConditionalBreakpoints = true, - supportsFunctionBreakpoints = false, - supportsConfigurationDoneRequest = true, - supportsExceptionFilterOptions = true, - exceptionBreakpointFilters = new dynamic[] - { - new - { - filter = "uncaught", - label = "Необработанные исключения", - description = "Остановка при возникновении необработанного исключения", - supportsCondition = true, - conditionDescription = "Искомая подстрока текста исключения" - }, - new - { - filter = "all", - label = "Все исключения", - description = "Остановка при возникновении любого исключения", - supportsCondition = true, - conditionDescription = "Искомая подстрока текста исключения" - } - }, - supportsEvaluateForHovers = true, - supportTerminateDebuggee = true - }); - - SendEvent(new InitializedEvent()); - } - - public override void Launch(Response response, dynamic args) - { - LogCommandReceived(); - try - { - Log.Debug("Initializing process settings"); - _debuggee.Init(args); - } - catch (InvalidDebugeeOptionsException e) - { - Log.Error(e, "Wrong options received {ErrorCode}: {Message}", e.ErrorCode, e.Message); - SendErrorResponse(response, e.ErrorCode, e.Message); - return; - } - - SubscribeForDebuggeeProcessEvents(); - - try - { - Log.Verbose("Starting debuggee"); - _debuggee.Start(); - Log.Information("Debuggee started"); - } - catch (Exception e) - { - Log.Error(e, "Can't launch debuggee"); - SendErrorResponse(response, 3012, "Can't launch debugee ({reason}).", new { reason = e.Message }); - return; - } - - DebugClientFactory debugClientFactory; - try - { - debugClientFactory = ConnectDebugServer(); - } - catch (Exception e) - { - _debuggee.Kill(); - SendEvent(new TerminatedEvent()); - Log.Error(e, "Can't connect to debug server"); - SendErrorResponse(response, 4550, "Can't connect: " + e.ToString()); - return; - } - - _debuggee.SetClient(debugClientFactory.CreateDebugClient()); - - SendResponse(response); - } - - private void SubscribeForDebuggeeProcessEvents() - { - _debuggee.OutputReceived += (s, e) => - { - Log.Debug("Output received {Output}", e.Content); - SendOutput(e.Category, e.Content); - }; - - _debuggee.ProcessExited += (s, e) => - { - Log.Information("Debuggee has exited"); - SendEvent(new TerminatedEvent()); - }; - } - - public override void Attach(Response response, dynamic arguments) - { - LogCommandReceived(); - SubscribeForDebuggeeProcessEvents(); - - _debuggee.DebugPort = GetFromContainer(arguments, "debugPort", 2801); - _debuggee.InitPathsMapper(arguments); - - DebugClientFactory debugClientFactory; - try - { - debugClientFactory = ConnectDebugServer(); - } - catch (Exception e) - { - Log.Error(e, "Can't connect debuggee"); - SendErrorResponse(response, 4550, "Can't connect: " + e.ToString()); - return; - } - - _debuggee.SetClient(debugClientFactory.CreateDebugClient()); - try - { - _debuggee.InitAttached(); - } - catch (Exception e) - { - Log.Error(e, "Attach failed"); - SendErrorResponse(response, 4550, "Attach failed: " + e.ToString()); - return; - } - - SendResponse(response); - } - - private DebugClientFactory ConnectDebugServer() - { - var tcpConnection = ConnectionFactory.Connect(_debuggee.DebugPort); - var listener = new OscriptDebugEventsListener(this, _threadState); - return new DebugClientFactory(tcpConnection, listener); - } - - public override void Disconnect(Response response, dynamic arguments) - { - LogCommandReceived(new { Terminate = arguments.terminateDebuggee }); - bool terminateDebuggee = arguments.terminateDebuggee == true; - - _debuggee.HandleDisconnect(terminateDebuggee); - SendResponse(response); - } - - public override void SetExceptionBreakpoints(Response response, dynamic arguments) - { - LogCommandReceived(); - var acceptedFilters = new List(); - var filters = new List<(string Id, string Condition)>(); - - foreach(var filter in arguments.filters) - { - filters.Add((filter, "")); - acceptedFilters.Add(new VSCodeDebug.Breakpoint(true)); - } - - foreach (var filterOption in arguments.filterOptions) - { - filters.Add((filterOption.filterId, filterOption.condition ?? "")); - acceptedFilters.Add(new VSCodeDebug.Breakpoint(true)); - } - - _debuggee.SetExceptionsBreakpoints(filters.ToArray()); - - SendResponse(response, new SetExceptionBreakpointsResponseBody(acceptedFilters)); - } - - public override void SetBreakpoints(Response response, dynamic arguments) - { - LogCommandReceived(); - - if ((bool)arguments.sourceModified) - { - if (_startupPerformed) - { - SendErrorResponse(response, 1102, "Нельзя установить точку останова на модифицированный файл."); - return; - } - SendResponse(response, new SetBreakpointsResponseBody()); - return; - } - - var path = (string) arguments.source.path; - path = ConvertClientPathToDebugger(path); - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - // vscode иногда передает путь, где диск - маленькая буква - path = NormalizeDriveLetter(path); - } - - var breaks = new List(); - - var useConditions = _debuggee.ProtocolVersion >= ProtocolVersions.Version2; - - foreach (var srcBreakpoint in arguments.breakpoints) - { - var bpt = new OneScript.DebugProtocol.Breakpoint - { - Line = (int)srcBreakpoint.line, - Source = path, - Condition = useConditions ? srcBreakpoint.condition ?? string.Empty : string.Empty - }; - breaks.Add(bpt); - } - - if(breaks.Count == 0) // в целях сохранения интерфейса WCF придется сделать костыль на перех. период - { - var bpt = new OneScript.DebugProtocol.Breakpoint - { - Line = 0, - Source = path - }; - breaks.Add(bpt); - } - - var confirmedBreaks = _debuggee.SetBreakpoints(breaks); - var confirmedBreaksVSCode = new List(confirmedBreaks.Length); - for (int i = 0; i < confirmedBreaks.Length; i++) - { - confirmedBreaksVSCode.Add(new VSCodeDebug.Breakpoint(true, confirmedBreaks[i].Line)); - } - - SendResponse(response, new SetBreakpointsResponseBody(confirmedBreaksVSCode)); - } - - private string NormalizeDriveLetter(string path) - { - if (Path.IsPathRooted(path)) - return path[0].ToString().ToUpperInvariant() + path.Substring(1); - else - return path; - - } - - public override void ConfigurationDone(Response response, dynamic args) - { - if (_debuggee == null) - { - Log.Debug("Config Done. Process is not started"); - SendResponse(response); - return; - } - Log.Debug("Config Done. Process is started, sending Execute"); - _debuggee.BeginExecution(-1); - _startupPerformed = true; - SendResponse(response); - } - - public override void Continue(Response response, dynamic arguments) - { - LogCommandReceived(); - SendResponse(response); - _debuggee.BeginExecution(-1); - } - - public override void Next(Response response, dynamic arguments) - { - LogCommandReceived(); - SendResponse(response); - lock (_debuggee) - { - if (!_debuggee.HasExited) - { - _debuggee.Next((int)arguments.threadId); - } - } - - } - - public override void StepIn(Response response, dynamic arguments) - { - LogCommandReceived(); - SendResponse(response); - lock (_debuggee) - { - if (!_debuggee.HasExited) - { - _debuggee.StepIn((int)arguments.threadId); - } - } - } - - public override void StepOut(Response response, dynamic arguments) - { - LogCommandReceived(); - SendResponse(response); - lock (_debuggee) - { - if (!_debuggee.HasExited) - { - _debuggee.StepOut((int)arguments.threadId); - } - } - } - - public override void Pause(Response response, dynamic arguments) - { - LogCommandReceived(); - throw new NotImplementedException(); - } - - public override void StackTrace(Response response, dynamic arguments) - { - LogCommandReceived(); - var firstFrameIdx = (int?)arguments.startFrame ?? 0; - var limit = (int?) arguments.levels ?? 0; - var threadId = (int) arguments.threadId; - var processFrames = _debuggee.GetStackTrace(threadId, firstFrameIdx, limit); - var frames = new VSCodeDebug.StackFrame[processFrames.Length]; - for (int i = 0; i < processFrames.Length; i++) - { - frames[i] = new VSCodeDebug.StackFrame( - _threadState.RegisterFrame(processFrames[i]), - processFrames[i].MethodName, - processFrames[i].GetSource(), - processFrames[i].LineNumber, 0); - } - - SendResponse(response, new StackTraceResponseBody(frames)); - } - - public override void Scopes(Response response, dynamic arguments) - { - LogCommandReceived(); - int frameId = GetFromContainer(arguments, "frameId", 0); - var frame = _threadState.GetFrameById(frameId); - if (frame == null) - { - SendErrorResponse(response, 10001, "No active stackframe"); - return; - } - - var scopes = new List(); - - // Scope 1: Локальные переменные - var localProvider = new LocalScopeProvider(frame.ThreadId, frame.Index); - var localHandle = _threadState.RegisterVariablesProvider(localProvider); - scopes.Add(new Scope("Локальные переменные", localHandle)); - - // Scope 2: Переменные модуля (начиная с протокола версии 4) - if (_debuggee.ProtocolVersion >= ProtocolVersions.Version4) - { - var moduleProvider = new ModuleScopeProvider(frame.ThreadId, frame.Index); - var moduleHandle = _threadState.RegisterVariablesProvider(moduleProvider); - scopes.Add(new Scope("Переменные модуля", moduleHandle)); - } - - SendResponse(response, new ScopesResponseBody(scopes.ToArray())); - } - - public override void Variables(Response response, dynamic arguments) - { - LogCommandReceived(); - int varsHandle = GetFromContainer(arguments, "variablesReference", 0); - var provider = _threadState.GetVariablesProviderById(varsHandle); - if (provider == null) - { - SendErrorResponse(response, 10001, "Invalid variables reference"); - return; - } - - // Получаем переменные через провайдер - var variables = _debuggee.FetchVariables(provider); - var responseArray = new VSCodeDebug.Variable[variables.Length]; - - for (int i = 0; i < responseArray.Length; i++) - { - var variable = variables[i]; - int childHandle = 0; - - if (variable.IsStructured) - { - var childProvider = provider.CreateChildProvider(i); - childHandle = _threadState.RegisterVariablesProvider(childProvider); - } - - responseArray[i] = new VSCodeDebug.Variable( - variable.Name, - variable.Presentation, - variable.TypeName, - childHandle); - } - - SendResponse(response, new VariablesResponseBody(responseArray)); - } - - public override void Threads(Response response, dynamic arguments) - { - LogCommandReceived(); - var threads = new List(); - var processThreads = _debuggee.GetThreads(); - for (int i = 0; i < processThreads.Length; i++) - { - threads.Add(new VSCodeDebug.Thread(processThreads[i], $"Thread {processThreads[i]}")); - } - - SendResponse(response, new ThreadsResponseBody(threads)); - } - - public override void Evaluate(Response response, dynamic arguments) - { - LogCommandReceived(); - // expression, frameId, context - int frameId = GetFromContainer(arguments, "frameId", 0); - var frame = _threadState.GetFrameById(frameId); - if (frame == null) - { - SendErrorResponse(response, 10001, "No active stackframe"); - return; - } - - var expression = (string) arguments.expression; - var context = (string) arguments.context; - - Log.Debug("Evaluate {Expression} in {Context}", expression, context); - - int id = 0; - OneScript.DebugProtocol.Variable evalResult; - try - { - evalResult = _debuggee.Evaluate(frame, expression); - - if (evalResult.IsStructured) - { - var provider = new EvaluatedExpressionProvider(expression, frame.ThreadId, frame.Index); - id = _threadState.RegisterVariablesProvider(provider); - } - } - catch (Exception e) - { - evalResult = new OneScript.DebugProtocol.Variable() { Presentation = e.Message, Name = "$evalFault" }; - } - - if (evalResult.Name.Equals("$evalFault") && context.Equals("hover")) - { - evalResult.Presentation = $"err: {expression}"; - } - - var protResult = new EvaluateResponseBody(evalResult.Presentation, id) {type = evalResult.TypeName}; - SendResponse(response, protResult); - } - - - private void SendOutput(string category, string data) - { - if (!String.IsNullOrEmpty(data)) - { - if (data[data.Length - 1] != '\n') - { - data += '\n'; - } - SendEvent(new OutputEvent(category, data)); - } - } - - private static T GetFromContainer(dynamic container, string propertyName, T defaultValue = default) - { - try - { - return (T)container[propertyName]; - } - catch (Exception) - { - // ignore and return default value - } - return defaultValue; - } - - protected override void OnRequestError(Exception e) - { - Log.Error(e, "Unhandled request processing error"); - } - - private void LogCommandReceived(dynamic args = null, [CallerMemberName] string commandName = "") - { - if (args == null) - Log.Debug("Command received {Command}", commandName); - else - Log.Debug("Command received {Command}: {@Args}", commandName, args); - } - } -} diff --git a/src/VSCode.DebugAdapter/Program.cs b/src/VSCode.DebugAdapter/Program.cs index 0966206d7..9cae5a4e7 100644 --- a/src/VSCode.DebugAdapter/Program.cs +++ b/src/VSCode.DebugAdapter/Program.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one @@ -55,13 +55,10 @@ private static void StartSession(Stream input, Stream output) var adapter = new OneScriptDebugAdapter(factory.CreateLogger("OneScriptDebugAdapter")); var dapServer = new DapServer(new BufferedTransport(input, output), adapter, factory); - - var session = new OscriptDebugSession(); - try { Log.Logger.Information("Starting debug adapter"); - session.Start(input, output); + dapServer.RunAsync(System.Threading.CancellationToken.None).GetAwaiter().GetResult(); } catch (Exception e) { diff --git a/src/VSCode.DebugAdapter/Protocol.cs b/src/VSCode.DebugAdapter/Protocol.cs deleted file mode 100644 index fad6cd03b..000000000 --- a/src/VSCode.DebugAdapter/Protocol.cs +++ /dev/null @@ -1,270 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -using System; -using System.Text; -using System.IO; -using System.Text.RegularExpressions; -using Newtonsoft.Json; -using Serilog; - -namespace VSCodeDebug -{ - public class ProtocolMessage - { - public int seq; - public string type { get; } - - public ProtocolMessage(string typ) { - type = typ; - } - - public ProtocolMessage(string typ, int sq) { - type = typ; - seq = sq; - } - } - - public class Request : ProtocolMessage - { - public string command; - public dynamic arguments; - - public Request(int id, string cmd, dynamic arg) : base("request", id) { - command = cmd; - arguments = arg; - } - } - - /* - * subclasses of ResponseBody are serialized as the body of a response. - * Don't change their instance variables since that will break the debug protocol. - */ - public class ResponseBody { - // empty - } - - public class Response : ProtocolMessage - { - public bool success { get; private set; } - public string message { get; private set; } - public int request_seq { get; } - public string command { get; } - public ResponseBody body { get; private set; } - - public Response(Request req) : base("response") { - success = true; - request_seq = req.seq; - command = req.command; - } - - public void SetBody(ResponseBody bdy) { - success = true; - body = bdy; - } - - public void SetErrorBody(string msg, ResponseBody bdy = null) { - success = false; - message = msg; - body = bdy; - } - } - - public class Event : ProtocolMessage - { - [JsonProperty(PropertyName = "event")] - public string eventType { get; } - public dynamic body { get; } - - public Event(string type, dynamic bdy = null) : base("event") { - eventType = type; - body = bdy; - } - } - - /* - * The ProtocolServer can be used to implement a server that uses the VSCode debug protocol. - */ - public abstract class ProtocolServer - { - protected const int BUFFER_SIZE = 4096; - protected const string TWO_CRLF = "\r\n\r\n"; - protected static readonly Regex CONTENT_LENGTH_MATCHER = new Regex(@"Content-Length: (\d+)"); - - protected static readonly Encoding Encoding = System.Text.Encoding.UTF8; - - private int _sequenceNumber = 1; - - private Stream _outputStream; - - private ByteBuffer _rawData = new ByteBuffer(); - private int _bodyLength = -1; - - private bool _stopRequested; - - - public void Start(Stream inputStream, Stream outputStream) - { - _outputStream = outputStream; - - byte[] buffer = new byte[BUFFER_SIZE]; - - _stopRequested = false; - while (!_stopRequested) { - var read = inputStream.Read(buffer, 0, buffer.Length); - - if (read == 0) { - // end of stream - break; - } - - if (read > 0) { - _rawData.Append(buffer, read); - ProcessData(); - } - } - } - - public void Stop() - { - _stopRequested = true; - } - - public void SendEvent(Event e) - { - SendMessage(e); - } - - protected abstract void DispatchRequest(string command, dynamic args, Response response); - - // ---- private ------------------------------------------------------------------------ - - private void ProcessData() - { - while (true) { - if (_bodyLength >= 0) { - if (_rawData.Length >= _bodyLength) { - var buf = _rawData.RemoveFirst(_bodyLength); - - _bodyLength = -1; - - Dispatch(Encoding.GetString(buf)); - - continue; // there may be more complete messages to process - } - } - else { - string s = _rawData.GetString(Encoding); - var idx = s.IndexOf(TWO_CRLF); - if (idx != -1) { - Match m = CONTENT_LENGTH_MATCHER.Match(s); - if (m.Success && m.Groups.Count == 2) { - _bodyLength = Convert.ToInt32(m.Groups[1].ToString()); - - _rawData.RemoveFirst(idx + TWO_CRLF.Length); - - continue; // try to handle a complete message - } - } - } - break; - } - } - - private void Dispatch(string req) - { - var request = JsonConvert.DeserializeObject(req); - if (request != null && request.type == "request") { - Log.Verbose("Got {Command} with args {Args}", request.command, JsonConvert.SerializeObject(request.arguments)); - - var response = new Response(request); - - DispatchRequest(request.command, request.arguments, response); - - SendMessage(response); - } - } - - protected void SendMessage(ProtocolMessage message) - { - message.seq = _sequenceNumber++; - - if (message.type == "response") { - Log.Verbose("Response {Response}", JsonConvert.SerializeObject(message)); - } - if (message.type == "event") { - Event e = (Event)message; - Log.Verbose("Event {EventType} with args {Args}", e.eventType, JsonConvert.SerializeObject(e.body)); - } - - var data = ConvertToBytes(message); - try { - _outputStream.Write(data, 0, data.Length); - _outputStream.Flush(); - } - catch (Exception) { - // ignore - } - } - - private static byte[] ConvertToBytes(ProtocolMessage request) - { - var asJson = JsonConvert.SerializeObject(request); - byte[] jsonBytes = Encoding.GetBytes(asJson); - - string header = string.Format("Content-Length: {0}{1}", jsonBytes.Length, TWO_CRLF); - byte[] headerBytes = Encoding.GetBytes(header); - - byte[] data = new byte[headerBytes.Length + jsonBytes.Length]; - System.Buffer.BlockCopy(headerBytes, 0, data, 0, headerBytes.Length); - System.Buffer.BlockCopy(jsonBytes, 0, data, headerBytes.Length, jsonBytes.Length); - - return data; - } - } - - //-------------------------------------------------------------------------------------- - - class ByteBuffer - { - private byte[] _buffer; - - public ByteBuffer() { - _buffer = new byte[0]; - } - - public int Length { - get { return _buffer.Length; } - } - - public string GetString(Encoding enc) - { - return enc.GetString(_buffer); - } - - public void Append(byte[] b, int length) - { - byte[] newBuffer = new byte[_buffer.Length + length]; - System.Buffer.BlockCopy(_buffer, 0, newBuffer, 0, _buffer.Length); - System.Buffer.BlockCopy(b, 0, newBuffer, _buffer.Length, length); - _buffer = newBuffer; - } - - public byte[] RemoveFirst(int n) - { - byte[] b = new byte[n]; - System.Buffer.BlockCopy(_buffer, 0, b, 0, n); - byte[] newBuffer = new byte[_buffer.Length - n]; - System.Buffer.BlockCopy(_buffer, n, newBuffer, 0, _buffer.Length - n); - _buffer = newBuffer; - return b; - } - } -} From 6858bd0856760cc52308dc6d83831e9c5601bb0b Mon Sep 17 00:00:00 2001 From: EvilBeaver Date: Wed, 25 Mar 2026 10:42:12 +0300 Subject: [PATCH 52/52] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D1=81=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20deb?= =?UTF-8?q?uggee=20=D0=B2=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=87?= =?UTF-8?q?=D0=B8=D0=BA=20initialized?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/VSCode.DebugAdapter/DebugeeFactory.cs | 8 ----- .../OneScriptDebugAdapter.cs | 30 ++++++++----------- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/src/VSCode.DebugAdapter/DebugeeFactory.cs b/src/VSCode.DebugAdapter/DebugeeFactory.cs index f25d51387..d494b21fd 100644 --- a/src/VSCode.DebugAdapter/DebugeeFactory.cs +++ b/src/VSCode.DebugAdapter/DebugeeFactory.cs @@ -25,13 +25,5 @@ public static DebugeeProcess CreateProcess(string adapterId, PathHandlingStrateg throw new ArgumentOutOfRangeException(nameof(adapterId), adapterId, "Unsupported debugger"); } - - public static DebugeeProcess CreateAttachableProcess(string adapterId, PathHandlingStrategy pathStrategy) - { - // Для аттача нам не важно, консольный это процесс или веб-сервер, - // так как мы не запускаем его, а только подключаемся. - // Но нам нужен экземпляр DebugeeProcess. ConsoleProcess подойдет. - return new ConsoleProcess(pathStrategy); - } } } \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs b/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs index 5500d4898..e98313f71 100644 --- a/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs +++ b/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs @@ -35,6 +35,17 @@ public OneScriptDebugAdapter(ILogger logger) protected override async Task OnInitializeAsync(InitializeRequest request, CancellationToken ct) { + var pathStrategy = new PathHandlingStrategy + { + ClientLinesStartAt1 = Client.LinesStartAt1, + ClientPathsAreUri = Client.PathFormat == "uri", + DebuggerLinesStartAt1 = true, + DebuggerPathsAreUri = false + }; + + _debuggee = DebugeeFactory.CreateProcess(Client.AdapterId, pathStrategy); + Log.LogDebug("Debuggee created"); + await EventsChannel.SendEventAsync(new InitializedEvent(), ct); return new InitializeResponse() @@ -338,15 +349,6 @@ public override Task AttachAsync(AttachRequest request, Cancella { var options = request.Arguments.DeserializeAdditionalProperties(); - var pathStrategy = new PathHandlingStrategy - { - ClientLinesStartAt1 = Client.LinesStartAt1, - ClientPathsAreUri = Client.PathFormat == "uri", - DebuggerLinesStartAt1 = true, - DebuggerPathsAreUri = false - }; - - _debuggee = DebugeeFactory.CreateAttachableProcess(Client.AdapterId, pathStrategy); _debuggee.DebugPort = options.DebugPort; _debuggee.PathsMapper = options.PathsMapping; @@ -428,15 +430,7 @@ public override async Task LaunchAsync(LaunchRequest request, Ca try { Log.LogDebug("Initializing process settings"); - var pathStrategy = new PathHandlingStrategy - { - ClientLinesStartAt1 = Client.LinesStartAt1, - ClientPathsAreUri = Client.PathFormat == "uri", - DebuggerLinesStartAt1 = true, - DebuggerPathsAreUri = false - }; - - _debuggee = DebugeeFactory.CreateProcess(Client.AdapterId, pathStrategy); + _debuggee.Init(request.Arguments); } catch (InvalidDebugeeOptionsException e)