Archived
Compare commits
9
Commits
csharp
..
bee2fcb62b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bee2fcb62b | ||
|
|
f21a6272d4 | ||
|
|
55763b2155 | ||
|
|
2db53ce0db | ||
|
|
10c6bcd483 | ||
|
|
317878dd3b | ||
|
|
a149609f03 | ||
|
|
21c5098cf7 | ||
|
|
294ecb7712 |
-101
@@ -1,101 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
[*.cs]
|
||||
csharp_space_after_keywords_in_control_flow_statements = false
|
||||
csharp_indent_case_contents_when_block = false
|
||||
csharp_style_unused_value_expression_preference = unused_local_variable
|
||||
csharp_prefer_braces = true
|
||||
csharp_prefer_static_local_function = true
|
||||
dotnet_style_prefer_foreach_explicit_cast_in_source = always
|
||||
dotnet_style_prefer_collection_expression = when_types_loosely_match
|
||||
dotnet_diagnostic.IDE0001.severity = warning
|
||||
dotnet_diagnostic.IDE0002.severity = warning
|
||||
dotnet_diagnostic.IDE0004.severity = warning
|
||||
dotnet_diagnostic.IDE0005.severity = warning
|
||||
dotnet_diagnostic.IDE0011.severity = warning
|
||||
dotnet_diagnostic.IDE0020.severity = warning
|
||||
dotnet_diagnostic.IDE0028.severity = warning
|
||||
dotnet_diagnostic.IDE0029.severity = warning
|
||||
dotnet_diagnostic.IDE0030.severity = warning
|
||||
dotnet_diagnostic.IDE0031.severity = warning
|
||||
dotnet_diagnostic.IDE0035.severity = error
|
||||
dotnet_diagnostic.IDE0038.severity = error
|
||||
dotnet_diagnostic.IDE0041.severity = warning
|
||||
dotnet_diagnostic.IDE0042.severity = warning
|
||||
dotnet_diagnostic.IDE0051.severity = warning
|
||||
dotnet_diagnostic.IDE0052.severity = warning
|
||||
dotnet_diagnostic.IDE0054.severity = warning
|
||||
dotnet_diagnostic.IDE0050.severity = warning
|
||||
dotnet_diagnostic.IDE0056.severity = warning
|
||||
dotnet_diagnostic.IDE0057.severity = warning
|
||||
dotnet_diagnostic.IDE0058.severity = warning
|
||||
dotnet_diagnostic.IDE0060.severity = warning
|
||||
dotnet_diagnostic.IDE0062.severity = warning
|
||||
dotnet_diagnostic.IDE0066.severity = warning
|
||||
dotnet_diagnostic.IDE0071.severity = warning
|
||||
dotnet_diagnostic.IDE0074.severity = warning
|
||||
dotnet_diagnostic.IDE0075.severity = warning
|
||||
dotnet_diagnostic.IDE0078.severity = warning
|
||||
dotnet_diagnostic.IDE0080.severity = warning
|
||||
dotnet_diagnostic.IDE0083.severity = warning
|
||||
dotnet_diagnostic.IDE0090.severity = warning
|
||||
dotnet_diagnostic.IDE0100.severity = warning
|
||||
dotnet_diagnostic.IDE0150.severity = warning
|
||||
dotnet_diagnostic.IDE0180.severity = warning
|
||||
dotnet_diagnostic.IDE0200.severity = warning
|
||||
dotnet_diagnostic.IDE0220.severity = warning
|
||||
dotnet_diagnostic.IDE0260.severity = warning
|
||||
dotnet_diagnostic.IDE0270.severity = warning
|
||||
dotnet_diagnostic.IDE0300.severity = warning
|
||||
dotnet_diagnostic.IDE0301.severity = warning
|
||||
dotnet_diagnostic.IDE0302.severity = warning
|
||||
dotnet_diagnostic.IDE0303.severity = warning
|
||||
dotnet_diagnostic.IDE0304.severity = warning
|
||||
dotnet_diagnostic.IDE0305.severity = warning
|
||||
dotnet_diagnostic.CA1508.severity = warning
|
||||
dotnet_diagnostic.CA1514.severity = warning
|
||||
dotnet_diagnostic.CA1515.severity = warning
|
||||
dotnet_diagnostic.CA1801.severity = warning
|
||||
dotnet_diagnostic.CA1802.severity = warning
|
||||
dotnet_diagnostic.CA1805.severity = warning
|
||||
dotnet_diagnostic.CA1806.severity = warning
|
||||
dotnet_diagnostic.CA1810.severity = warning
|
||||
dotnet_diagnostic.CA1814.severity = warning
|
||||
dotnet_diagnostic.CA1820.severity = warning
|
||||
dotnet_diagnostic.CA1822.severity = warning
|
||||
dotnet_diagnostic.CA1823.severity = warning
|
||||
dotnet_diagnostic.CA1825.severity = warning
|
||||
dotnet_diagnostic.CA1826.severity = error
|
||||
dotnet_diagnostic.CA1827.severity = error
|
||||
dotnet_diagnostic.CA1829.severity = error
|
||||
dotnet_diagnostic.CA1830.severity = error
|
||||
dotnet_diagnostic.CA1833.severity = warning
|
||||
dotnet_diagnostic.CA1834.severity = warning
|
||||
dotnet_diagnostic.CA1835.severity = warning
|
||||
dotnet_diagnostic.CA1836.severity = warning
|
||||
dotnet_diagnostic.CA1841.severity = error
|
||||
dotnet_diagnostic.CA1845.severity = warning
|
||||
dotnet_diagnostic.CA1846.severity = warning
|
||||
dotnet_diagnostic.CA1847.severity = warning
|
||||
dotnet_diagnostic.CA1849.severity = warning
|
||||
dotnet_diagnostic.CA1850.severity = warning
|
||||
dotnet_diagnostic.CA1851.severity = warning
|
||||
dotnet_diagnostic.CA1853.severity = warning
|
||||
dotnet_diagnostic.CA1859.severity = warning
|
||||
dotnet_diagnostic.CA1860.severity = warning
|
||||
dotnet_diagnostic.CA1861.severity = warning
|
||||
dotnet_diagnostic.CA1864.severity = warning
|
||||
dotnet_diagnostic.CA1865.severity = warning
|
||||
dotnet_diagnostic.CA1866.severity = warning
|
||||
dotnet_diagnostic.CA1867.severity = warning
|
||||
dotnet_diagnostic.CA2007.severity = warning
|
||||
dotnet_diagnostic.CA2011.severity = error
|
||||
dotnet_diagnostic.CA2248.severity = error
|
||||
+17
-2
@@ -1,2 +1,17 @@
|
||||
bin/
|
||||
obj/
|
||||
# ---> Rust
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
.vscode/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
|
||||
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
|
||||
"name": ".NET Core Launch (console)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/bin/Debug/net8.0/luaaaaah.dll",
|
||||
"args": ["run", "test/stringDataNotSet.lua"],
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
-41
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/luaaaaah.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/luaaaaah.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/luaaaaah.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "luaaaaah"
|
||||
version = "0.1.0"
|
||||
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "luaaaaah"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 0x4261756D
|
||||
|
||||
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.
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace luaaaaah;
|
||||
|
||||
[JsonDerivedType(typeof(Integer), typeDiscriminator: "int")]
|
||||
[JsonDerivedType(typeof(Float), typeDiscriminator: "float")]
|
||||
public interface INumeral
|
||||
{
|
||||
public class Integer(int value) : INumeral
|
||||
{
|
||||
public int value = value;
|
||||
|
||||
public bool RawEqual(INumeral other)
|
||||
{
|
||||
if(other is Integer integer)
|
||||
{
|
||||
return integer.value == value;
|
||||
}
|
||||
// TODO: Check if this is actually doing what is expected
|
||||
return ((Float)other).value == value;
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Numeral Integer {value}";
|
||||
}
|
||||
}
|
||||
public class Float(float value) : INumeral
|
||||
{
|
||||
public float value = value;
|
||||
|
||||
public bool RawEqual(INumeral other)
|
||||
{
|
||||
if(other is Float float_val)
|
||||
{
|
||||
return float_val.value == value;
|
||||
}
|
||||
// TODO: Check if this is actually doing what is expected
|
||||
return ((Integer)other).value == value;
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Numeral Float {value}";
|
||||
}
|
||||
}
|
||||
|
||||
public bool RawEqual(INumeral other);
|
||||
}
|
||||
|
||||
class CodeRegion(CodeLocation start, CodeLocation end)
|
||||
{
|
||||
public CodeLocation start = start;
|
||||
public CodeLocation end = end;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{start}-{end}";
|
||||
}
|
||||
}
|
||||
|
||||
class CodeLocation(int line, int col)
|
||||
{
|
||||
public int line = line;
|
||||
public int col = col;
|
||||
|
||||
public CodeLocation(CodeLocation other) : this(line: other.line, col: other.col) { }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{line + 1}:{col + 1}";
|
||||
}
|
||||
}
|
||||
@@ -1,1639 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace luaaaaah;
|
||||
|
||||
internal class Parser
|
||||
{
|
||||
public class ChunkNode(BlockNode block, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public BlockNode block = block;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class BlockNode(List<StatNode> stats, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public List<StatNode> stats = stats;
|
||||
public RetstatNode? retstat;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
[JsonDerivedType(typeof(Semicolon), typeDiscriminator: "st Semicolon")]
|
||||
[JsonDerivedType(typeof(Assignment), typeDiscriminator: "st Assignment")]
|
||||
[JsonDerivedType(typeof(Functioncall), typeDiscriminator: "st Functioncall")]
|
||||
[JsonDerivedType(typeof(Label), typeDiscriminator: "st Label")]
|
||||
[JsonDerivedType(typeof(Break), typeDiscriminator: "st Break")]
|
||||
[JsonDerivedType(typeof(Goto), typeDiscriminator: "st Goto")]
|
||||
[JsonDerivedType(typeof(Do), typeDiscriminator: "st Do")]
|
||||
[JsonDerivedType(typeof(While), typeDiscriminator: "st While")]
|
||||
[JsonDerivedType(typeof(Repeat), typeDiscriminator: "st Repeat")]
|
||||
[JsonDerivedType(typeof(If), typeDiscriminator: "st If")]
|
||||
[JsonDerivedType(typeof(ForNumerical), typeDiscriminator: "st ForNum")]
|
||||
[JsonDerivedType(typeof(ForGeneric), typeDiscriminator: "st ForGen")]
|
||||
[JsonDerivedType(typeof(Function), typeDiscriminator: "st Function")]
|
||||
[JsonDerivedType(typeof(LocalFunction), typeDiscriminator: "st LocalFunction")]
|
||||
[JsonDerivedType(typeof(Local), typeDiscriminator: "st Local")]
|
||||
public abstract class StatNode
|
||||
{
|
||||
public class Semicolon(CodeRegion region) : StatNode
|
||||
{
|
||||
public CodeRegion region = region;
|
||||
}
|
||||
public class Assignment(VarlistNode lhs, ExplistNode rhs, CodeRegion startRegion, CodeRegion endRegion) : StatNode
|
||||
{
|
||||
public VarlistNode lhs = lhs;
|
||||
public ExplistNode rhs = rhs;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class Functioncall(FunctioncallNode node) : StatNode
|
||||
{
|
||||
public FunctioncallNode node = node;
|
||||
}
|
||||
public class Label(string label, CodeRegion startRegion, CodeRegion endRegion) : StatNode
|
||||
{
|
||||
public CodeRegion startRegion = startRegion;
|
||||
public CodeRegion endRegion = endRegion;
|
||||
public string label = label;
|
||||
}
|
||||
public class Break(CodeRegion region) : StatNode
|
||||
{
|
||||
public CodeRegion region = region;
|
||||
}
|
||||
public class Goto(string label, CodeRegion startRegion, CodeRegion endRegion) : StatNode
|
||||
{
|
||||
public CodeRegion startRegion = startRegion;
|
||||
public CodeRegion endRegion = endRegion;
|
||||
public string label = label;
|
||||
}
|
||||
public class Do(BlockNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode
|
||||
{
|
||||
public CodeRegion startRegion = startRegion;
|
||||
public CodeRegion endRegion = endRegion;
|
||||
public BlockNode node = node;
|
||||
}
|
||||
public class While(ExpNode condition, BlockNode body, CodeRegion startRegion, CodeRegion endRegion) : StatNode
|
||||
{
|
||||
public ExpNode condition = condition;
|
||||
public BlockNode body = body;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class Repeat(ExpNode condition, BlockNode body, CodeRegion startRegion, CodeRegion endRegion) : StatNode
|
||||
{
|
||||
public ExpNode condition = condition;
|
||||
public BlockNode body = body;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class If(ExpNode condition, BlockNode body, List<ElseifNode> elseifs, CodeRegion startRegion, CodeRegion endRegion) : StatNode
|
||||
{
|
||||
public ExpNode condition = condition;
|
||||
public BlockNode body = body;
|
||||
public List<ElseifNode> elseifs = elseifs;
|
||||
public BlockNode? else_;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class ForNumerical(string variable, ExpNode start, ExpNode end, ExpNode? change, BlockNode body, CodeRegion startRegion, CodeRegion endRegion) : StatNode
|
||||
{
|
||||
public string variable = variable;
|
||||
public ExpNode start = start;
|
||||
public ExpNode end = end;
|
||||
public ExpNode? change = change;
|
||||
public BlockNode body = body;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class ForGeneric(List<string> vars, ExplistNode exps, BlockNode body, CodeRegion startRegion, CodeRegion endRegion) : StatNode
|
||||
{
|
||||
public List<string> vars = vars;
|
||||
public ExplistNode exps = exps;
|
||||
public BlockNode body = body;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class Function(FunctionNode node) : StatNode
|
||||
{
|
||||
public FunctionNode node = node;
|
||||
}
|
||||
public class LocalFunction(string name, FuncbodyNode body, CodeRegion startRegion, CodeRegion endRegion) : StatNode
|
||||
{
|
||||
public string name = name;
|
||||
public FuncbodyNode body = body;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class Local(AttnamelistNode attnames, ExplistNode? values, CodeRegion startRegion, CodeRegion endRegion) : StatNode
|
||||
{
|
||||
public AttnamelistNode attnames = attnames;
|
||||
public ExplistNode? values = values;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
}
|
||||
public class RetstatNode(ExplistNode? values, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public ExplistNode? values = values;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class FunctioncallNode(SuffixexpNode function, string? objectArg, ArgsNode args)
|
||||
{
|
||||
public SuffixexpNode function = function;
|
||||
public string? objectArg = objectArg;
|
||||
public ArgsNode args = args;
|
||||
public CodeRegion startRegion = function.startRegion, endRegion = function.endRegion;
|
||||
}
|
||||
public class FunctionNode(FuncnameNode name, FuncbodyNode body, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public FuncnameNode name = name;
|
||||
public FuncbodyNode body = body;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class ExplistNode(List<ExpNode> exps, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public List<ExpNode> exps = exps;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class VarlistNode(List<VarNode> vars, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public List<VarNode> vars = vars;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
[JsonDerivedType(typeof(Normal), typeDiscriminator: "s Normal")]
|
||||
[JsonDerivedType(typeof(Functioncall), typeDiscriminator: "s Functioncall")]
|
||||
public abstract class SuffixexpNode(CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
public class Normal(SuffixexpFirstPart firstPart, List<SuffixexpSuffix> suffixes, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpNode(startRegion, endRegion)
|
||||
{
|
||||
public SuffixexpFirstPart firstPart = firstPart;
|
||||
public List<SuffixexpSuffix> suffixes = suffixes;
|
||||
}
|
||||
public class Functioncall(FunctioncallNode node) : SuffixexpNode(node.startRegion, node.endRegion)
|
||||
{
|
||||
public FunctioncallNode node = node;
|
||||
}
|
||||
}
|
||||
[JsonDerivedType(typeof(Bracketed), typeDiscriminator: "a Bracketed")]
|
||||
[JsonDerivedType(typeof(Tableconstructor), typeDiscriminator: "a Tableconstructor")]
|
||||
[JsonDerivedType(typeof(Literal), typeDiscriminator: "a Literal")]
|
||||
public abstract class ArgsNode(CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
public class Bracketed(ExplistNode? node, CodeRegion startRegion, CodeRegion endRegion) : ArgsNode(startRegion, endRegion)
|
||||
{
|
||||
public ExplistNode? node = node;
|
||||
}
|
||||
public class Tableconstructor(TableconstructorNode node, CodeRegion startRegion, CodeRegion endRegion) : ArgsNode(startRegion, endRegion)
|
||||
{
|
||||
public TableconstructorNode node = node;
|
||||
}
|
||||
public class Literal(string name, CodeRegion startRegion, CodeRegion endRegion) : ArgsNode(startRegion, endRegion)
|
||||
{
|
||||
public string name = name;
|
||||
}
|
||||
}
|
||||
[JsonDerivedType(typeof(Nil), typeDiscriminator: "e Nil")]
|
||||
[JsonDerivedType(typeof(False), typeDiscriminator: "e True")]
|
||||
[JsonDerivedType(typeof(True), typeDiscriminator: "e False")]
|
||||
[JsonDerivedType(typeof(Numeral), typeDiscriminator: "e Numeral")]
|
||||
[JsonDerivedType(typeof(LiteralString), typeDiscriminator: "e Literal")]
|
||||
[JsonDerivedType(typeof(Varargs), typeDiscriminator: "e Varargs")]
|
||||
[JsonDerivedType(typeof(Functiondef), typeDiscriminator: "e Functiondef")]
|
||||
[JsonDerivedType(typeof(Suffixexp), typeDiscriminator: "e Suffixexp")]
|
||||
[JsonDerivedType(typeof(Tableconstructor), typeDiscriminator: "e Tableconstructor")]
|
||||
[JsonDerivedType(typeof(Unop), typeDiscriminator: "e Unop")]
|
||||
[JsonDerivedType(typeof(Binop), typeDiscriminator: "e Binop")]
|
||||
public abstract class ExpNode
|
||||
{
|
||||
public class Nil(CodeRegion region) : ExpNode
|
||||
{
|
||||
public CodeRegion region = region;
|
||||
}
|
||||
public class False(CodeRegion region) : ExpNode
|
||||
{
|
||||
public CodeRegion region = region;
|
||||
}
|
||||
public class True(CodeRegion region) : ExpNode
|
||||
{
|
||||
public CodeRegion region = region;
|
||||
}
|
||||
public class Numeral(INumeral value, CodeRegion region) : ExpNode
|
||||
{
|
||||
public CodeRegion region = region;
|
||||
public INumeral value = value;
|
||||
}
|
||||
public class LiteralString(string value, CodeRegion region) : ExpNode
|
||||
{
|
||||
public CodeRegion region = region;
|
||||
public string value = value;
|
||||
}
|
||||
public class Varargs(CodeRegion region) : ExpNode
|
||||
{
|
||||
public CodeRegion region = region;
|
||||
}
|
||||
public class Functiondef(FuncbodyNode node, CodeRegion startRegion, CodeRegion endRegion) : ExpNode
|
||||
{
|
||||
public CodeRegion startRegion = startRegion;
|
||||
public CodeRegion endRegion = endRegion;
|
||||
public FuncbodyNode node = node;
|
||||
}
|
||||
public class Suffixexp(SuffixexpNode node) : ExpNode
|
||||
{
|
||||
public SuffixexpNode node = node;
|
||||
}
|
||||
public class Tableconstructor(TableconstructorNode node) : ExpNode
|
||||
{
|
||||
public TableconstructorNode node = node;
|
||||
}
|
||||
public class Unop(UnopNode node) : ExpNode
|
||||
{
|
||||
public UnopNode node = node;
|
||||
}
|
||||
public class Binop(BinopNode node) : ExpNode
|
||||
{
|
||||
public BinopNode node = node;
|
||||
}
|
||||
}
|
||||
public class ElseifNode(ExpNode condition, BlockNode body, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public ExpNode condition = condition;
|
||||
public BlockNode body = body;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class FuncnameNode(string name, List<string> dottedNames, string? firstArg, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public string name = name;
|
||||
public List<string> dottedNames = dottedNames;
|
||||
public string? firstArg = firstArg;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class FuncbodyNode(ParlistNode? pars, BlockNode body, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public ParlistNode? pars = pars;
|
||||
public BlockNode body = body;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class AttnamelistNode(List<AttnameNode> attnames, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public List<AttnameNode> attnames = attnames;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
[JsonDerivedType(typeof(Name), typeDiscriminator: "v Name")]
|
||||
[JsonDerivedType(typeof(Indexed), typeDiscriminator: "v Indexed")]
|
||||
[JsonDerivedType(typeof(Member), typeDiscriminator: "v Member")]
|
||||
public abstract class VarNode(CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
public class Name(string name, CodeRegion startRegion, CodeRegion endRegion) : VarNode(startRegion, endRegion)
|
||||
{
|
||||
public string name = name;
|
||||
}
|
||||
public class Indexed(IndexedVarNode node, CodeRegion startRegion, CodeRegion endRegion) : VarNode(startRegion, endRegion)
|
||||
{
|
||||
public IndexedVarNode node = node;
|
||||
}
|
||||
public class Member(MemberVarNode node, CodeRegion startRegion, CodeRegion endRegion) : VarNode(startRegion, endRegion)
|
||||
{
|
||||
public MemberVarNode node = node;
|
||||
}
|
||||
}
|
||||
public class TableconstructorNode(FieldlistNode? exps, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public FieldlistNode? exps = exps;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class UnopNode(UnopType type, ExpNode exp, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public UnopType type = type;
|
||||
public ExpNode exp = exp;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public enum UnopType
|
||||
{
|
||||
Minus, LogicalNot, Length, BinaryNot,
|
||||
}
|
||||
public class BinopNode(ExpNode lhs, BinopType type, ExpNode rhs, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public ExpNode lhs = lhs;
|
||||
public BinopType type = type;
|
||||
public ExpNode rhs = rhs;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public enum BinopType
|
||||
{
|
||||
LogicalOr,
|
||||
LogicalAnd,
|
||||
Lt, Gt, LtEquals, GtEquals, NotEquals, Equals,
|
||||
BinaryOr,
|
||||
BinaryNot,
|
||||
BinaryAnd,
|
||||
Shl, Shr,
|
||||
Concat,
|
||||
Add, Sub,
|
||||
Mul, Div, IntDiv, Mod,
|
||||
Exp,
|
||||
}
|
||||
public class ParlistNode(List<string> names, bool hasVarargs, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public List<string> names = names;
|
||||
public bool hasVarargs = hasVarargs;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class AttnameNode(string name, string? attribute, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public string name = name;
|
||||
public string? attribute = attribute;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class IndexedVarNode(SuffixexpNode value, ExpNode index, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public SuffixexpNode value = value;
|
||||
public ExpNode index = index;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class MemberVarNode(SuffixexpNode value, string name, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public SuffixexpNode value = value;
|
||||
public string name = name;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
[JsonDerivedType(typeof(Name), typeDiscriminator: "sfp Name")]
|
||||
[JsonDerivedType(typeof(BracketedExp), typeDiscriminator: "sfp BracketedExp")]
|
||||
public abstract class SuffixexpFirstPart(CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
public class Name(string name, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpFirstPart(startRegion, endRegion)
|
||||
{
|
||||
public string name = name;
|
||||
}
|
||||
public class BracketedExp(ExpNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpFirstPart(startRegion, endRegion)
|
||||
{
|
||||
public ExpNode node = node;
|
||||
}
|
||||
}
|
||||
[JsonDerivedType(typeof(Dot))]
|
||||
[JsonDerivedType(typeof(Indexed))]
|
||||
[JsonDerivedType(typeof(Args))]
|
||||
[JsonDerivedType(typeof(ArgsFirstArg))]
|
||||
public abstract class SuffixexpSuffix(CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
public class Dot(string name, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpSuffix(startRegion, endRegion)
|
||||
{
|
||||
public string name = name;
|
||||
}
|
||||
public class Indexed(ExpNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpSuffix(startRegion, endRegion)
|
||||
{
|
||||
public ExpNode node = node;
|
||||
}
|
||||
public class Args(ArgsNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpSuffix(startRegion, endRegion)
|
||||
{
|
||||
public ArgsNode node = node;
|
||||
}
|
||||
public class ArgsFirstArg(ArgsFirstArgNode node) : SuffixexpSuffix(node.startRegion, node.endRegion)
|
||||
{
|
||||
public ArgsFirstArgNode node = node;
|
||||
}
|
||||
}
|
||||
public class FieldlistNode(List<FieldNode> exps, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public List<FieldNode> exps = exps;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class ArgsFirstArgNode(string name, ArgsNode rest, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public string name = name;
|
||||
public ArgsNode rest = rest;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
[JsonDerivedType(typeof(IndexedAssignment))]
|
||||
[JsonDerivedType(typeof(Assignment))]
|
||||
[JsonDerivedType(typeof(Exp))]
|
||||
public abstract class FieldNode(CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
public class IndexedAssignment(IndexedAssignmentNode node) : FieldNode(node.startRegion, node.endRegion)
|
||||
{
|
||||
public IndexedAssignmentNode node = node;
|
||||
}
|
||||
public class Assignment(FieldAssignmentNode node) : FieldNode(node.startRegion, node.endRegion)
|
||||
{
|
||||
public FieldAssignmentNode node = node;
|
||||
}
|
||||
public class Exp(ExpNode node, CodeRegion startRegion, CodeRegion endRegion) : FieldNode(startRegion, endRegion)
|
||||
{
|
||||
public ExpNode node = node;
|
||||
}
|
||||
}
|
||||
public class IndexedAssignmentNode(ExpNode index, ExpNode rhs, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public ExpNode index = index;
|
||||
public ExpNode rhs = rhs;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
public class FieldAssignmentNode(string lhs, ExpNode rhs, CodeRegion startRegion, CodeRegion endRegion)
|
||||
{
|
||||
public string lhs = lhs;
|
||||
public ExpNode rhs = rhs;
|
||||
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
||||
}
|
||||
|
||||
public int index;
|
||||
|
||||
public ChunkNode Parse(Token[] tokens)
|
||||
{
|
||||
return ParseChunk(tokens);
|
||||
}
|
||||
|
||||
public ChunkNode ParseChunk(Token[] tokens)
|
||||
{
|
||||
BlockNode body = ParseBlock(tokens);
|
||||
return new ChunkNode(block: body, startRegion: body.startRegion, endRegion: body.endRegion);
|
||||
}
|
||||
|
||||
public BlockNode ParseBlock(Token[] tokens)
|
||||
{
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
List<StatNode> stats = [];
|
||||
while(index < tokens.Length &&
|
||||
tokens[index].type != TokenType.Return &&
|
||||
tokens[index].type != TokenType.End &&
|
||||
tokens[index].type != TokenType.Elseif &&
|
||||
tokens[index].type != TokenType.Else &&
|
||||
tokens[index].type != TokenType.Until)
|
||||
{
|
||||
stats.Add(ParseStat(tokens));
|
||||
}
|
||||
BlockNode ret = new(stats: stats, startRegion: startRegion, endRegion: (stats.Count == 0 && index > 0) ? startRegion : tokens[index - 1].region);
|
||||
if(index < tokens.Length && tokens[index].type == TokenType.Return)
|
||||
{
|
||||
ret.retstat = ParseRetstat(tokens);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public StatNode ParseStat(Token[] tokens)
|
||||
{
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}");
|
||||
}
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
switch(tokens[index].type)
|
||||
{
|
||||
case TokenType.Semicolon:
|
||||
{
|
||||
index += 1;
|
||||
return new StatNode.Semicolon(startRegion);
|
||||
}
|
||||
case TokenType.Break:
|
||||
{
|
||||
index += 1;
|
||||
return new StatNode.Break(startRegion);
|
||||
}
|
||||
case TokenType.Goto:
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name for goto at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected name for goto, got {tokens[index].type}");
|
||||
}
|
||||
StatNode.Goto ret = new(label: ((Token.StringData)tokens[index].data!).data, startRegion: startRegion, endRegion: tokens[index].region);
|
||||
index += 1;
|
||||
return ret;
|
||||
}
|
||||
case TokenType.Do:
|
||||
{
|
||||
index += 1;
|
||||
BlockNode body = ParseBlock(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected end for `do` at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.End)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `end` to close `do` at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
StatNode.Do ret = new(node: body, startRegion: startRegion, endRegion: tokens[index].region);
|
||||
index += 1;
|
||||
return ret;
|
||||
}
|
||||
case TokenType.While:
|
||||
{
|
||||
index += 1;
|
||||
ExpNode condition = ParseExp(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `do` after condition of while loop starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Do)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `do` after condition of while starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
BlockNode body = ParseBlock(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` after body of while loop starting at {startRegion}");
|
||||
}
|
||||
CodeRegion endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new StatNode.While(condition: condition, body: body, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
case TokenType.Repeat:
|
||||
{
|
||||
index += 1;
|
||||
BlockNode body = ParseBlock(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `until` after body of until loop starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Until)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `until` after block of until loop starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
ExpNode conditon = ParseExp(tokens);
|
||||
return new StatNode.Repeat(condition: conditon, body: body, startRegion: startRegion, endRegion: tokens[index - 1].region);
|
||||
}
|
||||
case TokenType.If:
|
||||
{
|
||||
index += 1;
|
||||
ExpNode condition = ParseExp(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `then` after condition of if starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Then)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `then` after condition of if starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
BlockNode body = ParseBlock(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` after body of if starting at {startRegion}");
|
||||
}
|
||||
List<ElseifNode> elseifs = [];
|
||||
while(tokens[index].type == TokenType.Elseif)
|
||||
{
|
||||
CodeRegion elseifStartRegion = tokens[index].region;
|
||||
index += 1;
|
||||
ExpNode elseifCondition = ParseExp(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `then` after condition of elseif starting at {elseifStartRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Then)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `then` after condition of elseif starting at {elseifStartRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
BlockNode elseifBody = ParseBlock(tokens);
|
||||
elseifs.Add(new(condition: elseifCondition, body: elseifBody, startRegion: elseifStartRegion, endRegion: elseifBody.endRegion));
|
||||
}
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` after else-ifs of if starting at {startRegion}");
|
||||
}
|
||||
StatNode.If ret = new(condition: condition, body: body, elseifs: elseifs, startRegion: startRegion, endRegion: tokens[index - 1].region);
|
||||
if(tokens[index].type == TokenType.Else)
|
||||
{
|
||||
index += 1;
|
||||
ret.else_ = ParseBlock(tokens);
|
||||
}
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` to close if starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.End)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `end` to close if starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
ret.endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return ret;
|
||||
}
|
||||
case TokenType.For:
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name after for at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected name after for at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
string variable = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 1;
|
||||
switch(tokens[index].type)
|
||||
{
|
||||
case TokenType.Equals:
|
||||
{
|
||||
index += 1;
|
||||
ExpNode start = ParseExp(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `,` after start value of numerical for loop starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Comma)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `,` after start value of for loop starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
ExpNode end = ParseExp(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `do` or `,` after end value of numerical for loop starting at {startRegion}");
|
||||
}
|
||||
ExpNode? change = null;
|
||||
if(tokens[index].type == TokenType.Comma)
|
||||
{
|
||||
index += 1;
|
||||
change = ParseExp(tokens);
|
||||
}
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
string t = (change == null) ? "`do` or `,` after end value" : "`do` after change value";
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected {t} of numerical for loop starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Do)
|
||||
{
|
||||
string t = (change == null) ? "`do` or `,` after end value" : "`do` after change value";
|
||||
throw new Exception($"{tokens[index].region}: Expected {t} of numerical for loop starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
BlockNode body = ParseBlock(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` to close numerical for loop starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.End)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `end` to close numerical for loop starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
CodeRegion endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new StatNode.ForNumerical(variable: variable, start: start, end: end, change: change, body: body, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
case TokenType.Comma:
|
||||
{
|
||||
List<string> names = [variable];
|
||||
while(tokens[index].type == TokenType.Comma)
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected another name in name list of for-in loop starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected another name in name list of for-in loop starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
names.Add(((Token.StringData)tokens[index].data!).data);
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `,` or `in` in for-in loop starting at {startRegion}");
|
||||
}
|
||||
}
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `in` after name list of for-in loop starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.In)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `in` after name list of for-in loop starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
ExplistNode exps = ParseExplist(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `do` after exp list of for-in loop starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Do)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `do` after exp list of for-in loop starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
BlockNode body = ParseBlock(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` to close for-in loop starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.End)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `end` to close for-in loop starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
CodeRegion endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new StatNode.ForGeneric(vars: names, exps: exps, body: body, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
case TokenType.In:
|
||||
{
|
||||
index += 1;
|
||||
ExplistNode exps = ParseExplist(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `do` after exp list of for-in loop starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Do)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `do` after exp list of for-in loop starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
BlockNode body = ParseBlock(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` to close for-in loop starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.End)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `end` to close for-in loop starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
CodeRegion endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new StatNode.ForGeneric(vars: [variable], exps: exps, body: body, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception($"{tokens[index].type}: Expected either `=`, `,` or `in` after first name of for loop {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
case TokenType.Function:
|
||||
{
|
||||
index += 1;
|
||||
FuncnameNode name = ParseFuncname(tokens);
|
||||
FuncbodyNode body = ParseFuncbody(tokens);
|
||||
return new StatNode.Function(new(name: name, body: body, startRegion: startRegion, endRegion: body.endRegion));
|
||||
}
|
||||
case TokenType.Local:
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length} after `local` at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type == TokenType.Function)
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name of local function starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected name of local function starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
string name = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 1;
|
||||
FuncbodyNode body = ParseFuncbody(tokens);
|
||||
return new StatNode.LocalFunction(name: name, body: body, startRegion: startRegion, endRegion: body.endRegion);
|
||||
}
|
||||
else
|
||||
{
|
||||
AttnamelistNode attnames = ParseAttnamelist(tokens);
|
||||
StatNode.Local ret = new(attnames: attnames, values: null, startRegion: startRegion, endRegion: attnames.endRegion);
|
||||
if(index < tokens.Length && tokens[index].type == TokenType.Equals)
|
||||
{
|
||||
index += 1;
|
||||
ret.values = ParseExplist(tokens);
|
||||
ret.endRegion = ret.values.endRegion;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
case TokenType.ColonColon:
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name of label starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected name of label starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
string name = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `::` after label name starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.ColonColon)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `::` after label name starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
CodeRegion endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new StatNode.Label(label: name, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
case TokenType.Name:
|
||||
case TokenType.RoundOpen:
|
||||
{
|
||||
SuffixexpNode suffixExp = ParseSuffixExp(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
if(suffixExp is SuffixexpNode.Normal)
|
||||
{
|
||||
throw new Exception($"{startRegion}: Expected function call, got normal suffix expression");
|
||||
}
|
||||
if(suffixExp is SuffixexpNode.Functioncall functioncall)
|
||||
{
|
||||
return new StatNode.Functioncall(node: functioncall.node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(tokens[index].type)
|
||||
{
|
||||
case TokenType.Equals:
|
||||
{
|
||||
index += 1;
|
||||
List<VarNode> lhs = [SuffixExpToVar(suffixExp)];
|
||||
ExplistNode rhs = ParseExplist(tokens);
|
||||
return new StatNode.Assignment(lhs: new(vars: lhs, startRegion: startRegion, endRegion: suffixExp.endRegion), rhs: rhs, startRegion: startRegion, endRegion: rhs.endRegion);
|
||||
}
|
||||
case TokenType.Comma:
|
||||
{
|
||||
List<VarNode> vars = [SuffixExpToVar(suffixExp)];
|
||||
while(index < tokens.Length && tokens[index].type == TokenType.Comma)
|
||||
{
|
||||
index += 1;
|
||||
vars.Add(ParseVar(tokens));
|
||||
}
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `=` for assignment starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Equals)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `=` for assignment starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
VarlistNode varlistNode = new(vars: vars, startRegion: startRegion, endRegion: vars[^1].endRegion);
|
||||
ExplistNode rhs = ParseExplist(tokens);
|
||||
return new StatNode.Assignment(lhs: varlistNode, rhs: rhs, startRegion: startRegion, endRegion: rhs.endRegion);
|
||||
}
|
||||
}
|
||||
if(suffixExp is SuffixexpNode.Normal)
|
||||
{
|
||||
throw new Exception($"{startRegion}: Expected function call, got normal suffix expression");
|
||||
}
|
||||
if(suffixExp is SuffixexpNode.Functioncall functioncall)
|
||||
{
|
||||
return new StatNode.Functioncall(node: functioncall.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
throw new Exception($"Unexpected token {tokens[index]} at {startRegion}");
|
||||
}
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private VarNode ParseVar(Token[] tokens)
|
||||
{
|
||||
return SuffixExpToVar(ParseSuffixExp(tokens));
|
||||
}
|
||||
|
||||
private static VarNode SuffixExpToVar(SuffixexpNode suffixExp)
|
||||
{
|
||||
if(suffixExp is not SuffixexpNode.Normal normal)
|
||||
{
|
||||
throw new Exception($"Expected a normal suffix expression to convert to var at {suffixExp.startRegion}-{suffixExp.endRegion}");
|
||||
}
|
||||
if(normal.suffixes.Count == 0)
|
||||
{
|
||||
if(normal.firstPart is not SuffixexpFirstPart.Name name)
|
||||
{
|
||||
throw new Exception($"Expected a name as first part of suffix expression to convert to var at {normal.firstPart.startRegion}-{normal.firstPart.endRegion}");
|
||||
}
|
||||
return new VarNode.Name(name: name.name, startRegion: suffixExp.startRegion, endRegion: suffixExp.endRegion);
|
||||
}
|
||||
SuffixexpSuffix last = normal.suffixes[^1];
|
||||
_ = normal.suffixes.Remove(last);
|
||||
return last switch
|
||||
{
|
||||
SuffixexpSuffix.Dot dot => new VarNode.Member(node: new(name: dot.name, value: normal, startRegion: suffixExp.startRegion, endRegion: suffixExp.endRegion), startRegion: suffixExp.startRegion, endRegion: dot.endRegion),
|
||||
SuffixexpSuffix.Indexed indexed => new VarNode.Indexed(node: new(index: indexed.node, value: normal, startRegion: suffixExp.startRegion, endRegion: suffixExp.endRegion), startRegion: suffixExp.startRegion, endRegion: indexed.endRegion),
|
||||
_ => throw new Exception($"Expected dot or indexed suffix expression to convert to var at {last.startRegion}-{last.endRegion}")
|
||||
};
|
||||
}
|
||||
|
||||
private SuffixexpNode ParseSuffixExp(Token[] tokens)
|
||||
{
|
||||
// primaryexp { '.' 'Name' | '[' exp']' | ':' 'Name' args | args }
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}");
|
||||
}
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
SuffixexpFirstPart firstPart;
|
||||
switch(tokens[index].type)
|
||||
{
|
||||
case TokenType.Name:
|
||||
{
|
||||
string name = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 1;
|
||||
firstPart = new SuffixexpFirstPart.Name(name, startRegion, startRegion);
|
||||
}
|
||||
break;
|
||||
case TokenType.RoundOpen:
|
||||
{
|
||||
index += 1;
|
||||
ExpNode inner = ParseExp(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `)` to close bracketed expression starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.RoundClosed)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `)` to close bracketed expression at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
firstPart = new SuffixexpFirstPart.BracketedExp(node: inner, startRegion: startRegion, endRegion: tokens[index].region);
|
||||
index += 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"{startRegion}: Expected either `)` or name as first part of suffix-expression, got {tokens[index].type}");
|
||||
}
|
||||
List<SuffixexpSuffix> suffixes = [];
|
||||
bool shouldContinue = true;
|
||||
while(shouldContinue && index < tokens.Length)
|
||||
{
|
||||
CodeRegion suffixStartRegion = tokens[index].region;
|
||||
switch(tokens[index].type)
|
||||
{
|
||||
case TokenType.Dot:
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name in dotted suffix of suffix expression starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected name in dotted suffix of suffix expression at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
CodeRegion suffixEndRegion = tokens[index].region;
|
||||
string name = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 1;
|
||||
suffixes.Add(new SuffixexpSuffix.Dot(name, startRegion: suffixStartRegion, endRegion: suffixEndRegion));
|
||||
}
|
||||
break;
|
||||
case TokenType.SquareOpen:
|
||||
{
|
||||
index += 1;
|
||||
ExpNode inner = ParseExp(tokens);
|
||||
suffixes.Add(new SuffixexpSuffix.Indexed(node: inner, startRegion: suffixStartRegion, endRegion: tokens[index - 1].region));
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `]` to close indexed suffix of suffix-expression starting at {suffixStartRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.SquareClosed)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `]` to close indexed suffix of suffix-expression at {suffixStartRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
break;
|
||||
case TokenType.Colon:
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name as first arg after `:` in args suffix in suffix-expression starting at {suffixStartRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected name as first arg after `:` in args suffix in suffix-expression at {suffixStartRegion}, got {tokens[index].type}");
|
||||
}
|
||||
string name = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 1;
|
||||
ArgsNode args = ParseArgs(tokens);
|
||||
suffixes.Add(new SuffixexpSuffix.ArgsFirstArg(new(name, rest: args, startRegion: suffixStartRegion, endRegion: args.endRegion)));
|
||||
}
|
||||
break;
|
||||
case TokenType.RoundOpen:
|
||||
case TokenType.CurlyOpen:
|
||||
case TokenType.StringLiteral:
|
||||
{
|
||||
ArgsNode args = ParseArgs(tokens);
|
||||
suffixes.Add(new SuffixexpSuffix.Args(node: args, startRegion: suffixStartRegion, endRegion: args.endRegion));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
shouldContinue = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
CodeRegion endRegion;
|
||||
if(suffixes.Count > 0)
|
||||
{
|
||||
endRegion = suffixes[^1].endRegion;
|
||||
SuffixexpNode? ret = suffixes[^1] switch
|
||||
{
|
||||
SuffixexpSuffix.Args args => new SuffixexpNode.Functioncall(
|
||||
node: new(
|
||||
function: new SuffixexpNode.Normal(firstPart, suffixes[..^1], startRegion, args.endRegion),
|
||||
args: args.node,
|
||||
objectArg: null
|
||||
)
|
||||
),
|
||||
SuffixexpSuffix.ArgsFirstArg node => new SuffixexpNode.Functioncall(
|
||||
node: new(
|
||||
function: new SuffixexpNode.Normal(firstPart: firstPart, suffixes: suffixes[..^1], startRegion, node.endRegion),
|
||||
objectArg: node.node.name,
|
||||
args: node.node.rest
|
||||
)
|
||||
),
|
||||
_ => null,
|
||||
};
|
||||
if(ret is not null)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
endRegion = firstPart.endRegion;
|
||||
}
|
||||
|
||||
return new SuffixexpNode.Normal(firstPart: firstPart, suffixes: suffixes, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
|
||||
private ArgsNode ParseArgs(Token[] tokens)
|
||||
{
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `(`, `{{` or string to start args");
|
||||
}
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
switch(tokens[index].type)
|
||||
{
|
||||
case TokenType.RoundOpen:
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected explist or `)` to continue args starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type == TokenType.RoundClosed)
|
||||
{
|
||||
CodeRegion endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new ArgsNode.Bracketed(null, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
ExplistNode exps = ParseExplist(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `)` to close args starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.RoundClosed)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `)` to close args starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
return new ArgsNode.Bracketed(node: exps, startRegion: startRegion, endRegion: exps.endRegion);
|
||||
}
|
||||
case TokenType.CurlyOpen:
|
||||
{
|
||||
TableconstructorNode node = ParseTableconstructor(tokens);
|
||||
return new ArgsNode.Tableconstructor(node: node, startRegion: startRegion, endRegion: node.endRegion);
|
||||
}
|
||||
case TokenType.StringLiteral:
|
||||
{
|
||||
string value = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 1;
|
||||
return new ArgsNode.Literal(name: value, startRegion: startRegion, endRegion: startRegion);
|
||||
}
|
||||
default:
|
||||
throw new Exception($"{tokens[index].region}: Expected explist or `)` to continue args starting at {startRegion}");
|
||||
}
|
||||
}
|
||||
|
||||
private TableconstructorNode ParseTableconstructor(Token[] tokens)
|
||||
{
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `{{` to start tableconstructor");
|
||||
}
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
if(tokens[index].type != TokenType.CurlyOpen)
|
||||
{
|
||||
throw new Exception($"{startRegion}: Expected `{{` to start tableconstructor, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
if(index < tokens.Length && tokens[index].type == TokenType.CurlyClosed)
|
||||
{
|
||||
CodeRegion emptyEndRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new TableconstructorNode(exps: null, startRegion: startRegion, endRegion: emptyEndRegion);
|
||||
}
|
||||
FieldlistNode fields = ParseFieldlist(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `}}` to close tableconstructor starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.CurlyClosed)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `}}` to close tableconstructor starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
CodeRegion endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new TableconstructorNode(exps: fields, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
|
||||
private FieldlistNode ParseFieldlist(Token[] tokens)
|
||||
{
|
||||
List<FieldNode> fields = [ParseField(tokens)];
|
||||
while(index < tokens.Length && IsFieldsep(tokens[index]))
|
||||
{
|
||||
index += 1;
|
||||
if(index < tokens.Length && tokens[index].type is TokenType.SquareOpen or
|
||||
TokenType.Name or TokenType.Nil or TokenType.True or TokenType.False or TokenType.Numeral or TokenType.StringLiteral or
|
||||
TokenType.DotDotDot or TokenType.CurlyOpen or TokenType.Function or TokenType.Minus or TokenType.Hash or TokenType.Not or TokenType.Nil or TokenType.RoundOpen)
|
||||
{
|
||||
fields.Add(ParseField(tokens));
|
||||
}
|
||||
}
|
||||
// NOTE: Since at least 1 field is parsed the list accesses are safe
|
||||
return new FieldlistNode(exps: fields, startRegion: fields[0].startRegion, endRegion: fields[^1].endRegion);
|
||||
}
|
||||
|
||||
private static bool IsFieldsep(Token token) => token.type is TokenType.Comma or TokenType.Semicolon;
|
||||
|
||||
private FieldNode ParseField(Token[] tokens)
|
||||
{
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `[` or name to start field");
|
||||
}
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
switch(tokens[index].type)
|
||||
{
|
||||
case TokenType.SquareOpen:
|
||||
{
|
||||
index += 1;
|
||||
ExpNode indexNode = ParseExp(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `]` to close indexed field in indexed field assignment starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.SquareClosed)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `]` to close indexed field starting in indexed field assignment at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `=` to continue indexed field assignment starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Equals)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `=` to continue indexed field assignment starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
ExpNode rhs = ParseExp(tokens);
|
||||
return new FieldNode.IndexedAssignment(node: new(index: indexNode, rhs: rhs, startRegion: startRegion, endRegion: tokens[index - 1].region));
|
||||
}
|
||||
case TokenType.Name:
|
||||
{
|
||||
if(index + 1 < tokens.Length && tokens[index + 1].type == TokenType.Equals)
|
||||
{
|
||||
string name = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 2;
|
||||
ExpNode rhs = ParseExp(tokens);
|
||||
return new FieldNode.Assignment(node: new(lhs: name, rhs: rhs, startRegion: startRegion, endRegion: tokens[index - 1].region));
|
||||
}
|
||||
ExpNode exp = ParseExp(tokens);
|
||||
return new FieldNode.Exp(node: exp, startRegion: startRegion, endRegion: tokens[index - 1].region);
|
||||
}
|
||||
default:
|
||||
{
|
||||
ExpNode exp = ParseExp(tokens);
|
||||
return new FieldNode.Exp(node: exp, startRegion: startRegion, endRegion: tokens[index - 1].region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AttnamelistNode ParseAttnamelist(Token[] tokens)
|
||||
{
|
||||
List<AttnameNode> attnames = [ParseAttname(tokens)];
|
||||
while(index < tokens.Length && tokens[index].type == TokenType.Comma)
|
||||
{
|
||||
index += 1;
|
||||
attnames.Add(ParseAttname(tokens));
|
||||
}
|
||||
// NOTE: Since at least 1 attname is parsed the list accesses are safe
|
||||
return new(attnames: attnames, startRegion: attnames[0].startRegion, endRegion: attnames[^1].endRegion);
|
||||
}
|
||||
|
||||
private AttnameNode ParseAttname(Token[] tokens)
|
||||
{
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name to start attname");
|
||||
}
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected name to start attname at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
string name = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 1;
|
||||
if(index < tokens.Length && tokens[index].type == TokenType.Lt)
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected attribute name of attname starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected attribute name of attname at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
string attribute = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `>` to close attribute of attname starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Gt)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `>` to close attribute of attname starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
CodeRegion endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new AttnameNode(name: name, attribute: attribute, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
return new AttnameNode(name: name, attribute: null, startRegion: startRegion, endRegion: startRegion);
|
||||
}
|
||||
|
||||
private FuncbodyNode ParseFuncbody(Token[] tokens)
|
||||
{
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `(` to start funcbody");
|
||||
}
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
if(tokens[index].type != TokenType.RoundOpen)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `(` to start funcbody at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
ParlistNode? pars;
|
||||
if(index < tokens.Length && tokens[index].type == TokenType.RoundClosed)
|
||||
{
|
||||
index += 1;
|
||||
pars = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
pars = ParseParlist(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `)` to close parlist of funcbody starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.RoundClosed)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `)` to close parlist of funcbody at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
BlockNode body = ParseBlock(tokens);
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` to close funcbody starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.End)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `end` to close funcbody starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
CodeRegion endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new FuncbodyNode(pars: pars, body: body, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
|
||||
private ParlistNode ParseParlist(Token[] tokens)
|
||||
{
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `...` or name to start parlist");
|
||||
}
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
if(tokens[index].type == TokenType.DotDotDot)
|
||||
{
|
||||
index += 1;
|
||||
return new ParlistNode(names: [], hasVarargs: true, startRegion: startRegion, endRegion: startRegion);
|
||||
}
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{startRegion}: Expected `...` or name to start parlist, got {tokens[index].type}");
|
||||
}
|
||||
List<string> names = [((Token.StringData)tokens[index].data!).data];
|
||||
index += 1;
|
||||
while(index < tokens.Length && tokens[index].type == TokenType.Comma)
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `...` or name to continue parlist starting at {startRegion}");
|
||||
}
|
||||
switch(tokens[index].type)
|
||||
{
|
||||
case TokenType.Name:
|
||||
{
|
||||
names.Add(((Token.StringData)tokens[index].data!).data);
|
||||
index += 1;
|
||||
}
|
||||
break;
|
||||
case TokenType.DotDotDot:
|
||||
{
|
||||
CodeRegion endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new ParlistNode(names: names, hasVarargs: true, startRegion: startRegion, endRegion: endRegion);
|
||||
};
|
||||
default:
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected `...` or name to continue parlist starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ParlistNode(names: names, hasVarargs: false, startRegion: startRegion, endRegion: tokens[index - 1].region);
|
||||
}
|
||||
|
||||
private FuncnameNode ParseFuncname(Token[] tokens)
|
||||
{
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name to start funcname");
|
||||
}
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{startRegion}: Expected name to start funcname, got {tokens[index].type}");
|
||||
}
|
||||
string name = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 1;
|
||||
List<string> dottedNames = [];
|
||||
while(index < tokens.Length && tokens[index].type == TokenType.Dot)
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name in dotted funcname starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected name in dotted funcname starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
dottedNames.Add(((Token.StringData)tokens[index].data!).data);
|
||||
index += 1;
|
||||
}
|
||||
if(index < tokens.Length && tokens[index].type == TokenType.Colon)
|
||||
{
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name as first arg name after `:` in funcname starting at {startRegion}");
|
||||
}
|
||||
if(tokens[index].type != TokenType.Name)
|
||||
{
|
||||
throw new Exception($"{tokens[index].region}: Expected name as first arg name after `:` in funcname starting at {startRegion}, got {tokens[index].type}");
|
||||
}
|
||||
string firstArg = ((Token.StringData)tokens[index].data!).data;
|
||||
CodeRegion endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
return new FuncnameNode(name: name, dottedNames: dottedNames, firstArg: firstArg, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
return new FuncnameNode(name: name, dottedNames: dottedNames, firstArg: null, startRegion: startRegion, endRegion: tokens[index - 1].region);
|
||||
}
|
||||
|
||||
private ExplistNode ParseExplist(Token[] tokens)
|
||||
{
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
List<ExpNode> exps = [ParseExp(tokens)];
|
||||
while(index < tokens.Length && tokens[index].type == TokenType.Comma)
|
||||
{
|
||||
index += 1;
|
||||
exps.Add(ParseExp(tokens));
|
||||
}
|
||||
return new ExplistNode(exps: exps, startRegion: startRegion, endRegion: tokens[index - 1].region);
|
||||
}
|
||||
|
||||
private ExpNode ParseExp(Token[] tokens)
|
||||
{
|
||||
ExpNode lhs = ParseExpPrimary(tokens);
|
||||
return ParseExpPrecedence(tokens, lhs, 0);
|
||||
}
|
||||
|
||||
private ExpNode ParseExpPrecedence(Token[] tokens, ExpNode lhs, int minPrecedence)
|
||||
{
|
||||
ExpNode currentLhs = lhs;
|
||||
while(index < tokens.Length && IsBinop(tokens[index]))
|
||||
{
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
int precedence = GetPrecedence(tokens[index]);
|
||||
if(precedence < minPrecedence)
|
||||
{
|
||||
break;
|
||||
}
|
||||
BinopType op = GetBinopType(tokens[index]);
|
||||
index += 1;
|
||||
ExpNode rhs = ParseExpPrimary(tokens);
|
||||
while(index < tokens.Length && IsBinop(tokens[index]) && (GetPrecedence(tokens[index]) > precedence || (GetPrecedence(tokens[index]) == precedence && IsRightAssociative(tokens[index]))))
|
||||
{
|
||||
int associativityBoost = (GetPrecedence(tokens[index]) == precedence) ? 0 : 1;
|
||||
rhs = ParseExpPrecedence(tokens, lhs: rhs, minPrecedence: precedence + associativityBoost);
|
||||
}
|
||||
currentLhs = new ExpNode.Binop(node: new(lhs: currentLhs, type: op, rhs: rhs, startRegion: startRegion, endRegion: tokens[index - 1].region));
|
||||
}
|
||||
return currentLhs;
|
||||
}
|
||||
|
||||
private static bool IsRightAssociative(Token token) => token.type is TokenType.DotDot or TokenType.Caret;
|
||||
|
||||
private static BinopType GetBinopType(Token token) => token.type switch
|
||||
{
|
||||
TokenType.Or => BinopType.LogicalOr,
|
||||
TokenType.And => BinopType.LogicalAnd,
|
||||
TokenType.Lt => BinopType.Lt,
|
||||
TokenType.Gt => BinopType.Gt,
|
||||
TokenType.LtEquals => BinopType.LtEquals,
|
||||
TokenType.GtEquals => BinopType.GtEquals,
|
||||
TokenType.LtLt => BinopType.Shl,
|
||||
TokenType.GtGt => BinopType.Shr,
|
||||
TokenType.TildeEquals => BinopType.NotEquals,
|
||||
TokenType.EqualsEquals => BinopType.Equals,
|
||||
TokenType.Pipe => BinopType.BinaryOr,
|
||||
TokenType.Tilde => BinopType.BinaryNot,
|
||||
TokenType.Ampersand => BinopType.BinaryAnd,
|
||||
TokenType.DotDot => BinopType.Concat,
|
||||
TokenType.Plus => BinopType.Add,
|
||||
TokenType.Minus => BinopType.Sub,
|
||||
TokenType.Star => BinopType.Mul,
|
||||
TokenType.Slash => BinopType.Div,
|
||||
TokenType.SlashSlash => BinopType.IntDiv,
|
||||
TokenType.Percent => BinopType.Mod,
|
||||
TokenType.Caret => BinopType.Exp,
|
||||
_ => throw new Exception($"{token.region}: Expected binary operator with precedence, got {token.type}"),
|
||||
};
|
||||
|
||||
private static int GetPrecedence(Token token) => token.type switch
|
||||
{
|
||||
TokenType.Or => 2,
|
||||
TokenType.And => 4,
|
||||
TokenType.Lt or TokenType.Gt or TokenType.LtEquals or TokenType.GtEquals or TokenType.TildeEquals or TokenType.EqualsEquals => 6,
|
||||
TokenType.Pipe => 8,
|
||||
TokenType.Tilde => 10,
|
||||
TokenType.Ampersand => 12,
|
||||
TokenType.LtLt or TokenType.GtGt => 14,
|
||||
TokenType.DotDot => 16,
|
||||
TokenType.Plus or TokenType.Minus => 18,
|
||||
TokenType.Star or TokenType.Slash or TokenType.SlashSlash or TokenType.Percent => 20,
|
||||
TokenType.Caret => 22,
|
||||
_ => throw new Exception($"{token.region}: Expected binary operator with precedence, got {token.type}"),
|
||||
};
|
||||
|
||||
private static bool IsBinop(Token token) => token.type switch
|
||||
{
|
||||
TokenType.Or or TokenType.And or TokenType.Lt or TokenType.Gt or TokenType.LtEquals or TokenType.GtEquals or TokenType.TildeEquals or TokenType.EqualsEquals or
|
||||
TokenType.Pipe or TokenType.Tilde or TokenType.Ampersand or TokenType.LtLt or TokenType.GtGt or TokenType.DotDot or TokenType.Plus or TokenType.Minus or
|
||||
TokenType.Star or TokenType.Slash or TokenType.SlashSlash or TokenType.Percent or TokenType.Caret => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
private ExpNode ParseExpPrimary(Token[] tokens)
|
||||
{
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected primary expression (`nil`, `true`, `false`, numeral, string, `...`, `function`, `{{`, `#`, `not`, `~`)");
|
||||
}
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
switch(tokens[index].type)
|
||||
{
|
||||
case TokenType.Nil:
|
||||
{
|
||||
index += 1;
|
||||
return new ExpNode.Nil(region: startRegion);
|
||||
}
|
||||
case TokenType.True:
|
||||
{
|
||||
index += 1;
|
||||
return new ExpNode.True(region: startRegion);
|
||||
}
|
||||
case TokenType.False:
|
||||
{
|
||||
index += 1;
|
||||
return new ExpNode.False(region: startRegion);
|
||||
}
|
||||
case TokenType.Numeral:
|
||||
{
|
||||
INumeral numeral = ((Token.NumeralData)tokens[index].data!).numeral;
|
||||
index += 1;
|
||||
return new ExpNode.Numeral(value: numeral, region: startRegion);
|
||||
}
|
||||
case TokenType.StringLiteral:
|
||||
{
|
||||
string value = ((Token.StringData)tokens[index].data!).data;
|
||||
index += 1;
|
||||
return new ExpNode.LiteralString(value: value, region: startRegion);
|
||||
}
|
||||
case TokenType.DotDotDot:
|
||||
{
|
||||
index += 1;
|
||||
return new ExpNode.Varargs(region: startRegion);
|
||||
}
|
||||
case TokenType.CurlyOpen:
|
||||
{
|
||||
TableconstructorNode inner = ParseTableconstructor(tokens);
|
||||
return new ExpNode.Tableconstructor(node: inner);
|
||||
}
|
||||
case TokenType.Function:
|
||||
{
|
||||
index += 1;
|
||||
FuncbodyNode body = ParseFuncbody(tokens);
|
||||
return new ExpNode.Functiondef(node: body, startRegion: startRegion, endRegion: body.endRegion);
|
||||
}
|
||||
case TokenType.Minus:
|
||||
{
|
||||
index += 1;
|
||||
ExpNode unop = ParseExp(tokens);
|
||||
return new ExpNode.Unop(node: new(type: UnopType.Minus, exp: unop, startRegion: startRegion, endRegion: tokens[index - 1].region));
|
||||
}
|
||||
case TokenType.Hash:
|
||||
{
|
||||
index += 1;
|
||||
ExpNode unop = ParseExp(tokens);
|
||||
return new ExpNode.Unop(node: new(type: UnopType.Length, exp: unop, startRegion: startRegion, endRegion: tokens[index - 1].region));
|
||||
}
|
||||
case TokenType.Not:
|
||||
{
|
||||
index += 1;
|
||||
ExpNode unop = ParseExp(tokens);
|
||||
return new ExpNode.Unop(node: new(type: UnopType.LogicalNot, exp: unop, startRegion: startRegion, endRegion: tokens[index - 1].region));
|
||||
}
|
||||
case TokenType.Tilde:
|
||||
{
|
||||
index += 1;
|
||||
ExpNode unop = ParseExp(tokens);
|
||||
return new ExpNode.Unop(node: new(type: UnopType.BinaryNot, exp: unop, startRegion: startRegion, endRegion: tokens[index - 1].region));
|
||||
}
|
||||
default:
|
||||
{
|
||||
SuffixexpNode suffixexp = ParseSuffixExp(tokens);
|
||||
return new ExpNode.Suffixexp(node: suffixexp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RetstatNode ParseRetstat(Token[] tokens)
|
||||
{
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `return` to start retstat");
|
||||
}
|
||||
CodeRegion startRegion = tokens[index].region;
|
||||
if(tokens[index].type != TokenType.Return)
|
||||
{
|
||||
throw new Exception($"{startRegion}: Expected `return` to start retstat, got {tokens[index].type}");
|
||||
}
|
||||
index += 1;
|
||||
if(index >= tokens.Length)
|
||||
{
|
||||
return new RetstatNode(values: null, startRegion: startRegion, endRegion: startRegion);
|
||||
}
|
||||
if(tokens[index].type is TokenType.Semicolon or TokenType.Else or TokenType.Elseif or TokenType.End)
|
||||
{
|
||||
CodeRegion emptyEndRegion;
|
||||
if(tokens[index].type == TokenType.Semicolon)
|
||||
{
|
||||
emptyEndRegion = tokens[index].region;
|
||||
index += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
emptyEndRegion = startRegion;
|
||||
}
|
||||
return new RetstatNode(values: null, startRegion: startRegion, endRegion: emptyEndRegion);
|
||||
}
|
||||
ExplistNode values = ParseExplist(tokens);
|
||||
CodeRegion endRegion;
|
||||
if(index < tokens.Length && tokens[index].type == TokenType.Semicolon)
|
||||
{
|
||||
endRegion = tokens[index].region;
|
||||
index += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
endRegion = values.endRegion;
|
||||
}
|
||||
return new RetstatNode(values: values, startRegion: startRegion, endRegion: endRegion);
|
||||
}
|
||||
}
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace luaaaaah;
|
||||
|
||||
public class Program
|
||||
{
|
||||
internal static JsonSerializerOptions options = new()
|
||||
{
|
||||
IncludeFields = true,
|
||||
WriteIndented = true,
|
||||
};
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
switch(args[0])
|
||||
{
|
||||
case "test":
|
||||
{
|
||||
Test(args[1]);
|
||||
}
|
||||
break;
|
||||
case "run":
|
||||
{
|
||||
Run(args[1], true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
public static void Run(string file, bool debug)
|
||||
{
|
||||
string content = File.ReadAllText(file).ReplaceLineEndings();
|
||||
Token[] tokens = new Tokenizer().Tokenize(content);
|
||||
if(debug)
|
||||
{
|
||||
foreach(Token token in tokens)
|
||||
{
|
||||
Console.WriteLine($"{token.region}: {token.type} {{{token.data}}}");
|
||||
}
|
||||
}
|
||||
if(tokens.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(Path.GetFileName(file).StartsWith("tokenizer"))
|
||||
{
|
||||
Console.WriteLine($"Skipping parsing of `{file}`");
|
||||
}
|
||||
else
|
||||
{
|
||||
Parser.ChunkNode root = new Parser().Parse(tokens);
|
||||
if(debug)
|
||||
{
|
||||
Console.WriteLine("Parsed tree:");
|
||||
Console.WriteLine(JsonSerializer.Serialize(root, options: options));
|
||||
}
|
||||
}
|
||||
}
|
||||
static readonly Dictionary<string, string> failedFiles = [];
|
||||
public static void Test(string directory)
|
||||
{
|
||||
TestRecursive(directory);
|
||||
Console.WriteLine("===FAILED===");
|
||||
foreach(KeyValuePair<string, string> entry in failedFiles)
|
||||
{
|
||||
Console.WriteLine($"{entry.Key}: {entry.Value}");
|
||||
}
|
||||
Console.WriteLine($"==={failedFiles.Count}===");
|
||||
}
|
||||
|
||||
public static void TestRecursive(string directory)
|
||||
{
|
||||
foreach(string file in Directory.EnumerateFiles(directory))
|
||||
{
|
||||
if(file.EndsWith(".lua"))
|
||||
{
|
||||
try
|
||||
{
|
||||
Run(file, false);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Console.WriteLine($"{file}: {e}");
|
||||
failedFiles.Add(file, e.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach(string dir in Directory.EnumerateDirectories(directory))
|
||||
{
|
||||
TestRecursive(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
-2843
@@ -1,2843 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace luaaaaah;
|
||||
class Tokenizer
|
||||
{
|
||||
private readonly List<Token> tokens = [];
|
||||
private State state = State.Start;
|
||||
int? lastIndex;
|
||||
int index;
|
||||
int openingLongBracketLevel;
|
||||
int closingLongBracketLevel;
|
||||
Token? currentToken;
|
||||
CodeLocation currentLocation = new(line: 0, col: 0);
|
||||
long escapeSequenceNumber;
|
||||
|
||||
public Token[] Tokenize(string content)
|
||||
{
|
||||
if(content.StartsWith('#'))
|
||||
{
|
||||
content = content[content.IndexOf('\n')..];
|
||||
}
|
||||
while(index < content.Length)
|
||||
{
|
||||
TokenizeChar(content[index]);
|
||||
if(content[index] == '\n')
|
||||
{
|
||||
currentLocation.line += 1;
|
||||
currentLocation.col = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentLocation.col += 1;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
TokenizeChar('\n');
|
||||
return [.. tokens];
|
||||
}
|
||||
|
||||
private void AppendDataChar(char ch)
|
||||
{
|
||||
if((Token.StringData?)currentToken!.data == null)
|
||||
{
|
||||
currentToken!.data = new Token.StringData($"{ch}");
|
||||
}
|
||||
else
|
||||
{
|
||||
((Token.StringData?)currentToken!.data!).data += ch;
|
||||
}
|
||||
currentToken.region.end = new(currentLocation);
|
||||
}
|
||||
|
||||
private void AppendDataInt(char ch)
|
||||
{
|
||||
if((Token.NumeralData?)currentToken!.data == null)
|
||||
{
|
||||
currentToken!.data = new Token.NumeralData(new INumeral.Integer(ch - '0'));
|
||||
}
|
||||
else
|
||||
{
|
||||
((INumeral.Integer)((Token.NumeralData?)currentToken!.data!).numeral).value *= 10;
|
||||
((INumeral.Integer)((Token.NumeralData?)currentToken!.data!).numeral).value += ch - '0';
|
||||
}
|
||||
currentToken.region.end = new(currentLocation);
|
||||
}
|
||||
|
||||
private void AppendDataIntHex(char ch)
|
||||
{
|
||||
int v = char.IsAsciiDigit(ch) ? ch - '0' : 10 + char.ToLower(ch) - 'a';
|
||||
if((Token.NumeralData?)currentToken!.data == null)
|
||||
{
|
||||
currentToken!.data = new Token.NumeralData(new INumeral.Integer(v));
|
||||
}
|
||||
else
|
||||
{
|
||||
((INumeral.Integer)((Token.NumeralData?)currentToken!.data!).numeral).value *= 16;
|
||||
((INumeral.Integer)((Token.NumeralData?)currentToken!.data!).numeral).value += v;
|
||||
}
|
||||
currentToken.region.end = new(currentLocation);
|
||||
}
|
||||
|
||||
private void TokenizeTerminal(State newState, TokenType type)
|
||||
{
|
||||
lastIndex = index;
|
||||
state = newState;
|
||||
currentToken = new(region: new(start: new(currentLocation), end: new(currentLocation)), type: type);
|
||||
}
|
||||
private void TokenizeTerminalName(State newState, char ch)
|
||||
{
|
||||
lastIndex = index;
|
||||
state = newState;
|
||||
currentToken = new(region: new(start: new(currentLocation), end: new(currentLocation)), type: TokenType.Name, data: new Token.StringData($"{ch}"));
|
||||
}
|
||||
|
||||
private void Backtrack(TokenType newType)
|
||||
{
|
||||
if(currentToken == null || currentToken.type == null)
|
||||
{
|
||||
throw new Exception($"Lexer error at {currentLocation}");
|
||||
}
|
||||
currentToken.type = newType;
|
||||
currentToken.data = null;
|
||||
currentLocation = new(currentToken.region.end);
|
||||
tokens.Add(currentToken);
|
||||
currentToken = null;
|
||||
index = lastIndex!.Value;
|
||||
lastIndex = null;
|
||||
state = State.Start;
|
||||
}
|
||||
private void BacktrackNoClear(TokenType newType)
|
||||
{
|
||||
if(currentToken == null || currentToken.type == null)
|
||||
{
|
||||
throw new Exception($"Lexer error at {currentLocation}");
|
||||
}
|
||||
currentToken.type = newType;
|
||||
currentLocation = new(currentToken.region.end);
|
||||
tokens.Add(currentToken);
|
||||
currentToken = null;
|
||||
index = lastIndex!.Value;
|
||||
lastIndex = null;
|
||||
state = State.Start;
|
||||
}
|
||||
private void BacktrackNoTypeChange()
|
||||
{
|
||||
if(currentToken == null || currentToken.type == null)
|
||||
{
|
||||
throw new Exception($"Lexer error at {currentLocation}");
|
||||
}
|
||||
currentLocation = new(currentToken.region.end);
|
||||
tokens.Add(currentToken);
|
||||
currentToken = null;
|
||||
index = lastIndex!.Value;
|
||||
lastIndex = null;
|
||||
state = State.Start;
|
||||
}
|
||||
|
||||
private void TokenizeChar(char ch)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case State.Start:
|
||||
{
|
||||
switch(ch)
|
||||
{
|
||||
case '-':
|
||||
TokenizeTerminal(State.Minus, TokenType.Minus);
|
||||
break;
|
||||
case ',':
|
||||
TokenizeTerminal(State.Comma, TokenType.Comma);
|
||||
break;
|
||||
case '=':
|
||||
TokenizeTerminal(State.Equals, TokenType.Equals);
|
||||
break;
|
||||
case '(':
|
||||
TokenizeTerminal(State.RoundOpen, TokenType.RoundOpen);
|
||||
break;
|
||||
case ')':
|
||||
TokenizeTerminal(State.RoundClosed, TokenType.RoundClosed);
|
||||
break;
|
||||
case '.':
|
||||
TokenizeTerminal(State.Dot, TokenType.Dot);
|
||||
break;
|
||||
case ':':
|
||||
TokenizeTerminal(State.Colon, TokenType.Colon);
|
||||
break;
|
||||
case '{':
|
||||
TokenizeTerminal(State.CurlyOpen, TokenType.CurlyOpen);
|
||||
break;
|
||||
case '}':
|
||||
TokenizeTerminal(State.CurlyClosed, TokenType.CurlyClosed);
|
||||
break;
|
||||
case '[':
|
||||
TokenizeTerminal(State.SquareOpen, TokenType.SquareOpen);
|
||||
break;
|
||||
case ']':
|
||||
TokenizeTerminal(State.SquareClosed, TokenType.SquareClosed);
|
||||
break;
|
||||
case '+':
|
||||
TokenizeTerminal(State.Plus, TokenType.Plus);
|
||||
break;
|
||||
case '~':
|
||||
TokenizeTerminal(State.Tilde, TokenType.Tilde);
|
||||
break;
|
||||
case '>':
|
||||
TokenizeTerminal(State.Gt, TokenType.Gt);
|
||||
break;
|
||||
case '<':
|
||||
TokenizeTerminal(State.Lt, TokenType.Lt);
|
||||
break;
|
||||
case '#':
|
||||
TokenizeTerminal(State.Hash, TokenType.Hash);
|
||||
break;
|
||||
case '|':
|
||||
TokenizeTerminal(State.Pipe, TokenType.Pipe);
|
||||
break;
|
||||
case '&':
|
||||
TokenizeTerminal(State.Ampersand, TokenType.Ampersand);
|
||||
break;
|
||||
case '%':
|
||||
TokenizeTerminal(State.Percent, TokenType.Percent);
|
||||
break;
|
||||
case '*':
|
||||
TokenizeTerminal(State.Star, TokenType.Star);
|
||||
break;
|
||||
case '/':
|
||||
TokenizeTerminal(State.Slash, TokenType.Slash);
|
||||
break;
|
||||
case ';':
|
||||
TokenizeTerminal(State.Semicolon, TokenType.Semicolon);
|
||||
break;
|
||||
case '^':
|
||||
TokenizeTerminal(State.Caret, TokenType.Caret);
|
||||
break;
|
||||
case 'a':
|
||||
TokenizeTerminalName(State.A, ch);
|
||||
break;
|
||||
case 'b':
|
||||
TokenizeTerminalName(State.B, ch);
|
||||
break;
|
||||
case 'd':
|
||||
TokenizeTerminalName(State.D, ch);
|
||||
break;
|
||||
case 'e':
|
||||
TokenizeTerminalName(State.E, ch);
|
||||
break;
|
||||
case 'f':
|
||||
TokenizeTerminalName(State.F, ch);
|
||||
break;
|
||||
case 'i':
|
||||
TokenizeTerminalName(State.I, ch);
|
||||
break;
|
||||
case 'g':
|
||||
TokenizeTerminalName(State.G, ch);
|
||||
break;
|
||||
case 'l':
|
||||
TokenizeTerminalName(State.L, ch);
|
||||
break;
|
||||
case 'n':
|
||||
TokenizeTerminalName(State.N, ch);
|
||||
break;
|
||||
case 'o':
|
||||
TokenizeTerminalName(State.O, ch);
|
||||
break;
|
||||
case 'r':
|
||||
TokenizeTerminalName(State.R, ch);
|
||||
break;
|
||||
case 't':
|
||||
TokenizeTerminalName(State.T, ch);
|
||||
break;
|
||||
case 'u':
|
||||
TokenizeTerminalName(State.U, ch);
|
||||
break;
|
||||
case 'w':
|
||||
TokenizeTerminalName(State.W, ch);
|
||||
break;
|
||||
case '0':
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Zero;
|
||||
currentToken = new(region: new(start: new(currentLocation), end: new(currentLocation)), type: TokenType.Numeral, data: new Token.NumeralData(new INumeral.Integer(0)));
|
||||
} /* tokenizeTerminalIntNum(TokenType.Numeral, TokenizerState.Zero, tokenNumeral, ch); */
|
||||
break;
|
||||
case '"':
|
||||
{
|
||||
state = State.Quote;
|
||||
currentToken = new(region: new(start: new(currentLocation), end: new(currentLocation)), type: TokenType.StringLiteral);
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
{
|
||||
state = State.SingleQuote;
|
||||
currentToken = new(region: new(start: new(currentLocation), end: new(currentLocation)), type: TokenType.StringLiteral);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if(char.IsWhiteSpace(ch)) { }
|
||||
else if(char.IsAsciiLetter(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken = new(region: new(start: new(currentLocation), end: new(currentLocation)), type: TokenType.Name, data: new Token.StringData($"{ch}"));
|
||||
}
|
||||
else if(char.IsDigit(ch))
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Integer;
|
||||
currentToken = new(region: new(start: new(currentLocation), end: new(currentLocation)), type: TokenType.Numeral, data: new Token.NumeralData(new INumeral.Integer(ch - '0')));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"{ch} at {currentLocation}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Quote:
|
||||
{
|
||||
if(ch == '\\')
|
||||
{
|
||||
state = State.QuoteBackslash;
|
||||
}
|
||||
else if(ch == '"')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.String;
|
||||
if(currentToken == null || currentToken.type == null)
|
||||
{
|
||||
currentToken = new(region: new(start: new(currentLocation), end: new(currentLocation)), type: TokenType.StringLiteral);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentToken.type = TokenType.StringLiteral;
|
||||
currentToken.region.end = new(currentLocation);
|
||||
currentToken.data ??= new Token.StringData("");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.QuoteBackslash:
|
||||
{
|
||||
switch(ch)
|
||||
{
|
||||
case 'a':
|
||||
{
|
||||
AppendDataChar('\u0007');
|
||||
state = State.Quote;
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
{
|
||||
AppendDataChar('\u0008');
|
||||
state = State.Quote;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
{
|
||||
AppendDataChar('\t');
|
||||
state = State.Quote;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
case '\n':
|
||||
{
|
||||
AppendDataChar('\n');
|
||||
state = State.Quote;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
{
|
||||
AppendDataChar('\u000b');
|
||||
state = State.Quote;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
{
|
||||
AppendDataChar('\u000c');
|
||||
state = State.Quote;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
{
|
||||
AppendDataChar('\r');
|
||||
state = State.Quote;
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
{
|
||||
AppendDataChar('\\');
|
||||
state = State.Quote;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
{
|
||||
AppendDataChar('"');
|
||||
state = State.Quote;
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
{
|
||||
AppendDataChar('\'');
|
||||
state = State.Quote;
|
||||
}
|
||||
break;
|
||||
case 'z':
|
||||
{
|
||||
state = State.QuoteBackslashZ;
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
state = State.QuoteBackslashX;
|
||||
throw new NotImplementedException($"\\x escape sequences are broken right now");
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
state = State.QuoteBackslashU;
|
||||
throw new NotImplementedException($"\\u escape sequences are broken right now");
|
||||
}
|
||||
default: throw new Exception($"Unknown escape sequence: \\{ch} at {currentLocation}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.QuoteBackslashU:
|
||||
{
|
||||
if(ch == '{')
|
||||
{
|
||||
state = State.QuoteBackslashUBracket;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Expected `{{` to continue \\u escape sequence at {currentLocation}, got {ch}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.QuoteBackslashUBracket:
|
||||
{
|
||||
if(char.IsAsciiHexDigit(ch))
|
||||
{
|
||||
state = State.QuoteBackslashUBracketHex;
|
||||
escapeSequenceNumber = char.IsAsciiDigit(ch) ? ch - '0' : 10 + char.ToLower(ch) - 'a';
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Expected hex digit to continue \\u escape sequence at {currentLocation}, got {ch}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.QuoteBackslashUBracketHex:
|
||||
{
|
||||
if(char.IsAsciiHexDigit(ch))
|
||||
{
|
||||
escapeSequenceNumber = (escapeSequenceNumber * 16) + (char.IsAsciiDigit(ch) ? ch - '0' : 10 + char.ToLower(ch) - 'a');
|
||||
if(escapeSequenceNumber > uint.MaxValue)
|
||||
{
|
||||
throw new Exception($"{currentLocation}: \\u escape sequence has a value > 2^31 which is not permitted");
|
||||
}
|
||||
}
|
||||
else if(ch == '}')
|
||||
{
|
||||
state = State.Quote;
|
||||
// TODO: THIS IS WRONG, there is zero padding due to the fixed size array
|
||||
char[] chars = Encoding.UTF8.GetChars(BitConverter.GetBytes((uint)escapeSequenceNumber));
|
||||
for(int i = 0; i < chars.Length; i++)
|
||||
{
|
||||
AppendDataChar(chars[i]);
|
||||
}
|
||||
|
||||
escapeSequenceNumber = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Expected second hex digit to continue \\u escape sequence at {currentLocation}, got {ch}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.QuoteBackslashZ:
|
||||
{
|
||||
if(ch == '\\')
|
||||
{
|
||||
state = State.QuoteBackslash;
|
||||
}
|
||||
else if(ch == '"')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.String;
|
||||
if(currentToken == null || currentToken.type == null)
|
||||
{
|
||||
currentToken = new(region: new(start: new(currentLocation), end: new(currentLocation)), type: TokenType.StringLiteral);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentToken.type = TokenType.StringLiteral;
|
||||
currentToken.region.end = new(currentLocation);
|
||||
currentToken.data = new Token.StringData("");
|
||||
}
|
||||
}
|
||||
else if(!char.IsWhiteSpace(ch))
|
||||
{
|
||||
AppendDataChar(ch);
|
||||
state = State.Quote;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Noop, https://www.lua.org/manual/5.4/manual.html#3.1:
|
||||
// "The escape sequence '\z' skips the following span of whitespace characters, including line breaks;"
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.SingleQuote:
|
||||
{
|
||||
if(ch == '\\')
|
||||
{
|
||||
state = State.SingleQuoteBackslash;
|
||||
}
|
||||
else if(ch == '\'')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.String;
|
||||
if(currentToken == null || currentToken.type == null)
|
||||
{
|
||||
currentToken = new(region: new(start: new(currentLocation), end: new(currentLocation)), type: TokenType.StringLiteral);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentToken.type = TokenType.StringLiteral;
|
||||
currentToken.region.end = new(currentLocation);
|
||||
currentToken.data ??= new Token.StringData("");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.SingleQuoteBackslash:
|
||||
{
|
||||
switch(ch)
|
||||
{
|
||||
case 'a':
|
||||
{
|
||||
AppendDataChar('\u0007');
|
||||
state = State.SingleQuote;
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
{
|
||||
AppendDataChar('\u0008');
|
||||
state = State.SingleQuote;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
{
|
||||
AppendDataChar('\t');
|
||||
state = State.SingleQuote;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
case '\n':
|
||||
{
|
||||
AppendDataChar('\n');
|
||||
state = State.SingleQuote;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
{
|
||||
AppendDataChar('\u000b');
|
||||
state = State.SingleQuote;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
{
|
||||
AppendDataChar('\u000c');
|
||||
state = State.SingleQuote;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
{
|
||||
AppendDataChar('\r');
|
||||
state = State.SingleQuote;
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
{
|
||||
AppendDataChar('\\');
|
||||
state = State.SingleQuote;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
{
|
||||
AppendDataChar('"');
|
||||
state = State.SingleQuote;
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
{
|
||||
AppendDataChar('\'');
|
||||
state = State.SingleQuote;
|
||||
}
|
||||
break;
|
||||
case 'z':
|
||||
state = State.SingleQuoteBackslashZ;
|
||||
break;
|
||||
case 'x':
|
||||
state = State.SingleQuoteBackslashX;
|
||||
break;
|
||||
case 'u':
|
||||
state = State.SingleQuoteBackslashU;
|
||||
break;
|
||||
default: throw new Exception($"Unknown escape sequence: \\{ch}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.SingleQuoteBackslashU:
|
||||
state = ch == '{'
|
||||
? State.SingleQuoteBackslashUBracket
|
||||
: throw new Exception($"Expected `{{` to continue \\u escape sequence at {currentLocation}, got {ch}");
|
||||
break;
|
||||
case State.SingleQuoteBackslashUBracket:
|
||||
{
|
||||
if(char.IsAsciiHexDigit(ch))
|
||||
{
|
||||
state = State.SingleQuoteBackslashUBracketHex;
|
||||
escapeSequenceNumber = char.IsAsciiDigit(ch) ? ch - '0' : 10 + char.ToLower(ch) - 'a';
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Expected hex digit to continue \\u escape sequence at {currentLocation}, got {ch}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.SingleQuoteBackslashUBracketHex:
|
||||
{
|
||||
if(char.IsAsciiHexDigit(ch))
|
||||
{
|
||||
escapeSequenceNumber = (escapeSequenceNumber * 16) + (char.IsAsciiDigit(ch) ? ch - '0' : 10 + char.ToLower(ch) - 'a');
|
||||
if(escapeSequenceNumber > uint.MaxValue)
|
||||
{
|
||||
throw new Exception($"{currentLocation}: \\u escape sequence has a value > 2^31 which is not permitted");
|
||||
}
|
||||
}
|
||||
else if(ch == '}')
|
||||
{
|
||||
state = State.SingleQuote;
|
||||
// TODO: THIS IS WRONG, there is zero padding due to the fixed size array
|
||||
char[] chars = Encoding.UTF8.GetChars(BitConverter.GetBytes((uint)escapeSequenceNumber));
|
||||
for(int i = 0; i < chars.Length; i++)
|
||||
{
|
||||
AppendDataChar(chars[i]);
|
||||
}
|
||||
escapeSequenceNumber = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Expected second hex digit to continue \\u escape sequence at {currentLocation}, got {ch}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.SingleQuoteBackslashZ:
|
||||
{
|
||||
if(ch == '\\')
|
||||
{
|
||||
state = State.SingleQuoteBackslash;
|
||||
}
|
||||
else if(ch == '\'')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.String;
|
||||
if(currentToken == null || currentToken.type == null)
|
||||
{
|
||||
currentToken = new(region: new(start: new(currentLocation), end: new(currentLocation)), type: TokenType.StringLiteral);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentToken.type = TokenType.StringLiteral;
|
||||
currentToken.region.end = new(currentLocation);
|
||||
}
|
||||
}
|
||||
else if(!char.IsWhiteSpace(ch))
|
||||
{
|
||||
AppendDataChar(ch);
|
||||
state = State.SingleQuote;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Noop, https://www.lua.org/manual/5.4/manual.html#3.1:
|
||||
// "The escape sequence '\z' skips the following span of whitespace characters, including line breaks;"
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.SingleQuoteBackslashX:
|
||||
{
|
||||
if(char.IsAsciiHexDigit(ch))
|
||||
{
|
||||
state = State.SingleQuoteBackslashXHex;
|
||||
escapeSequenceNumber = char.IsAsciiDigit(ch) ? ch - '0' : 10 + char.ToLower(ch) - 'a';
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"{currentLocation}: Expected hex digit in \\x escape sequence, got {ch}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.SingleQuoteBackslashXHex:
|
||||
{
|
||||
if(char.IsAsciiHexDigit(ch))
|
||||
{
|
||||
state = State.SingleQuote;
|
||||
escapeSequenceNumber = (escapeSequenceNumber * 16) + (char.IsAsciiDigit(ch) ? ch - '0' : 10 + char.ToLower(ch) - 'a');
|
||||
// TODO: THIS IS WRONG, there is zero padding due to the fixed size array
|
||||
foreach(char c in Encoding.UTF8.GetChars(BitConverter.GetBytes(escapeSequenceNumber)))
|
||||
{
|
||||
AppendDataChar(c);
|
||||
}
|
||||
escapeSequenceNumber = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"{currentLocation}: Expected second hex digit in \\x escape sequence, got {ch}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.QuoteBackslashX:
|
||||
{
|
||||
if(char.IsAsciiHexDigit(ch))
|
||||
{
|
||||
state = State.QuoteBackslashXHex;
|
||||
escapeSequenceNumber = char.IsAsciiDigit(ch) ? ch - '0' : 10 + char.ToLower(ch) - 'a';
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"{currentLocation}: Expected hex digit in \\x escape sequence, got {ch}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.QuoteBackslashXHex:
|
||||
{
|
||||
if(char.IsAsciiHexDigit(ch))
|
||||
{
|
||||
state = State.Quote;
|
||||
escapeSequenceNumber = (escapeSequenceNumber * 16) + (char.IsAsciiDigit(ch) ? ch - '0' : 10 + char.ToLower(ch) - 'a');
|
||||
// TODO: THIS IS WRONG, there is zero padding due to the fixed size array
|
||||
foreach(char c in Encoding.UTF8.GetChars(BitConverter.GetBytes(escapeSequenceNumber)))
|
||||
{
|
||||
AppendDataChar(c);
|
||||
}
|
||||
escapeSequenceNumber = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"{currentLocation}: Expected second hex digit in \\x escape sequence, got {ch}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.String:
|
||||
{
|
||||
BacktrackNoClear(TokenType.StringLiteral);
|
||||
}
|
||||
break;
|
||||
case State.Name:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Zero:
|
||||
{
|
||||
if(ch is 'x' or 'X')
|
||||
{
|
||||
currentToken!.type = null;
|
||||
state = State.HexNumberX;
|
||||
}
|
||||
else if(ch == '.')
|
||||
{
|
||||
state = State.Float;
|
||||
currentToken!.type = null;
|
||||
currentToken!.data = null;
|
||||
AppendDataChar('0');
|
||||
AppendDataChar('.');
|
||||
}
|
||||
else if(char.IsAsciiDigit(ch))
|
||||
{
|
||||
lastIndex = index;
|
||||
AppendDataInt(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Float:
|
||||
{
|
||||
if(char.IsAsciiDigit(ch))
|
||||
{
|
||||
lastIndex = index;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(currentToken == null)
|
||||
{
|
||||
throw new Exception($"Lexer error at {currentLocation}");
|
||||
}
|
||||
currentLocation = new(currentToken.region.end);
|
||||
currentToken.type = TokenType.Numeral;
|
||||
currentToken.data = new Token.NumeralData(new INumeral.Float(float.Parse(((Token.StringData)currentToken.data!).data)));
|
||||
tokens.Add(currentToken);
|
||||
currentToken = null;
|
||||
index = lastIndex!.Value;
|
||||
lastIndex = null;
|
||||
state = State.Start;
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.HexNumberX:
|
||||
{
|
||||
if(char.IsAsciiHexDigit(ch))
|
||||
{
|
||||
lastIndex = index;
|
||||
currentToken!.type = TokenType.Numeral;
|
||||
AppendDataIntHex(ch);
|
||||
state = State.HexNumber;
|
||||
}
|
||||
else if(ch == '.')
|
||||
{
|
||||
throw new NotImplementedException($"{currentLocation}: Hex floats at are not implemented");
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.HexNumber:
|
||||
{
|
||||
if(ch == 'p')
|
||||
{
|
||||
currentToken!.type = null;
|
||||
state = State.HexExpNumber;
|
||||
}
|
||||
else if(char.IsAsciiHexDigit(ch))
|
||||
{
|
||||
lastIndex = index;
|
||||
currentToken!.type = TokenType.Numeral;
|
||||
AppendDataIntHex(ch);
|
||||
}
|
||||
else if(ch == '.')
|
||||
{
|
||||
throw new NotImplementedException($"{currentLocation}: Hex floats at are not implemented");
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Integer:
|
||||
{
|
||||
if(ch == 'e')
|
||||
{
|
||||
currentToken!.type = null;
|
||||
state = State.ExpNumber;
|
||||
}
|
||||
else if(ch == '.')
|
||||
{
|
||||
currentToken!.type = null;
|
||||
currentToken.data = new Token.StringData($"{((INumeral.Integer)((Token.NumeralData)currentToken!.data!).numeral).value}.");
|
||||
state = State.Float;
|
||||
}
|
||||
else if(char.IsAsciiDigit(ch))
|
||||
{
|
||||
lastIndex = index;
|
||||
currentToken!.type = TokenType.Numeral;
|
||||
AppendDataInt(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.SquareOpen:
|
||||
{
|
||||
if(ch == '[')
|
||||
{
|
||||
currentToken = new Token(region: new(start: new(currentLocation), end: new(currentLocation)), type: TokenType.StringLiteral);
|
||||
state = State.StringWithLongBracket;
|
||||
}
|
||||
else if(ch == '=')
|
||||
{
|
||||
openingLongBracketLevel = 1;
|
||||
state = State.StringStartLongBracket;
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Comma:
|
||||
case State.RoundOpen:
|
||||
case State.RoundClosed:
|
||||
case State.CurlyOpen:
|
||||
case State.CurlyClosed:
|
||||
case State.Plus:
|
||||
case State.TildeEquals:
|
||||
case State.EqualsEquals:
|
||||
case State.Hash:
|
||||
case State.GtEquals:
|
||||
case State.LtEquals:
|
||||
case State.SquareClosed:
|
||||
case State.Pipe:
|
||||
case State.Ampersand:
|
||||
case State.Percent:
|
||||
case State.Star:
|
||||
case State.Semicolon:
|
||||
case State.Caret:
|
||||
case State.DotDotDot:
|
||||
case State.GtGt:
|
||||
case State.LtLt:
|
||||
case State.ColonColon:
|
||||
case State.SlashSlash:
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
break;
|
||||
case State.Tilde:
|
||||
{
|
||||
if(ch == '=')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.TildeEquals;
|
||||
currentToken!.type = TokenType.TildeEquals;
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Gt:
|
||||
{
|
||||
if(ch == '=')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.GtEquals;
|
||||
currentToken!.type = TokenType.GtEquals;
|
||||
}
|
||||
else if(ch == '>')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.GtGt;
|
||||
currentToken!.type = TokenType.GtGt;
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Lt:
|
||||
{
|
||||
if(ch == '=')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.LtEquals;
|
||||
currentToken!.type = TokenType.LtEquals;
|
||||
}
|
||||
else if(ch == '<')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.LtLt;
|
||||
currentToken!.type = TokenType.LtLt;
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Slash:
|
||||
{
|
||||
if(ch == '/')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.SlashSlash;
|
||||
currentToken!.type = TokenType.SlashSlash;
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Dot:
|
||||
{
|
||||
if(ch == '.')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.DotDot;
|
||||
currentToken!.type = TokenType.DotDot;
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case State.DotDot:
|
||||
{
|
||||
if(ch == '.')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.DotDotDot;
|
||||
currentToken!.type = TokenType.DotDotDot;
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Colon:
|
||||
{
|
||||
if(ch == ':')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.ColonColon;
|
||||
currentToken!.type = TokenType.ColonColon;
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Equals:
|
||||
{
|
||||
if(ch == '=')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.EqualsEquals;
|
||||
currentToken!.type = TokenType.EqualsEquals;
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Minus:
|
||||
{
|
||||
if(ch == '-')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.SmallCommentStart;
|
||||
currentToken = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.SmallCommentStart:
|
||||
{
|
||||
if(ch == '[')
|
||||
{
|
||||
state = State.BigCommentStartLongBracket;
|
||||
}
|
||||
else if(ch == '\n')
|
||||
{
|
||||
state = State.Start;
|
||||
lastIndex = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.SmallComment;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.SmallComment:
|
||||
{
|
||||
if(ch == '\n')
|
||||
{
|
||||
state = State.Start;
|
||||
lastIndex = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.BigCommentStartLongBracket:
|
||||
{
|
||||
if(ch == '=')
|
||||
{
|
||||
openingLongBracketLevel += 1;
|
||||
}
|
||||
else if(ch == '[')
|
||||
{
|
||||
state = State.BigComment;
|
||||
}
|
||||
else if(ch == '\n')
|
||||
{
|
||||
state = State.Start;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.SmallComment;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.BigComment:
|
||||
{
|
||||
if(ch == ']')
|
||||
{
|
||||
state = State.BigCommentEndLongBracket;
|
||||
closingLongBracketLevel = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.BigCommentEndLongBracket:
|
||||
{
|
||||
if(ch == '=')
|
||||
{
|
||||
closingLongBracketLevel += 1;
|
||||
if(openingLongBracketLevel < closingLongBracketLevel)
|
||||
{
|
||||
state = State.BigComment;
|
||||
}
|
||||
}
|
||||
else if(ch == ']' && openingLongBracketLevel == closingLongBracketLevel)
|
||||
{
|
||||
state = State.Start;
|
||||
openingLongBracketLevel = 0;
|
||||
closingLongBracketLevel = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
closingLongBracketLevel = 0;
|
||||
state = State.BigComment;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.StringStartLongBracket:
|
||||
{
|
||||
if(ch == '=')
|
||||
{
|
||||
openingLongBracketLevel += 1;
|
||||
|
||||
}
|
||||
else if(ch == '[')
|
||||
{
|
||||
state = State.StringWithLongBracket;
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoTypeChange();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.StringWithLongBracket:
|
||||
{
|
||||
if(ch == ']')
|
||||
{
|
||||
state = State.StringEndLongBracket;
|
||||
closingLongBracketLevel = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.StringEndLongBracket:
|
||||
{
|
||||
if(ch == '=')
|
||||
{
|
||||
closingLongBracketLevel += 1;
|
||||
if(openingLongBracketLevel < closingLongBracketLevel)
|
||||
{
|
||||
state = State.StringWithLongBracket;
|
||||
}
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(ch == ']' && openingLongBracketLevel == closingLongBracketLevel)
|
||||
{
|
||||
if(currentToken == null || currentToken.type == null)
|
||||
{
|
||||
throw new Exception($"Lexer error at {currentLocation}");
|
||||
}
|
||||
if((Token.StringData?)currentToken.data == null)
|
||||
{
|
||||
currentToken.data = new Token.StringData("");
|
||||
}
|
||||
currentToken.type = TokenType.StringLiteral;
|
||||
((Token.StringData)currentToken.data).data = ((Token.StringData)currentToken.data).data.Remove(((Token.StringData)currentToken.data).data.Length - closingLongBracketLevel);
|
||||
currentLocation = new(currentToken.region.end);
|
||||
tokens.Add(currentToken);
|
||||
currentToken = null;
|
||||
lastIndex = null;
|
||||
state = State.Start;
|
||||
openingLongBracketLevel = 0;
|
||||
closingLongBracketLevel = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
closingLongBracketLevel = 0;
|
||||
AppendDataChar(ch);
|
||||
state = State.StringWithLongBracket;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.A:
|
||||
{
|
||||
if(ch == 'n')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.An;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.An:
|
||||
{
|
||||
if(ch == 'd')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.And;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.And:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.And);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.W:
|
||||
{
|
||||
if(ch == 'h')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Wh;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Wh:
|
||||
{
|
||||
if(ch == 'i')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Whi;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Whi:
|
||||
{
|
||||
if(ch == 'l')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Whil;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Whil:
|
||||
{
|
||||
if(ch == 'e')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.While;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.While:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.While);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.B:
|
||||
{
|
||||
if(ch == 'r')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Br;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Br:
|
||||
{
|
||||
if(ch == 'e')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Bre;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Bre:
|
||||
{
|
||||
if(ch == 'a')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Brea;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Brea:
|
||||
{
|
||||
if(ch == 'k')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Break;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Break:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Break);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.G:
|
||||
{
|
||||
if(ch == 'o')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Go;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Go:
|
||||
{
|
||||
if(ch == 't')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Got;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Got:
|
||||
{
|
||||
if(ch == 'o')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Goto;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Goto:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Goto);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.R:
|
||||
{
|
||||
if(ch == 'e')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Re;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Re:
|
||||
{
|
||||
if(ch == 't')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Ret;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(ch == 'p')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Rep;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Ret:
|
||||
{
|
||||
if(ch == 'u')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Retu;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Retu:
|
||||
{
|
||||
if(ch == 'r')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Retur;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Retur:
|
||||
{
|
||||
if(ch == 'n')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Return;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Return:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Return);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Rep:
|
||||
{
|
||||
if(ch == 'e')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Repe;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Repe:
|
||||
{
|
||||
if(ch == 'a')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Repea;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Repea:
|
||||
{
|
||||
if(ch == 't')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Repeat;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Repeat:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Repeat);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.N:
|
||||
{
|
||||
if(ch == 'i')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Ni;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(ch == 'o')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.No;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Ni:
|
||||
{
|
||||
if(ch == 'l')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Nil;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Nil:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Nil);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.No:
|
||||
{
|
||||
if(ch == 't')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Not;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Not:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Not);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.T:
|
||||
{
|
||||
if(ch == 'h')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Th;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(ch == 'r')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Tr;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Th:
|
||||
{
|
||||
if(ch == 'e')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.The;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.The:
|
||||
{
|
||||
if(ch == 'n')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Then;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Then:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Then);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Tr:
|
||||
{
|
||||
if(ch == 'u')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Tru;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Tru:
|
||||
{
|
||||
if(ch == 'e')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.True;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.True:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.True);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.E:
|
||||
{
|
||||
if(ch == 'l')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.El;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(ch == 'n')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.En;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.El:
|
||||
{
|
||||
if(ch == 's')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Els;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Els:
|
||||
{
|
||||
if(ch == 'e')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Else;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Else:
|
||||
{
|
||||
if(ch == 'i')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Elsei;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Else);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Elsei:
|
||||
{
|
||||
if(ch == 'f')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Elseif;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Elseif:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Elseif);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.En:
|
||||
{
|
||||
if(ch == 'd')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.End;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.End:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.End);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.O:
|
||||
{
|
||||
if(ch == 'r')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Or;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Or:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Or);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.D:
|
||||
{
|
||||
if(ch == 'o')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Do;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Do:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Do);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.I:
|
||||
{
|
||||
if(ch == 'f')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.If;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(ch == 'n')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.In;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.In:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.In);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.If:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.If);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.F:
|
||||
{
|
||||
if(ch == 'u')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Fu;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(ch == 'a')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Fa;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(ch == 'o')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Fo;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Fu:
|
||||
{
|
||||
if(ch == 'n')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Fun;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Fun:
|
||||
{
|
||||
if(ch == 'c')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Func;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Func:
|
||||
{
|
||||
if(ch == 't')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Funct;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Funct:
|
||||
{
|
||||
if(ch == 'i')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Functi;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Functi:
|
||||
{
|
||||
if(ch == 'o')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Functio;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Functio:
|
||||
{
|
||||
if(ch == 'n')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Function;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Function:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Function);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Fa:
|
||||
{
|
||||
if(ch == 'l')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Fal;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Fal:
|
||||
{
|
||||
if(ch == 's')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Fals;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Fals:
|
||||
{
|
||||
if(ch == 'e')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.False;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.False:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.False);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Fo:
|
||||
{
|
||||
if(ch == 'r')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.For;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.For:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.For);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.L:
|
||||
{
|
||||
if(ch == 'o')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Lo;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Lo:
|
||||
{
|
||||
if(ch == 'c')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Loc;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Loc:
|
||||
{
|
||||
if(ch == 'a')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Loca;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Loca:
|
||||
{
|
||||
if(ch == 'l')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Local;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Local:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Local);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.U:
|
||||
{
|
||||
if(ch == 'n')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Un;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Un:
|
||||
{
|
||||
if(ch == 't')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Unt;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Unt:
|
||||
{
|
||||
if(ch == 'i')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Unti;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Unti:
|
||||
{
|
||||
if(ch == 'l')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Until;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BacktrackNoClear(TokenType.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Until:
|
||||
{
|
||||
if(char.IsAsciiLetterOrDigit(ch) || ch == '_')
|
||||
{
|
||||
lastIndex = index;
|
||||
state = State.Name;
|
||||
currentToken!.type = TokenType.Name;
|
||||
AppendDataChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack(TokenType.Until);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException(state.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
Start,
|
||||
Quote, SingleQuote, Name, Integer, Float, Zero,
|
||||
A, B, D, E, F, G, I, L, N, O, R, T, U, W,
|
||||
Plus, Minus, Star, Slash, Percent, Caret, Hash,
|
||||
Ampersand, Tilde, Pipe, Lt, Gt, Equals, RoundOpen, RoundClosed, CurlyOpen, CurlyClosed, SquareOpen, SquareClosed, StringStartLongBracket, StringWithLongBracket, StringEndLongBracket,
|
||||
Colon, Semicolon, Comma, Dot,
|
||||
|
||||
An, Br, Do, El, En, Fa, Fo, Fu, Go, If, In, Lo, Ni, No, Or, Re, Th, Tr, Un, Wh,
|
||||
LtLt, GtGt, SlashSlash, EqualsEquals, TildeEquals, LtEquals, GtEquals, ColonColon, DotDot,
|
||||
SmallCommentStart, QuoteBackslash, SingleQuoteBackslash, String, HexNumberX, ExpNumber,
|
||||
|
||||
And, Bre, Els, End, Fal, For, Fun, Got, Loc, Nil, Not, Rep, Ret, The, Tru, Unt, Whi,
|
||||
DotDotDot, HexNumber, QuoteBackslashZ, SingleQuoteBackslashZ, QuoteBackslashX, SingleQuoteBackslashX, QuoteBackslashXHex, SingleQuoteBackslashXHex,
|
||||
SingleQuoteBackslashU, SingleQuoteBackslashUBracket, SingleQuoteBackslashUBracketHex,
|
||||
QuoteBackslashU, QuoteBackslashUBracket, QuoteBackslashUBracketHex,
|
||||
SmallComment, BigComment, BigCommentStartLongBracket, BigCommentEndLongBracket,
|
||||
|
||||
Brea, Else, Fals, Func, Goto, Loca, Repe, Retu, Then, True, Unti, Whil, HexExpNumber,
|
||||
|
||||
Break, Elsei, False, Funct, Local, Repea, Retur, Until, While,
|
||||
|
||||
Elseif, Functi, Repeat, Return,
|
||||
|
||||
Functio,
|
||||
|
||||
Function,
|
||||
}
|
||||
}
|
||||
|
||||
internal class Token(CodeRegion region, TokenType? type = null, Token.IData? data = null)
|
||||
{
|
||||
public CodeRegion region = region;
|
||||
public IData? data = data;
|
||||
public TokenType? type = type;
|
||||
|
||||
public interface IData { }
|
||||
public class NumeralData(INumeral numeral) : IData
|
||||
{
|
||||
public INumeral numeral = numeral;
|
||||
public override string ToString()
|
||||
{
|
||||
return $"NumeralData {numeral}";
|
||||
}
|
||||
}
|
||||
public class StringData(string data) : IData
|
||||
{
|
||||
public string data = data;
|
||||
public override string ToString()
|
||||
{
|
||||
return $"StringData \"{data}\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
public enum TokenType
|
||||
{
|
||||
Name,
|
||||
And, Break, Do, Else, Elseif, End,
|
||||
False, For, Function, Goto, If, In,
|
||||
Local, Nil, Not, Or, Repeat, Return,
|
||||
Then, True, Until, While,
|
||||
Plus, Minus, Star, Slash, Percent, Caret, Hash,
|
||||
Ampersand, Tilde, Pipe, LtLt, GtGt, SlashSlash,
|
||||
EqualsEquals, TildeEquals, LtEquals, GtEquals, Lt, Gt, Equals,
|
||||
RoundOpen, RoundClosed, CurlyOpen, CurlyClosed, SquareOpen, SquareClosed, ColonColon,
|
||||
Semicolon, Colon, Comma, Dot, DotDot, DotDotDot,
|
||||
Numeral,
|
||||
StringLiteral,
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<Project
|
||||
Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
pub mod tokenizer;
|
||||
pub mod parser;
|
||||
|
||||
use std::{env, fs};
|
||||
|
||||
use crate::{tokenizer::{Token, tokenize}, parser::parse};
|
||||
|
||||
fn main()
|
||||
{
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let file_content = fs::read_to_string(&args[1]).expect("Could not read source file");
|
||||
|
||||
match compile(&file_content)
|
||||
{
|
||||
Ok(()) =>
|
||||
{
|
||||
println!("Done compiling");
|
||||
}
|
||||
Err(msg) => println!("ERROR: {}", msg)
|
||||
}
|
||||
}
|
||||
|
||||
fn compile(file_content: &String) -> Result<(), &'static str>
|
||||
{
|
||||
let tokens: Vec<Token> = tokenize(&file_content)?;
|
||||
println!("{:?}", tokens);
|
||||
let node = parse(tokens)?;
|
||||
println!("{:?}", node);
|
||||
return Ok(());
|
||||
}
|
||||
+1186
@@ -0,0 +1,1186 @@
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Token
|
||||
{
|
||||
Name(String),
|
||||
And, Break, Do, Else, Elseif, End,
|
||||
False, For, Function, Goto, If, In,
|
||||
Local, Nil, Not, Or, Repeat, Return,
|
||||
Then, True, Until, While,
|
||||
Plus, Minus, Star, Slash, Percent, Caret, Hash,
|
||||
Ampersand, Tilde, Pipe, LtLt, GtGt, SlashSlash,
|
||||
EqualsEquals, TildeEquals, LtEquals, GtEquals, Lt, Gt, Equals,
|
||||
RoundOpen, RoundClosed, CurlyOpen, CurlyClosed, SquareOpen, SquareClosed, ColonColon,
|
||||
Semicolon, Colon, Comma, Dot, DotDot, DotDotDot,
|
||||
IntLiteral(String),
|
||||
HexLiteral(String),
|
||||
StringLiteral(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum TokenizerState
|
||||
{
|
||||
Start,
|
||||
Quote, SingleQuote, Name, Number, Zero,
|
||||
A, B, D, E, F, G, I, L, N, O, R, T, U, W,
|
||||
Plus, Minus, Star, Slash, Percent, Caret, Hash,
|
||||
Ampersand, Tilde, Pipe, Lt, Gt, Equals, RoundOpen, RoundClosed, CurlyOpen, CurlyClosed, SquareOpen, SquareClosed,
|
||||
Colon, Semicolon, Comma, Dot,
|
||||
|
||||
An, Br, Do, El, En, Fa, Fo, Fu, Go, If, In, Lo, Ni, No, Or, Re, Th, Tr, Un, Wh,
|
||||
LtLt, GtGt, SlashSlash, EqualsEquals, TildeEquals, LtEquals, GtEquals, ColonColon, DotDot,
|
||||
SmallCommentStart, QuoteBackslash, SingleQuoteBackslash, String, HexNumberX, ExpNumber,
|
||||
|
||||
And, Bre, Els, End, Fal, For, Fun, Got, Loc, Nil, Not, Rep, Ret, The, Tru, Unt, Whi,
|
||||
DotDotDot, HexNumber, QuoteBackslashZ, SingleQuoteBackslashZ,
|
||||
BigCommentLongBracketStart, SmallComment,
|
||||
|
||||
Brea, Else, Fals, Func, Goto, Loca, Repe, Retu, Then, True, Unti, Whil, HexExpNumber,
|
||||
BigComment, BigCommentLongBracketEnd,
|
||||
|
||||
Break, Elsei, False, Funct, Local, Repea, Retur, Until, While,
|
||||
|
||||
Elseif, Functi, Repeat, Return,
|
||||
|
||||
Functio,
|
||||
|
||||
Function,
|
||||
}
|
||||
|
||||
fn tokenize_update_index_and_state(last_index: &mut i32, index: usize, state: &mut TokenizerState, new_state: TokenizerState)
|
||||
{
|
||||
*last_index = index as i32;
|
||||
*state = new_state;
|
||||
}
|
||||
fn tokenize_terminal_no_str(last_index: &mut i32, index: usize, token: &mut Option<Token>, state: &mut TokenizerState, new_token: Option<Token>, new_state: TokenizerState)
|
||||
{
|
||||
tokenize_update_index_and_state(last_index, index, state, new_state);
|
||||
*token = new_token;
|
||||
}
|
||||
fn tokenize_terminal_no_token(last_index: &mut i32, index: usize, state: &mut TokenizerState, new_state: TokenizerState, token_str: &mut String, ch: char)
|
||||
{
|
||||
tokenize_update_index_and_state(last_index, index, state, new_state);
|
||||
token_str.push(ch);
|
||||
}
|
||||
fn tokenize_terminal(last_index: &mut i32, index: usize, token: &mut Option<Token>, state: &mut TokenizerState, new_token: Option<Token>, new_state: TokenizerState, token_str: &mut String, ch: char)
|
||||
{
|
||||
tokenize_terminal_no_str(last_index, index, token, state, new_token, new_state);
|
||||
token_str.push(ch);
|
||||
}
|
||||
fn tokenize_backtrack(last_index: &mut i32, index: &mut usize, tokens: &mut Vec<Token>, token: &mut Option<Token>, token_str: &mut String, state: &mut TokenizerState) -> Result<(), &'static str>
|
||||
{
|
||||
return tokenize_backtrack_custom_token(last_index, index, tokens, token, token_str, state, token.clone().unwrap());
|
||||
}
|
||||
fn tokenize_backtrack_name(last_index: &mut i32, index: &mut usize, tokens: &mut Vec<Token>, token: &mut Option<Token>, token_str: &mut String, state: &mut TokenizerState) -> Result<(), &'static str>
|
||||
{
|
||||
if *last_index == -1 || token.is_none()
|
||||
{
|
||||
println!("{}|{}|{:?} | {:?}", last_index, index, token, tokens);
|
||||
return Err("Lexerr");
|
||||
}
|
||||
*index = *last_index as usize;
|
||||
*last_index = -1;
|
||||
tokens.push(Token::Name(token_str.clone()));
|
||||
*token = None;
|
||||
token_str.clear();
|
||||
*state = TokenizerState::Start;
|
||||
return Ok(());
|
||||
}
|
||||
fn tokenize_backtrack_custom_token(last_index: &mut i32, index: &mut usize, tokens: &mut Vec<Token>, token: &mut Option<Token>, token_str: &mut String, state: &mut TokenizerState, new_token: Token) -> Result<(), &'static str>
|
||||
{
|
||||
if *last_index == -1 || token.is_none()
|
||||
{
|
||||
println!("{}|{}|{:?} | {:?}", last_index, index, token, tokens);
|
||||
return Err("Lexerr");
|
||||
}
|
||||
*index = *last_index as usize;
|
||||
*last_index = -1;
|
||||
tokens.push(new_token);
|
||||
*token = None;
|
||||
token_str.clear();
|
||||
*state = TokenizerState::Start;
|
||||
return Ok(());
|
||||
}
|
||||
fn tokenize_alphanumeric_nonstart(last_index: &mut i32, index: &mut usize, tokens: &mut Vec<Token>, token: &mut Option<Token>, token_str: &mut String, state: &mut TokenizerState, ch: char) -> Result<(), &'static str>
|
||||
{
|
||||
if ch.is_ascii_alphanumeric() || ch == '_'
|
||||
{
|
||||
tokenize_update_index_and_state(last_index, *index, state, TokenizerState::Name);
|
||||
token_str.push(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenize_backtrack_name(last_index, index, tokens, token, token_str, state)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
fn tokenize_alphanumeric_nonstart_custom(last_index: &mut i32, index: &mut usize, tokens: &mut Vec<Token>, token: &mut Option<Token>, token_str: &mut String, state: &mut TokenizerState, ch: char, new_token: Token) -> Result<(), &'static str>
|
||||
{
|
||||
if ch.is_ascii_alphanumeric() || ch == '_'
|
||||
{
|
||||
tokenize_update_index_and_state(last_index, *index, state, TokenizerState::Name);
|
||||
token_str.push(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenize_backtrack_custom_token(last_index, index, tokens, token, token_str, state, new_token)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
fn tokenize_char(state: &mut TokenizerState, ch: char, last_index: &mut i32, index: &mut usize, token: &mut Option<Token>, token_str: &mut String, tokens: &mut Vec<Token>, long_bracket_level: &mut u32) -> Result<(), &'static str>
|
||||
{
|
||||
match state
|
||||
{
|
||||
TokenizerState::Start =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'-' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Minus), TokenizerState::Minus),
|
||||
'a' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("a".to_string())), TokenizerState::A, token_str, ch),
|
||||
'b' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("b".to_string())), TokenizerState::B, token_str, ch),
|
||||
'd' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("d".to_string())), TokenizerState::D, token_str, ch),
|
||||
'e' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("e".to_string())), TokenizerState::E, token_str, ch),
|
||||
'f' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("f".to_string())), TokenizerState::F, token_str, ch),
|
||||
'i' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("i".to_string())), TokenizerState::I, token_str, ch),
|
||||
'g' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("g".to_string())), TokenizerState::G, token_str, ch),
|
||||
'l' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("l".to_string())), TokenizerState::L, token_str, ch),
|
||||
'n' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("n".to_string())), TokenizerState::N, token_str, ch),
|
||||
'o' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("o".to_string())), TokenizerState::O, token_str, ch),
|
||||
'r' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("r".to_string())), TokenizerState::R, token_str, ch),
|
||||
't' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("t".to_string())), TokenizerState::T, token_str, ch),
|
||||
'u' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("u".to_string())), TokenizerState::U, token_str, ch),
|
||||
'w' => tokenize_terminal(last_index, *index, token, state, Some(Token::Name("w".to_string())), TokenizerState::W, token_str, ch),
|
||||
',' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Comma), TokenizerState::Comma),
|
||||
'=' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Equals), TokenizerState::Equals),
|
||||
'(' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::RoundOpen), TokenizerState::RoundOpen),
|
||||
')' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::RoundClosed), TokenizerState::RoundClosed),
|
||||
'.' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Dot), TokenizerState::Dot),
|
||||
':' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Colon), TokenizerState::Colon),
|
||||
'{' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::CurlyOpen), TokenizerState::CurlyOpen),
|
||||
'}' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::CurlyClosed), TokenizerState::CurlyClosed),
|
||||
'[' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::SquareOpen), TokenizerState::SquareOpen),
|
||||
']' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::SquareClosed), TokenizerState::SquareClosed),
|
||||
'+' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Plus), TokenizerState::Plus),
|
||||
'~' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Tilde), TokenizerState::Tilde),
|
||||
'>' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Gt), TokenizerState::Gt),
|
||||
'<' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Lt), TokenizerState::Lt),
|
||||
'#' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Hash), TokenizerState::Hash),
|
||||
'|' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Pipe), TokenizerState::Pipe),
|
||||
'&' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Ampersand), TokenizerState::Ampersand),
|
||||
'%' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Percent), TokenizerState::Percent),
|
||||
'*' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Star), TokenizerState::Star),
|
||||
'/' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Slash), TokenizerState::Slash),
|
||||
';' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Semicolon), TokenizerState::Semicolon),
|
||||
'^' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::Caret), TokenizerState::Caret),
|
||||
'0' => tokenize_terminal(last_index, *index, token, state, Some(Token::IntLiteral("0".to_string())), TokenizerState::Zero, token_str, ch),
|
||||
'"' =>
|
||||
{
|
||||
*token = None;
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
'\'' =>
|
||||
{
|
||||
*token = None;
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
if ch.is_whitespace() { }
|
||||
else if ch.is_ascii_alphabetic() || ch == '_'
|
||||
{
|
||||
tokenize_terminal(last_index, *index, token, state, Some(Token::Name(token_str.clone())), TokenizerState::Name, token_str, ch);
|
||||
}
|
||||
else if ch.is_numeric() && ch.is_ascii()
|
||||
{
|
||||
tokenize_terminal(last_index, *index, token, state, Some(Token::IntLiteral(token_str.clone())), TokenizerState::Number, token_str, ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
todo!("State {:?}, Char {}", state, ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TokenizerState::Quote =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'\\' =>
|
||||
{
|
||||
*state = TokenizerState::QuoteBackslash;
|
||||
}
|
||||
'"' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::StringLiteral(token_str.clone())), TokenizerState::String),
|
||||
_ =>
|
||||
{
|
||||
token_str.push(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
TokenizerState::QuoteBackslash =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'a' =>
|
||||
{
|
||||
token_str.push('\u{0007}');
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
'b' =>
|
||||
{
|
||||
token_str.push('\u{0008}');
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
't' =>
|
||||
{
|
||||
token_str.push('\t');
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
'n' | '\n' =>
|
||||
{
|
||||
token_str.push('\n');
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
'v' =>
|
||||
{
|
||||
token_str.push('\u{000b}');
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
'f' =>
|
||||
{
|
||||
token_str.push('\u{000c}');
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
'r' =>
|
||||
{
|
||||
token_str.push('\r');
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
'\\' =>
|
||||
{
|
||||
token_str.push('\\');
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
'"' =>
|
||||
{
|
||||
token_str.push('\"');
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
'\'' =>
|
||||
{
|
||||
token_str.push('\'');
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
'z' =>
|
||||
{
|
||||
*state = TokenizerState::QuoteBackslashZ;
|
||||
}
|
||||
_ => return Err("Unknown escape sequence"),
|
||||
}
|
||||
}
|
||||
TokenizerState::QuoteBackslashZ =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'\\' =>
|
||||
{
|
||||
*state = TokenizerState::QuoteBackslash;
|
||||
}
|
||||
'"' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::StringLiteral(token_str.clone())), TokenizerState::String),
|
||||
_ =>
|
||||
{
|
||||
if !ch.is_whitespace()
|
||||
{
|
||||
token_str.push(ch);
|
||||
*state = TokenizerState::Quote;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TokenizerState::SingleQuote =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'\\' =>
|
||||
{
|
||||
*state = TokenizerState::SingleQuoteBackslash;
|
||||
}
|
||||
'\'' =>
|
||||
{
|
||||
*last_index = *index as i32;
|
||||
*token = Some(Token::StringLiteral(token_str.clone()));
|
||||
*state = TokenizerState::String;
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
token_str.push(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
TokenizerState::SingleQuoteBackslash =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'a' =>
|
||||
{
|
||||
token_str.push('\u{0007}');
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
'b' =>
|
||||
{
|
||||
token_str.push('\u{0008}');
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
't' =>
|
||||
{
|
||||
token_str.push('\t');
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
'n' | '\n' =>
|
||||
{
|
||||
token_str.push('\n');
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
'v' =>
|
||||
{
|
||||
token_str.push('\u{000b}');
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
'f' =>
|
||||
{
|
||||
token_str.push('\u{000c}');
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
'r' =>
|
||||
{
|
||||
token_str.push('\r');
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
'\\' =>
|
||||
{
|
||||
token_str.push('\\');
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
'"' =>
|
||||
{
|
||||
token_str.push('\"');
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
'\'' =>
|
||||
{
|
||||
token_str.push('\'');
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
'z' =>
|
||||
{
|
||||
*state = TokenizerState::SingleQuoteBackslashZ;
|
||||
}
|
||||
_ => return Err("Unknown escape sequence"),
|
||||
}
|
||||
}
|
||||
TokenizerState::SingleQuoteBackslashZ =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'\\' =>
|
||||
{
|
||||
*state = TokenizerState::SingleQuoteBackslash;
|
||||
}
|
||||
'\'' =>
|
||||
{
|
||||
*last_index = *index as i32;
|
||||
*token = Some(Token::StringLiteral(token_str.clone()));
|
||||
*state = TokenizerState::String;
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
if !ch.is_whitespace()
|
||||
{
|
||||
token_str.push(ch);
|
||||
*state = TokenizerState::SingleQuote;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TokenizerState::String =>
|
||||
{
|
||||
let content = token_str.clone();
|
||||
tokenize_backtrack_custom_token(last_index, index, tokens, token, token_str, state, Token::StringLiteral(content))?;
|
||||
}
|
||||
TokenizerState::Name => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
TokenizerState::Zero =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'x' =>
|
||||
{
|
||||
token_str.push(ch);
|
||||
*token = None;
|
||||
*state = TokenizerState::HexNumberX;
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
if ch.is_numeric() && ch.is_ascii()
|
||||
{
|
||||
*last_index = *index as i32;
|
||||
token_str.push(ch);
|
||||
*token = Some(Token::IntLiteral(token_str.clone()));
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenize_backtrack(last_index, index, tokens, token, token_str, state)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TokenizerState::HexNumberX =>
|
||||
{
|
||||
if ch.is_ascii() && ch.is_numeric() || match ch
|
||||
{
|
||||
'A'..='F' | 'a'..='f' => true,
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
*last_index = *index as i32;
|
||||
token_str.push(ch);
|
||||
*token = Some(Token::HexLiteral(token_str.clone()));
|
||||
*state = TokenizerState::HexNumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenize_backtrack(last_index, index, tokens, token, token_str, state)?;
|
||||
}
|
||||
}
|
||||
TokenizerState::HexNumber =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'p' =>
|
||||
{
|
||||
token_str.push(ch);
|
||||
*token = None;
|
||||
*state = TokenizerState::HexExpNumber;
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
if ch.is_ascii() && ch.is_numeric() || match ch
|
||||
{
|
||||
'A'..='F' | 'a'..='f' => true,
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
*last_index = *index as i32;
|
||||
token_str.push(ch);
|
||||
*token = Some(Token::HexLiteral(token_str.clone()));
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenize_backtrack(last_index, index, tokens, token, token_str, state)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TokenizerState::Number =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'e' =>
|
||||
{
|
||||
token_str.push(ch);
|
||||
*token = None;
|
||||
*state = TokenizerState::ExpNumber;
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
if ch.is_numeric() && ch.is_ascii()
|
||||
{
|
||||
*last_index = *index as i32;
|
||||
token_str.push(ch);
|
||||
*token = Some(Token::IntLiteral(token_str.clone()));
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenize_backtrack(last_index, index, tokens, token, token_str, state)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TokenizerState::Comma | TokenizerState::RoundOpen | TokenizerState::RoundClosed |
|
||||
TokenizerState::CurlyOpen | TokenizerState::CurlyClosed | TokenizerState::Plus |
|
||||
TokenizerState::TildeEquals | TokenizerState::EqualsEquals | TokenizerState::Hash |
|
||||
TokenizerState::GtEquals | TokenizerState::LtEquals | TokenizerState::SquareOpen |
|
||||
TokenizerState::SquareClosed | TokenizerState::Pipe | TokenizerState::Ampersand |
|
||||
TokenizerState::Percent | TokenizerState::Star | TokenizerState::Semicolon |
|
||||
TokenizerState::Caret | TokenizerState::DotDotDot | TokenizerState::GtGt |
|
||||
TokenizerState::LtLt | TokenizerState::SlashSlash => tokenize_backtrack(last_index, index, tokens, token, token_str, state)?,
|
||||
TokenizerState::Tilde =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'=' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::TildeEquals), TokenizerState::TildeEquals),
|
||||
_ => tokenize_backtrack(last_index, index, tokens, token, token_str, state)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Gt =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'>' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::GtGt), TokenizerState::GtGt),
|
||||
'=' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::GtEquals), TokenizerState::GtEquals),
|
||||
_ => tokenize_backtrack(last_index, index, tokens, token, token_str, state)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Lt =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'>' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::LtLt), TokenizerState::LtLt),
|
||||
'=' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::LtEquals), TokenizerState::LtEquals),
|
||||
_ => tokenize_backtrack(last_index, index, tokens, token, token_str, state)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Slash =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'/' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::SlashSlash), TokenizerState::SlashSlash),
|
||||
_ => tokenize_backtrack(last_index, index, tokens, token, token_str, state)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Dot =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'.' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::DotDot), TokenizerState::DotDot),
|
||||
_ => tokenize_backtrack(last_index, index, tokens, token, token_str, state)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::DotDot =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'.' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::DotDotDot), TokenizerState::DotDotDot),
|
||||
_ => tokenize_backtrack(last_index, index, tokens, token, token_str, state)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Colon =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
':' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::ColonColon), TokenizerState::ColonColon),
|
||||
_ => tokenize_backtrack(last_index, index, tokens, token, token_str, state)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Equals =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'=' => tokenize_terminal_no_str(last_index, *index, token, state, Some(Token::EqualsEquals), TokenizerState::EqualsEquals),
|
||||
_ => tokenize_backtrack(last_index, index, tokens, token, token_str, state)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Minus =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'-' => tokenize_terminal_no_str(last_index, *index, token, state, None, TokenizerState::SmallCommentStart),
|
||||
_ => tokenize_backtrack(last_index, index, tokens, token, token_str, state)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::SmallCommentStart =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'[' =>
|
||||
{
|
||||
*token = None;
|
||||
*state = TokenizerState::BigCommentLongBracketStart;
|
||||
}
|
||||
'\n' =>
|
||||
{
|
||||
*state = TokenizerState::Start;
|
||||
*last_index = -1;
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
*state = TokenizerState::SmallComment;
|
||||
}
|
||||
}
|
||||
}
|
||||
TokenizerState::SmallComment =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'\n' =>
|
||||
{
|
||||
*state = TokenizerState::Start;
|
||||
*last_index = -1;
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
TokenizerState::BigCommentLongBracketStart =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'=' =>
|
||||
{
|
||||
*long_bracket_level += 1;
|
||||
}
|
||||
'[' =>
|
||||
{
|
||||
*state = TokenizerState::BigComment;
|
||||
}
|
||||
_ => return Err("Malformed long bracket at the beginning of a big comment"),
|
||||
}
|
||||
}
|
||||
TokenizerState::BigComment =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
']' =>
|
||||
{
|
||||
*state = TokenizerState::BigCommentLongBracketEnd;
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
TokenizerState::BigCommentLongBracketEnd =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'=' =>
|
||||
{
|
||||
if *long_bracket_level == 0
|
||||
{
|
||||
return Err("Long bracket level too big when ending big comment");
|
||||
}
|
||||
*long_bracket_level -= 1;
|
||||
}
|
||||
']' =>
|
||||
{
|
||||
if *long_bracket_level != 0
|
||||
{
|
||||
return Err("Long bracket level too small when ending big comment");
|
||||
}
|
||||
*state = TokenizerState::Start;
|
||||
}
|
||||
_ => return Err("Malformed long bracket when ending big comment"),
|
||||
}
|
||||
}
|
||||
TokenizerState::A =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'n' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::An, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::An =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'd' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::And, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::And => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::And)?,
|
||||
TokenizerState::W =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'h' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Wh, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Wh =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'i' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Whi, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Whi =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'l' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Whil, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Whil =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'e' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::While, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::While => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::While)?,
|
||||
TokenizerState::B =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'r' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Br, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Br =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'e' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Bre, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Bre =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'a' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Brea, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Brea =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'k' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Break, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Break => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Break)?,
|
||||
TokenizerState::G =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'o' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Go, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Go =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
't' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Got, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Got =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'o' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Goto, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Goto => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Goto)?,
|
||||
TokenizerState::R =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'e' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Re, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Re =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
't' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Ret, token_str, ch),
|
||||
'p' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Rep, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Ret =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'u' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Retu, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Retu =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'r' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Retur, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Retur =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'n' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Return, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Return => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Return)?,
|
||||
TokenizerState::Rep =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'e' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Repe, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Repe =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'a' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Repea, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Repea =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
't' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Repeat, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Repeat => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Repeat)?,
|
||||
TokenizerState::N =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'i' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Ni, token_str, ch),
|
||||
'o' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::No, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::No =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
't' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Not, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Not => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Not)?,
|
||||
TokenizerState::Ni =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'l' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Nil, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Nil => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Nil)?,
|
||||
TokenizerState::T =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'h' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Th, token_str, ch),
|
||||
'r' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Tr, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Th =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'e' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::The, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::The =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'n' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Then, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Then => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Then)?,
|
||||
TokenizerState::Tr =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'u' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Tru, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Tru =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'e' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::True, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::True => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::True)?,
|
||||
TokenizerState::E =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'l' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::El, token_str, ch),
|
||||
'n' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::En, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::En =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'd' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::End, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::End => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::End)?,
|
||||
TokenizerState::El =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
's' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Els, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Els =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'e' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Else, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Else =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'i' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Elsei, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Else)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Elsei =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'f' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Elseif, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Elseif => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Elseif)?,
|
||||
TokenizerState::O =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'r' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Or, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Or => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Or)?,
|
||||
TokenizerState::D =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'o' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Do, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Do => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Do)?,
|
||||
TokenizerState::I =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'f' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::If, token_str, ch),
|
||||
'n' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::In, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::In => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::In)?,
|
||||
TokenizerState::If => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::If)?,
|
||||
TokenizerState::F =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'a' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Fa, token_str, ch),
|
||||
'o' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Fo, token_str, ch),
|
||||
'u' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Fu, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Fu =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'n' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Fun, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Fun =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'c' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Func, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Func =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
't' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Funct, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Funct =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'i' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Functi, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Functi =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'o' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Functio, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Functio =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'n' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Function, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Function => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Function)?,
|
||||
TokenizerState::Fa =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'l' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Fal, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Fal =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
's' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Fals, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Fals =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'e' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::False, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::False => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::False)?,
|
||||
TokenizerState::Fo =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'r' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::For, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::For => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::For)?,
|
||||
TokenizerState::L =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'o' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Lo, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Lo =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'c' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Loc, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Loc =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'a' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Loca, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Loca =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'l' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Local, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Local => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Local)?,
|
||||
TokenizerState::U =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'n' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Un, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Un =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
't' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Unt, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Unt =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'i' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Unti, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Unti =>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'l' => tokenize_terminal_no_token(last_index, *index, state, TokenizerState::Until, token_str, ch),
|
||||
_ => tokenize_alphanumeric_nonstart(last_index, index, tokens, token, token_str, state, ch)?,
|
||||
}
|
||||
}
|
||||
TokenizerState::Until => tokenize_alphanumeric_nonstart_custom(last_index, index, tokens, token, token_str, state, ch, Token::Until)?,
|
||||
_ => todo!("State: {:?}", state),
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn tokenize(file_content: &String) -> Result<Vec<Token>, &'static str>
|
||||
{
|
||||
let mut tokens: Vec<Token> = Vec::new();
|
||||
let mut state = TokenizerState::Start;
|
||||
let char_vec: Vec<char> = file_content.chars().collect();
|
||||
|
||||
let mut last_index: i32 = -1;
|
||||
let mut index = 0;
|
||||
let mut token: Option<Token> = None;
|
||||
let mut token_str: String = String::new();
|
||||
let mut long_bracket_level = 0;
|
||||
|
||||
while index < char_vec.len()
|
||||
{
|
||||
let ch = char_vec[index];
|
||||
tokenize_char(&mut state, ch, &mut last_index, &mut index, &mut token, &mut token_str, &mut tokens, &mut long_bracket_level)?;
|
||||
index += 1;
|
||||
}
|
||||
match state
|
||||
{
|
||||
TokenizerState::Name => tokenize_backtrack_name(&mut last_index, &mut index, &mut tokens, &mut token, &mut token_str, &mut state)?,
|
||||
TokenizerState::End => tokenize_backtrack_custom_token(&mut last_index, &mut index, &mut tokens, &mut token, &mut token_str, &mut state, Token::End)?,
|
||||
TokenizerState::And => tokenize_backtrack_custom_token(&mut last_index, &mut index, &mut tokens, &mut token, &mut token_str, &mut state, Token::And)?,
|
||||
TokenizerState::Semicolon => tokenize_backtrack_custom_token(&mut last_index, &mut index, &mut tokens, &mut token, &mut token_str, &mut state, Token::Semicolon)?,
|
||||
_ => todo!("state: {:?}", state),
|
||||
}
|
||||
|
||||
return Ok(tokens);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
if T == nil then
|
||||
(Message or print)('\n >>> testC not active: \z
|
||||
skipping some generational tests <<<\n')
|
||||
print 'OK'
|
||||
return
|
||||
end
|
||||
@@ -1 +0,0 @@
|
||||
local smt = getmetatable("")
|
||||
@@ -1,8 +0,0 @@
|
||||
a, b = 12, test(32, 4)
|
||||
local t=(string.find(originalField.af,'m') and originalField.tableAction) or c.tableAction or originalField.tableAction or tableActionGeneric
|
||||
b = {["a"] = 23}
|
||||
for i=0, 10 do b[i] = 2^23 end
|
||||
print("asdf")
|
||||
function test(a, b)
|
||||
return 42 + a / b
|
||||
end
|
||||
@@ -1,5 +0,0 @@
|
||||
0.000001
|
||||
0.1
|
||||
0.12345
|
||||
1234566788.21212
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
0xff11 123 , () ~ ~= > >> >= < << <= / // . .. ... : :: = == - --
|
||||
-- asdf
|
||||
--[[askf]]zzz
|
||||
--[==========[asdfasdf]==========] zzzz
|
||||
[[fsad]]
|
||||
[=
|
||||
]
|
||||
[===[test]]=]==]===] zzzzz
|
||||
a ab an anb ando and
|
||||
w wo wh who whi whio whil whilo while whileo
|
||||
b bo br bro bre breo brea breao break breako
|
||||
g gz go goz got gotz goto gotoz
|
||||
r ro re reo ret rep reto repo retu repe retuo repeo retur repea returo repeao return repeat returno repeato
|
||||
n nz ni no niz noz nil not nilz notz
|
||||
t to th tr tho the tro tru then true theno trueo
|
||||
e eo el en elo eno els end elso endo else elso elsei elseif elseio elseifo
|
||||
o oo or oro
|
||||
d dz do doz
|
||||
i io if in ifo ino
|
||||
f fz fo fa fu foz faz fuz for fal fun forz falz funz fals func falsz funcz false funct falsez functz functi functiz functio functioz function functionz
|
||||
l lz lo loz loc locz loca locaz local localz
|
||||
u uo un uno unt unto unti untio until untilo
|
||||
@@ -1,10 +0,0 @@
|
||||
"test" "\z
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
abc" "123" "sdlfkgj<3" "asldkfj" zzz "" "" "" "" "" "fasd!" "afd" "" "as" zzzz
|
||||
Reference in New Issue
Block a user