Browse Source

change: draft of better compile exception

Akeit0 7 months ago
parent
commit
058de2fb50

+ 1 - 1
src/Lua/CodeAnalysis/Compilation/Function.cs

@@ -305,7 +305,7 @@ internal class Function : IPoolNode<Function>
 
 
     public void SemanticError(string message)
     public void SemanticError(string message)
     {
     {
-        P.Scanner.Token.T = default;
+        P.Scanner.Token = default;
         P.Scanner.SyntaxError(message);
         P.Scanner.SyntaxError(message);
     }
     }
 
 

+ 2 - 1
src/Lua/CodeAnalysis/Compilation/Parser.cs

@@ -3,6 +3,7 @@ using Lua.Runtime;
 using System.Buffers;
 using System.Buffers;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using static System.Diagnostics.Debug;
 using static System.Diagnostics.Debug;
+
 namespace Lua.CodeAnalysis.Compilation;
 namespace Lua.CodeAnalysis.Compilation;
 
 
 using static Function;
 using static Function;
@@ -914,7 +915,7 @@ internal class Parser : IPoolNode<Parser>, IDisposable
             R = r,
             R = r,
             LineNumber = 1,
             LineNumber = 1,
             LastLine = 1,
             LastLine = 1,
-            LookAheadToken = new() { T = TkEos },
+            LookAheadToken = new(0, TkEos),
             L = l,
             L = l,
             Buffer = new(),
             Buffer = new(),
             Source = name
             Source = name

+ 73 - 38
src/Lua/CodeAnalysis/Compilation/Scanner.cs

@@ -16,6 +16,7 @@ internal struct Scanner
     public int LineNumber, LastLine;
     public int LineNumber, LastLine;
     public string Source;
     public string Source;
     public Token LookAheadToken;
     public Token LookAheadToken;
+    private int lastNewLinePos;
 
 
     ///inline
     ///inline
     public Token Token;
     public Token Token;
@@ -113,8 +114,29 @@ internal struct Scanner
         var shortSourceBuffer = (stackalloc char[59]);
         var shortSourceBuffer = (stackalloc char[59]);
         var len = LuaDebug.WriteShortSource(Source, shortSourceBuffer);
         var len = LuaDebug.WriteShortSource(Source, shortSourceBuffer);
         var buff = shortSourceBuffer[..len].ToString();
         var buff = shortSourceBuffer[..len].ToString();
-        message = token != 0 ? $"{buff}:{LineNumber}: {message} near {TokenToString(token)}" : $"{buff}:{LineNumber}: {message}";
-        throw new LuaScanException(message);
+        var pos = R.Position;
+        if (token != 0)
+        {
+            var t = TokenToString(token);
+            message = $"{message} near {t}";
+            pos = Token.Pos;
+        }
+
+        throw new LuaCompileException(buff, new SourcePosition(LineNumber, pos - lastNewLinePos + 1), pos - 1, message);
+    }
+
+    public void ScanError(int pos, string message, int token)
+    {
+        var shortSourceBuffer = (stackalloc char[59]);
+        var len = LuaDebug.WriteShortSource(Source, shortSourceBuffer);
+        var buff = shortSourceBuffer[..len].ToString();
+        if (token != 0)
+        {
+            var t = TokenToString(token);
+            message = $"{message} near {t}";
+        }
+
+        throw new LuaCompileException(buff, new SourcePosition(LineNumber, pos - lastNewLinePos + 1), pos - 1, message);
     }
     }
 
 
     public void IncrementLineNumber()
     public void IncrementLineNumber()
@@ -123,6 +145,7 @@ internal struct Scanner
         Assert(IsNewLine(old));
         Assert(IsNewLine(old));
         Advance();
         Advance();
         if (IsNewLine(Current) && Current != old) Advance();
         if (IsNewLine(Current) && Current != old) Advance();
+        lastNewLinePos = R.Position;
         if (++LineNumber >= MaxLine) SyntaxError("chunk has too many lines");
         if (++LineNumber >= MaxLine) SyntaxError("chunk has too many lines");
     }
     }
 
 
@@ -255,7 +278,7 @@ internal struct Scanner
         }
         }
     }
     }
 
 
-    public Token ReadNumber()
+    public Token ReadNumber(int pos)
     {
     {
         var c = Current;
         var c = Current;
         Assert(IsDecimal(c));
         Assert(IsDecimal(c));
@@ -311,7 +334,7 @@ internal struct Scanner
                 Buffer.Clear();
                 Buffer.Clear();
             }
             }
 
 
-            return new() { T = TkNumber, N = (fraction * Math.Pow(2, exponent)) };
+            return new(pos, fraction * Math.Pow(2, exponent));
         }
         }
 
 
         c = ReadDigits();
         c = ReadDigits();
@@ -339,7 +362,7 @@ internal struct Scanner
             if (str.Length == 1)
             if (str.Length == 1)
             {
             {
                 Buffer.Clear();
                 Buffer.Clear();
-                return new() { T = TkNumber, N = 0 };
+                return new(pos, 0d);
             }
             }
 
 
             str = str.TrimStart('0');
             str = str.TrimStart('0');
@@ -355,7 +378,7 @@ internal struct Scanner
         }
         }
 
 
         Buffer.Clear();
         Buffer.Clear();
-        return new() { T = TkNumber, N = f };
+        return new(pos, f);
     }
     }
 
 
     static readonly Dictionary<int, char> escapes = new()
     static readonly Dictionary<int, char> escapes = new()
@@ -372,7 +395,7 @@ internal struct Scanner
         { '\'', '\'' },
         { '\'', '\'' },
     };
     };
 
 
-    public void EscapeError(ReadOnlySpan<int> c, string message)
+    public void EscapeError(int pos, ReadOnlySpan<int> c, string message)
     {
     {
         Buffer.Clear();
         Buffer.Clear();
         Save('\'');
         Save('\'');
@@ -388,9 +411,10 @@ internal struct Scanner
         }
         }
 
 
         Save('\'');
         Save('\'');
-        Token.S = Buffer.ToString();
+
+        Token = new(pos, TkString, Buffer.ToString());
         Buffer.Clear();
         Buffer.Clear();
-        ScanError(message, TkString);
+        ScanError(pos, message, TkString);
     }
     }
 
 
     public int ReadHexEscape()
     public int ReadHexEscape()
@@ -399,6 +423,7 @@ internal struct Scanner
         var r = 0;
         var r = 0;
         var b = (stackalloc int[3] { 'x', 0, 0 });
         var b = (stackalloc int[3] { 'x', 0, 0 });
         var (i, c) = (1, Current);
         var (i, c) = (1, Current);
+        var pos = R.Position;
         for (; i < b.Length; (i, c, r) = (i + 1, Current, (r << 4) + c))
         for (; i < b.Length; (i, c, r) = (i + 1, Current, (r << 4) + c))
         {
         {
             b[i] = c;
             b[i] = c;
@@ -414,11 +439,12 @@ internal struct Scanner
                     c -= ('A' - 10);
                     c -= ('A' - 10);
                     break;
                     break;
                 default:
                 default:
-                    EscapeError(b.Slice(0, i + 1), "hexadecimal digit expected");
+                    EscapeError(pos, b.Slice(0, i + 1), "hexadecimal digit expected");
                     break;
                     break;
             }
             }
 
 
             Advance();
             Advance();
+            pos = R.Position;
         }
         }
 
 
         return r;
         return r;
@@ -429,16 +455,18 @@ internal struct Scanner
         var b = (stackalloc int[3] { 0, 0, 0 });
         var b = (stackalloc int[3] { 0, 0, 0 });
         var c = Current;
         var c = Current;
         var r = 0;
         var r = 0;
+        var pos = R.Position;
         for (int i = 0; i < b.Length && IsDecimal(c); i++, c = Current)
         for (int i = 0; i < b.Length && IsDecimal(c); i++, c = Current)
         {
         {
             b[i] = c;
             b[i] = c;
             r = 10 * r + c - '0';
             r = 10 * r + c - '0';
             Advance();
             Advance();
+            pos = R.Position;
         }
         }
 
 
         if (r > 255)
         if (r > 255)
         {
         {
-            EscapeError(b, "decimal escape too large");
+            EscapeError(pos, b, "decimal escape too large");
         }
         }
 
 
         return r;
         return r;
@@ -446,16 +474,19 @@ internal struct Scanner
 
 
     public Token ReadString()
     public Token ReadString()
     {
     {
+        var pos = R.Position;
         var delimiter = Current;
         var delimiter = Current;
         for (SaveAndAdvance(); Current != delimiter;)
         for (SaveAndAdvance(); Current != delimiter;)
         {
         {
             switch (Current)
             switch (Current)
             {
             {
                 case EndOfStream:
                 case EndOfStream:
-                    ScanError("unfinished string", TkEos);
+                    Token = new(R.Position - Buffer.Length, TkString, Buffer.ToString());
+                    ScanError(R.Position, "unfinished string", TkEos);
                     break;
                     break;
                 case '\n' or '\r':
                 case '\n' or '\r':
-                    ScanError("unfinished string", TkString);
+                    Token = new(R.Position - Buffer.Length, TkString, Buffer.ToString());
+                    ScanError(R.Position, "unfinished string", TkString);
                     break;
                     break;
                 case '\\':
                 case '\\':
                     Advance();
                     Advance();
@@ -496,7 +527,7 @@ internal struct Scanner
                     }
                     }
                     else
                     else
                     {
                     {
-                        EscapeError([c], "invalid escape sequence");
+                        EscapeError(R.Position - 1, [c], "invalid escape sequence");
                     }
                     }
 
 
                     break;
                     break;
@@ -514,7 +545,7 @@ internal struct Scanner
         // }
         // }
         var str = Buffer.ToString(1, length);
         var str = Buffer.ToString(1, length);
         Buffer.Clear();
         Buffer.Clear();
-        return new() { T = TkString, S = str };
+        return new(pos, TkString, str);
     }
     }
 
 
     public static bool IsReserved(string s)
     public static bool IsReserved(string s)
@@ -532,22 +563,24 @@ internal struct Scanner
 
 
     public Token ReservedOrName()
     public Token ReservedOrName()
     {
     {
+        var pos = R.Position - Buffer.Length;
         var str = Buffer.ToString();
         var str = Buffer.ToString();
         Buffer.Clear();
         Buffer.Clear();
         for (var i = 0; i < Tokens.Length; i++)
         for (var i = 0; i < Tokens.Length; i++)
         {
         {
             if (str == Tokens[i])
             if (str == Tokens[i])
             {
             {
-                return new() { T = (i + FirstReserved), S = str };
+                return new(pos, (i + FirstReserved), str);
             }
             }
         }
         }
 
 
-        return new() { T = TkName, S = str };
+        return new(pos, TkName, str);
     }
     }
 
 
     public Token Scan()
     public Token Scan()
     {
     {
         const bool comment = true, str = false;
         const bool comment = true, str = false;
+        var pos = R.Position;
         while (true)
         while (true)
         {
         {
             var c = Current;
             var c = Current;
@@ -562,12 +595,13 @@ internal struct Scanner
                 case '\t':
                 case '\t':
                 case '\v':
                 case '\v':
                     Advance();
                     Advance();
+                    pos = R.Position;
                     break;
                     break;
                 case '-':
                 case '-':
                     Advance();
                     Advance();
                     if (Current != '-')
                     if (Current != '-')
                     {
                     {
-                        return new() { T = '-' };
+                        return new(pos, '-');
                     }
                     }
 
 
                     Advance();
                     Advance();
@@ -595,11 +629,11 @@ internal struct Scanner
                         var sep = SkipSeparator();
                         var sep = SkipSeparator();
                         if (sep >= 0)
                         if (sep >= 0)
                         {
                         {
-                            return new() { T = TkString, S = ReadMultiLine(str, sep) };
+                            return new(pos, TkString, ReadMultiLine(str, sep));
                         }
                         }
 
 
                         Buffer.Clear();
                         Buffer.Clear();
-                        if (sep == -1) return new() { T = '[' };
+                        if (sep == -1) return new(pos, '[');
 
 
                         ScanError("invalid long string delimiter", TkString);
                         ScanError("invalid long string delimiter", TkString);
                         break;
                         break;
@@ -608,52 +642,52 @@ internal struct Scanner
                     Advance();
                     Advance();
                     if (Current != '=')
                     if (Current != '=')
                     {
                     {
-                        return new() { T = '=' };
+                        return new(pos, '=');
                     }
                     }
 
 
                     Advance();
                     Advance();
-                    return new() { T = TkEq };
+                    return new(pos, TkEq);
                 case '<':
                 case '<':
                     Advance();
                     Advance();
                     if (Current != '=')
                     if (Current != '=')
                     {
                     {
-                        return new() { T = '<' };
+                        return new(pos, '<');
                     }
                     }
 
 
                     Advance();
                     Advance();
-                    return new() { T = TkLe };
+                    return new(pos, TkLe);
                 case '>':
                 case '>':
                     Advance();
                     Advance();
                     if (Current != '=')
                     if (Current != '=')
                     {
                     {
-                        return new() { T = '>' };
+                        return new(pos, '>');
                     }
                     }
 
 
                     Advance();
                     Advance();
-                    return new() { T = TkGe };
+                    return new(pos, TkGe);
                 case '~':
                 case '~':
                     Advance();
                     Advance();
                     if (Current != '=')
                     if (Current != '=')
                     {
                     {
-                        return new() { T = '~' };
+                        return new(pos, '~');
                     }
                     }
 
 
                     Advance();
                     Advance();
-                    return new() { T = TkNe };
+                    return new(pos, TkNe);
                 case ':':
                 case ':':
                     Advance();
                     Advance();
                     if (Current != ':')
                     if (Current != ':')
                     {
                     {
-                        return new() { T = ':' };
+                        return new(pos, ':');
                     }
                     }
 
 
                     Advance();
                     Advance();
-                    return new() { T = TkDoubleColon };
+                    return new(pos, TkDoubleColon);
                 case '"':
                 case '"':
                 case '\'':
                 case '\'':
                     return ReadString();
                     return ReadString();
                 case EndOfStream:
                 case EndOfStream:
-                    return new() { T = TkEos };
+                    return new(pos, TkEos);
                 case '.':
                 case '.':
                     SaveAndAdvance();
                     SaveAndAdvance();
                     if (CheckNext("."))
                     if (CheckNext("."))
@@ -661,28 +695,29 @@ internal struct Scanner
                         if (CheckNext("."))
                         if (CheckNext("."))
                         {
                         {
                             Buffer.Clear();
                             Buffer.Clear();
-                            return new() { T = TkDots };
+                            return new(pos, TkDots);
                         }
                         }
 
 
                         Buffer.Clear();
                         Buffer.Clear();
-                        return new() { T = TkConcat };
+                        return new(pos, TkConcat);
                     }
                     }
 
 
                     if (!IsDigit(Current))
                     if (!IsDigit(Current))
                     {
                     {
                         Buffer.Clear();
                         Buffer.Clear();
-                        return new() { T = '.' };
+                        return new(pos, '.');
                     }
                     }
 
 
-                    return ReadNumber();
+                    return ReadNumber(pos);
                 case 0:
                 case 0:
                     Advance();
                     Advance();
+                    pos = R.Position;
                     break;
                     break;
                 default:
                 default:
                     {
                     {
                         if (IsDigit(c))
                         if (IsDigit(c))
                         {
                         {
-                            return ReadNumber();
+                            return ReadNumber(pos);
                         }
                         }
 
 
                         if (IsLetter(c))
                         if (IsLetter(c))
@@ -696,7 +731,7 @@ internal struct Scanner
                         }
                         }
 
 
                         Advance();
                         Advance();
-                        return new() { T = c };
+                        return new(pos, c);
                     }
                     }
             }
             }
         }
         }
@@ -708,7 +743,7 @@ internal struct Scanner
         if (LookAheadToken.T != TkEos)
         if (LookAheadToken.T != TkEos)
         {
         {
             Token = LookAheadToken;
             Token = LookAheadToken;
-            LookAheadToken.T = TkEos;
+            LookAheadToken = new(0, TkEos);
         }
         }
         else
         else
         {
         {

+ 15 - 7
src/Lua/CodeAnalysis/Compilation/Token.cs

@@ -3,15 +3,23 @@
 namespace Lua.CodeAnalysis.Compilation;
 namespace Lua.CodeAnalysis.Compilation;
 
 
 [DebuggerDisplay("{DebuggerDisplay}")]
 [DebuggerDisplay("{DebuggerDisplay}")]
-internal struct Token
+internal readonly struct Token(int pos, int t)
 {
 {
-    public int T;
-    public double N;
-    public string S;
-    string DebuggerDisplay => $"{Scanner.TokenToString(this)} {T} {N} {S}";
+    public Token(int pos, int t, string str) : this(pos, t)
+    {
+        S = str;
+        N = 0;
+    }
 
 
-    public static implicit operator Token(int token)
+    public Token(int pos, double n) : this(pos, Scanner.TkNumber)
     {
     {
-        return new() { T = token };
+        N = n;
+        S = string.Empty;
     }
     }
+
+    public readonly int Pos = pos;
+    public readonly int T = t;
+    public readonly double N;
+    public readonly string S;
+    string DebuggerDisplay => $"{Scanner.TokenToString(this)} {T} {N} {S}";
 }
 }

+ 11 - 3
src/Lua/Exceptions.cs

@@ -23,7 +23,7 @@ public class LuaException : Exception
 public class LuaParseException(string? chunkName, SourcePosition position, string message) : LuaException(message)
 public class LuaParseException(string? chunkName, SourcePosition position, string message) : LuaException(message)
 {
 {
     public string? ChunkName { get; } = chunkName;
     public string? ChunkName { get; } = chunkName;
-    public SourcePosition? Position { get; } = position;
+    public SourcePosition Position { get; } = position;
 
 
     public static void UnexpectedToken(string? chunkName, SourcePosition position, SyntaxToken token)
     public static void UnexpectedToken(string? chunkName, SourcePosition position, SyntaxToken token)
     {
     {
@@ -55,10 +55,18 @@ public class LuaParseException(string? chunkName, SourcePosition position, strin
         throw new LuaParseException(chunkName, position, "<break> not inside a loop");
         throw new LuaParseException(chunkName, position, "<break> not inside a loop");
     }
     }
 
 
-    public override string Message => $"{ChunkName}:{(Position == null ? "" : $"{Position.Value}:")} {base.Message}";
+    public override string Message => $"{ChunkName}:{Position.Line}: {base.Message}";
 }
 }
 
 
-public class LuaScanException(string message) : LuaException(message);
+public class LuaCompileException(string chunkName, SourcePosition position, int offset, string message) : LuaException(message)
+{
+    public string ChunkName { get; } = chunkName;
+    public int OffSet { get; } = offset;
+    public SourcePosition Position  => position;
+    
+    public string MainMessage => base.Message;
+    public override string Message => $"{ChunkName}:{Position.Line}: {base.Message}";
+}
 
 
 public class LuaUnDumpException(string message) : LuaException(message);
 public class LuaUnDumpException(string message) : LuaException(message);