//----------------------------------------------------------------------- //----------------------------------------------------------------------- // file="ExpSourceReader.cs" // Project: StgSharp // AuthorGroup: Nitload Space // Copyright (c) Nitload Space. All rights reserved. // // 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. // //----------------------------------------------------------------------- //----------------------------------------------------------------------- using CommunityToolkit.HighPerformance; using StgSharp.Intrinsic; using System; using System.Collections.Generic; using System.Diagnostics.Tracing; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; namespace StgSharp.Script.Express { public partial class ExpSourceReader { private const int MultiLineEnd = 0x0029002A; // *) private const string Token = "token", End = "end", Rest = "rest"; private bool _isReadingMultiLineComment; private int _current; private IScriptSourceProvider _provider; private string _lineCache; public ExpSourceReader( IScriptSourceProvider provider ) { _provider = provider; _current = 0; _lineCache = string.Empty; _isReadingMultiLineComment = false; } public int LineNumber { get => _provider.Location; } public bool IsEmpty { get => ( ( string.IsNullOrEmpty( _lineCache ) || _current >= _lineCache.Length ) ) && _provider.IsEmpty; } /// /// Read code until end of a line. Lines in multi-line comment will be ignored. Line /// begins with part of multi-line comment will automatically remove the comment part, but /// beginning with a full multi-line comment will not causing removing. (*Just /// Like The Comment Here*) Will Not Be Removed /// /// public unsafe ReadOnlySpan ReadLine() { bool lineOver = string.IsNullOrEmpty( _lineCache ) || _current >= _lineCache.Length; while( lineOver || _isReadingMultiLineComment ) { if( lineOver ) { do { _lineCache = _provider.ReadLine(); } while (string.IsNullOrEmpty( _lineCache )); _current = 0; } if( _isReadingMultiLineComment ) { int index; fixed( char* cPtr = _lineCache.AsSpan( _current ) ) { index = UnsafeCompute.IndexOfCharPair( cPtr, MultiLineEnd, _lineCache.Length ); } if( index == -1 ) { _lineCache = string.Empty; } else { _isReadingMultiLineComment = false; _current += index + 2; } } lineOver = string.IsNullOrEmpty( _lineCache ) || _current >= _lineCache.Length; } return _lineCache.AsSpan().Slice( _current ); } public unsafe ReadOnlySpan ReadToken() { int begin, end, rest; readLine: ReadOnlySpan source = ReadLine(); char c = source[ 0 ]; if( char.IsLetter( c ) ) { Match match = _nextWordTokenPattern.Match( _lineCache, _current ); if( match.Success ) { begin = match.Groups[ Token ].Index; end = match.Groups[ End ].Index; rest = match.Groups[ Rest ].Index; _current = rest; return source.Slice( 0, end - begin ); } else { throw new ExpInvalidTokenException( _current ); } } else if( char.IsPunctuation( c ) || char.IsSymbol( c ) ) { switch( c ) { case '"': Match match = _nextStringLiteralPattern.Match( _lineCache, _current ); if( match.Success ) { begin = match.Groups[ "token" ].Index; end = match.Groups[ "end" ].Index; rest = match.Groups[ "rest" ].Index; _current = rest; return source[ ..( end - begin ) ]; } else { throw new ExpInvalidCharException( '\"' ); } case '(': char cNext = source[ 1 ]; if( cNext == '*' ) { _isReadingMultiLineComment = true; _current += 2; goto readLine; } else { return source[ ..1 ]; } case '-': // 1. -number // 2. -- comment // 3. - number type param cNext = source[ 1 ]; if( char.IsNumber( cNext ) ) { match = _nextNumberLiteralPattern.Match( _lineCache, _current ); if( match.Success ) { begin = match.Groups[ Token ].Index; end = match.Groups[ End ].Index; rest = match.Groups[ Rest ].Index; _current = rest; return source[ ..( end - begin ) ]; } else { throw new ExpInvalidTokenException( _current ); } } else if( cNext == '-' ) { _lineCache = string.Empty; _current = 0; goto readLine; } else { _current++; return source[ ..1 ]; } case ')' or '[' or ']' or '{' or '}' or ':' or ';' or '+' or '/' or '\\' or '*' or '.' or ',': _current++; return source[ ..1 ]; default: throw new ExpInvalidCharException( c ); } } else if( char.IsNumber( c ) ) { Match match = _nextNumberLiteralPattern.Match( _lineCache, _current ); if( match.Success ) { begin = match.Groups[ Token ].Index; end = match.Groups[ End ].Index; rest = match.Groups[ Rest ].Index; _current = rest; return source[ ..( end - begin ) ]; } else { throw new ExpInvalidTokenException( _current ); } } else if( char.IsWhiteSpace( c ) ) { Match match = _skipBlankPattern.Match( _lineCache, _current ); if( match.Success ) { rest = match.Groups[ Rest ].Index; _current = rest; goto readLine; } else { throw new ExpInvalidTokenException( _current ); } } else { throw new ExpInvalidCharException( c ); } } [GeneratedRegex( @"(?[a-zA-Z][a-zA-Z0-9_]*)(?\s*?)(?\S|$)", RegexOptions.Singleline )] private static partial Regex GetNextWordTokenPattern(); [GeneratedRegex( @"(?-?\s*?[0-9]+(?:\.[0-9]*)?)(?\s*?)(?\S|$)", RegexOptions.Singleline )] private static partial Regex GetNumberLiteralPattern(); [GeneratedRegex( @"(?:\s+?)(?\S|$)", RegexOptions.Singleline )] private static partial Regex GetSkipBlankPattern(); [GeneratedRegex( @"(?"")(?:.*?[^""\\](\\\\)*)?(?"")\s*?(?\S|$)", RegexOptions.Singleline )] private static partial Regex GetStringLiteralPattern(); private Regex _nextWordTokenPattern = GetNextWordTokenPattern(), _nextStringLiteralPattern = GetStringLiteralPattern(), _nextNumberLiteralPattern = GetNumberLiteralPattern(), _skipBlankPattern = GetSkipBlankPattern(); } }