//----------------------------------------------------------------------- //----------------------------------------------------------------------- // 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 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 ',': _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(); } }