This commit is contained in:
nub31
2026-03-09 16:38:09 +01:00
parent 32042d0769
commit 4210ad878b
8 changed files with 800 additions and 675 deletions

View File

@@ -77,336 +77,346 @@ public class Tokenizer
switch (c)
{
case '0' when Peek(1) is 'x':
{
Consume();
Consume();
var parsed = BigInteger.Zero;
var seenDigit = false;
while (TryPeek(out c))
{
if (c == '_')
{
Consume();
continue;
}
if (!char.IsAsciiHexDigit(c))
break;
seenDigit = true;
parsed <<= 4;
Consume();
parsed += c switch
Consume();
var parsed = BigInteger.Zero;
var seenDigit = false;
while (TryPeek(out c))
{
>= '0' and <= '9' => c - '0',
>= 'a' and <= 'f' => c - 'a' + 10,
>= 'A' and <= 'F' => c - 'A' + 10,
_ => 0
};
if (c == '_')
{
Consume();
continue;
}
if (!char.IsAsciiHexDigit(c))
break;
seenDigit = true;
parsed <<= 4;
Consume();
parsed += c switch
{
>= '0' and <= '9' => c - '0',
>= 'a' and <= 'f' => c - 'a' + 10,
>= 'A' and <= 'F' => c - 'A' + 10,
_ => 0
};
}
if (!seenDigit)
throw new CompileException(Diagnostic.Error("Expected hexadecimal digits after 0x").At(fileName, line, startColumn, column - startColumn).Build());
return new TokenIntLiteral(line, startColumn, column - startColumn, parsed);
}
if (!seenDigit)
throw new CompileException(Diagnostic.Error("Expected hexadecimal digits after 0x").At(fileName, line, startColumn, column - startColumn).Build());
return new TokenIntLiteral(line, startColumn, column - startColumn, parsed);
}
case '0' when Peek(1) is 'b':
{
Consume();
Consume();
var parsed = BigInteger.Zero;
var seenDigit = false;
while (TryPeek(out c))
{
if (c == '_')
Consume();
Consume();
var parsed = BigInteger.Zero;
var seenDigit = false;
while (TryPeek(out c))
{
Consume();
continue;
if (c == '_')
{
Consume();
continue;
}
if (c is not '0' and not '1')
break;
seenDigit = true;
parsed <<= 1;
if (Consume() == '1')
parsed += BigInteger.One;
}
if (c is not '0' and not '1')
break;
if (!seenDigit)
throw new CompileException(Diagnostic.Error("Expected binary digits after 0b").At(fileName, line, startColumn, column - startColumn).Build());
seenDigit = true;
parsed <<= 1;
if (Consume() == '1')
parsed += BigInteger.One;
return new TokenIntLiteral(line, startColumn, column - startColumn, parsed);
}
if (!seenDigit)
throw new CompileException(Diagnostic.Error("Expected binary digits after 0b").At(fileName, line, startColumn, column - startColumn).Build());
return new TokenIntLiteral(line, startColumn, column - startColumn, parsed);
}
default:
{
var parsed = BigInteger.Zero;
while (TryPeek(out c))
{
if (c == '_')
var parsed = BigInteger.Zero;
while (TryPeek(out c))
{
Consume();
continue;
if (c == '_')
{
Consume();
continue;
}
if (!char.IsDigit(c))
break;
parsed *= 10;
parsed += Consume() - '0';
}
if (!char.IsDigit(c))
break;
parsed *= 10;
parsed += Consume() - '0';
return new TokenIntLiteral(line, startColumn, column - startColumn, parsed);
}
return new TokenIntLiteral(line, startColumn, column - startColumn, parsed);
}
}
}
switch (c)
{
case '"':
{
Consume();
var buf = new StringBuilder();
while (true)
{
if (!TryPeek(out c))
throw new CompileException(Diagnostic.Error("Unterminated string literal").At(fileName, line, column, 0).Build());
if (c == '"')
break;
if (c == '\n')
throw new CompileException(Diagnostic.Error("Unterminated string literal").At(fileName, line, column, 1).Build());
buf.Append(Consume());
}
Consume();
return new TokenStringLiteral(line, startColumn, column - startColumn, buf.ToString());
}
case '{':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenCurly);
}
case '}':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseCurly);
}
case '(':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenParen);
}
case ')':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseParen);
}
case ',':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Comma);
}
case '.':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Period);
}
case ':' when Peek(1) is ':':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ColonColon);
}
case ':':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Colon);
}
case '^':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Caret);
}
case '!' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.BangEqual);
}
case '!':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Bang);
}
case '=' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.EqualEqual);
}
case '=':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Equal);
}
case '<' when Peek(1) is '<':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThanLessThan);
}
case '<' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThanEqual);
}
case '<':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThan);
}
case '>' when Peek(1) is '>':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThanGreaterThan);
}
case '>' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThanEqual);
}
case '>':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThan);
}
case '+' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PlusEqual);
}
case '+':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Plus);
}
case '-' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.MinusEqual);
}
case '-':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Minus);
}
case '*' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.StarEqual);
}
case '*':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Star);
}
case '/' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ForwardSlashEqual);
}
case '/':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ForwardSlash);
}
case '%' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PercentEqual);
}
case '%':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Percent);
}
case '&' when Peek(1) is '&':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.AmpersandAmpersand);
}
case '&':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Ampersand);
}
case '|' when Peek(1) is '|':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PipePipe);
}
case '|':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Pipe);
}
default:
{
if (char.IsLetter(c) || c == '_')
{
Consume();
var buf = new StringBuilder();
while (TryPeek(out c) && (char.IsLetterOrDigit(c) || c == '_'))
buf.Append(Consume());
var value = buf.ToString();
return value switch
while (true)
{
"func" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Func),
"struct" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Struct),
"packed" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Packed),
"enum" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Enum),
"new" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.New),
"match" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Match),
"let" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Let),
"if" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.If),
"else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else),
"while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While),
"return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return),
"module" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Module),
"export" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Export),
"extern" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Extern),
"true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true),
"false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false),
_ => new TokenIdent(line, startColumn, column - startColumn, value)
};
if (!TryPeek(out c))
throw new CompileException(Diagnostic.Error("Unterminated string literal").At(fileName, line, column, 0).Build());
if (c == '"')
break;
if (c == '\n')
throw new CompileException(Diagnostic.Error("Unterminated string literal").At(fileName, line, column, 1).Build());
buf.Append(Consume());
}
Consume();
return new TokenStringLiteral(line, startColumn, column - startColumn, buf.ToString());
}
throw new CompileException(Diagnostic.Error($"Unexpected character '{c}'").At(fileName, line, column, 1).Build());
}
case '{':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenCurly);
}
case '}':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseCurly);
}
case '[':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenSquare);
}
case ']':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseSquare);
}
case '(':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenParen);
}
case ')':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseParen);
}
case ',':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Comma);
}
case '.':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Period);
}
case ':' when Peek(1) is ':':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ColonColon);
}
case ':':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Colon);
}
case '^':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Caret);
}
case '!' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.BangEqual);
}
case '!':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Bang);
}
case '=' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.EqualEqual);
}
case '=':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Equal);
}
case '<' when Peek(1) is '<':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThanLessThan);
}
case '<' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThanEqual);
}
case '<':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThan);
}
case '>' when Peek(1) is '>':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThanGreaterThan);
}
case '>' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThanEqual);
}
case '>':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThan);
}
case '+' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PlusEqual);
}
case '+':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Plus);
}
case '-' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.MinusEqual);
}
case '-':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Minus);
}
case '*' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.StarEqual);
}
case '*':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Star);
}
case '/' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ForwardSlashEqual);
}
case '/':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ForwardSlash);
}
case '%' when Peek(1) is '=':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PercentEqual);
}
case '%':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Percent);
}
case '&' when Peek(1) is '&':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.AmpersandAmpersand);
}
case '&':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Ampersand);
}
case '|' when Peek(1) is '|':
{
Consume();
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PipePipe);
}
case '|':
{
Consume();
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Pipe);
}
default:
{
if (char.IsLetter(c) || c == '_')
{
var buf = new StringBuilder();
while (TryPeek(out c) && (char.IsLetterOrDigit(c) || c == '_'))
buf.Append(Consume());
var value = buf.ToString();
return value switch
{
"func" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Func),
"struct" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Struct),
"packed" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Packed),
"enum" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Enum),
"new" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.New),
"match" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Match),
"let" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Let),
"if" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.If),
"else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else),
"while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While),
"return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return),
"module" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Module),
"export" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Export),
"extern" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Extern),
"true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true),
"false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false),
_ => new TokenIdent(line, startColumn, column - startColumn, value)
};
}
throw new CompileException(Diagnostic.Error($"Unexpected character '{c}'").At(fileName, line, column, 1).Build());
}
}
}
@@ -497,6 +507,8 @@ public enum Symbol
CloseCurly,
OpenParen,
CloseParen,
OpenSquare,
CloseSquare,
Comma,
Period,
Colon,
@@ -566,6 +578,8 @@ public static class TokenExtensions
Symbol.CloseCurly => "}",
Symbol.OpenParen => "(",
Symbol.CloseParen => ")",
Symbol.OpenSquare => "[",
Symbol.CloseSquare => "]",
Symbol.Comma => ",",
Symbol.Period => ".",
Symbol.Colon => ":",