This commit is contained in:
nub31
2025-01-26 01:15:02 +01:00
parent a6f2bb132b
commit 8c51c31927
24 changed files with 426 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

16
Nub.Lang/Nub.Lang.sln Normal file
View File

@@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nub.Lang", "Nub.Lang\Nub.Lang.csproj", "{5047E21F-590D-4CB3-AFF3-064316485009}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5047E21F-590D-4CB3-AFF3-064316485009}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5047E21F-590D-4CB3-AFF3-064316485009}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5047E21F-590D-4CB3-AFF3-064316485009}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5047E21F-590D-4CB3-AFF3-064316485009}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,14 @@
let SYS_WRITE = 1;
let STD_IN = 0;
let STD_OUT = 1;
let STD_ERR = 2;
func main() {
write("test");
syscall(SYS_WRITE, STD_OUT, msg);
return 12;
}
func write(msg: void) {
syscall(SYS_WRITE, STD_OUT, msg);
}

View File

@@ -0,0 +1,6 @@
namespace Nub.Lang.Lexing;
public class IdentifierToken(string value) : Token
{
public string Value { get; } = value;
}

View File

@@ -0,0 +1,131 @@
using Nub.Lib;
namespace Nub.Lang.Lexing;
public class Lexer
{
private static readonly Dictionary<string, Symbol> Keywords = new()
{
["func"] = Symbol.Func,
["return"] = Symbol.Return,
["let"] = Symbol.Let,
};
private static readonly Dictionary<char, Symbol> Chars = new()
{
[';'] = Symbol.Semicolon,
[':'] = Symbol.Colon,
['('] = Symbol.OpenParen,
[')'] = Symbol.CloseParen,
['{'] = Symbol.OpenBrace,
['}'] = Symbol.CloseBrace,
['['] = Symbol.OpenBracket,
[']'] = Symbol.CloseBracket,
[','] = Symbol.Comma,
['.'] = Symbol.Period,
['='] = Symbol.Assign,
};
private readonly string _src;
private int _index;
public Lexer(string src)
{
_src = src;
}
public IEnumerable<Token> Lex()
{
_index = 0;
List<Token> tokens = [];
while (Peek().HasValue)
{
tokens.Add(ParseToken());
}
return tokens;
}
private Token ParseToken()
{
var current = Peek();
if (char.IsLetter(current.Value) || current.Value == '_')
{
var buffer = string.Empty;
while (current.HasValue && (char.IsLetterOrDigit(current.Value) || current.Value == '_'))
{
buffer += current.Value;
Next();
current = Peek();
}
if (Keywords.TryGetValue(buffer, out var keywordSymbol))
{
return new SymbolToken(keywordSymbol);
}
return new IdentifierToken(buffer);
}
if (char.IsDigit(current.Value))
{
var buffer = string.Empty;
while (current.HasValue && char.IsDigit(current.Value))
{
buffer += current.Value;
Next();
current = Peek();
}
return new LiteralToken(Type.Int32, buffer);
}
if (Chars.TryGetValue(current.Value, out var charSymbol))
{
Next();
return new SymbolToken(charSymbol);
}
if (current.Value == '"')
{
Next();
var buffer = string.Empty;
while (true)
{
current = Peek();
Next();
if (!current.HasValue) throw new Exception("Unclosed string literal");
if (current.Value == '"') break;
buffer += current.Value;
}
return new LiteralToken(Type.Pointer, buffer);
}
if (char.IsWhiteSpace(current.Value))
{
Next();
return new SymbolToken(Symbol.Whitespace);
}
throw new Exception($"Unknown character {current.Value}");
}
private Optional<char> Peek()
{
if (_index < _src.Length)
{
return _src[_index];
}
return Optional<char>.Empty();
}
private void Next()
{
_index++;
}
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Lexing;
public class LiteralToken(Type type, string value) : Token
{
public Type Type { get; } = type;
public string Value { get; } = value;
}

View File

@@ -0,0 +1,25 @@
namespace Nub.Lang.Lexing;
public class SymbolToken(Symbol symbol) : Token
{
public Symbol Symbol { get; } = symbol;
}
public enum Symbol
{
Whitespace,
Func,
Return,
Let,
Semicolon,
Colon,
OpenParen,
CloseParen,
OpenBrace,
CloseBrace,
OpenBracket,
CloseBracket,
Comma,
Period,
Assign
}

View File

@@ -0,0 +1,3 @@
namespace Nub.Lang.Lexing;
public abstract class Token;

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nub.Lib" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
namespace Nub.Lang.Parsing;
public class BlockNode(IEnumerable<StatementNode> statements) : StatementNode
{
public IEnumerable<StatementNode> Statements { get; } = statements;
}

View File

@@ -0,0 +1,3 @@
namespace Nub.Lang.Parsing;
public abstract class DefinitionNode : Node;

View File

@@ -0,0 +1,11 @@
namespace Nub.Lang.Parsing;
public abstract class ExpressionNode : Node
{
private Type? _type;
public Type Type
{
get => _type ?? throw new Exception("Tried to access expression type before type was populated");
set => _type = value;
}
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Parsing;
public class FuncDefinitionNode(string name, IEnumerable<FuncParameter> parameters) : DefinitionNode
{
public string Name { get; } = name;
public IEnumerable<FuncParameter> Parameters { get; } = parameters;
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Parsing;
public class FuncParameter(string name, Type type)
{
public string Name { get; } = name;
public Type Type { get; } = type;
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Parsing;
public class GlobalVariableDefinitionNode(string name, ExpressionNode value) : DefinitionNode
{
public string Name { get; } = name;
public ExpressionNode Value { get; } = value;
}

View File

@@ -0,0 +1,3 @@
namespace Nub.Lang.Parsing;
public abstract class Node;

View File

@@ -0,0 +1,95 @@
using Nub.Lang.Lexing;
using Nub.Lib;
namespace Nub.Lang.Parsing;
public class Parser
{
private readonly Token[] _tokens;
private int _index;
public Parser(IEnumerable<Token> tokens)
{
_tokens = tokens.ToArray();
}
public IEnumerable<DefinitionNode> Parse()
{
List<DefinitionNode> definitions = [];
while (Peek().HasValue)
{
definitions.Add(ParseDefinition());
}
return definitions;
}
private DefinitionNode ParseDefinition()
{
throw new NotImplementedException();
}
private Token ExpectToken()
{
var token = Peek();
if (!token.HasValue)
{
throw new Exception("Reached end of tokens");
}
return token.Value;
}
private SymbolToken ExpectSymbol()
{
var token = ExpectToken();
if (token is not SymbolToken symbol)
{
throw new Exception($"Expected {nameof(SymbolToken)} but got {token.GetType().Name}");
}
return symbol;
}
private void ExpectSymbol(Symbol symbol)
{
var token = ExpectSymbol();
if (token.Symbol != symbol)
{
throw new Exception($"Expected symbol {symbol} but got {token.Symbol}");
}
}
private IdentifierToken ExpectIdentifier()
{
var token = ExpectToken();
if (token is not IdentifierToken identifier)
{
throw new Exception($"Expected {nameof(IdentifierToken)} but got {token.GetType().Name}");
}
return identifier;
}
private LiteralToken ExpectLiteral()
{
var token = ExpectToken();
if (token is not LiteralToken literal)
{
throw new Exception($"Expected {nameof(LiteralToken)} but got {token.GetType().Name}");
}
return literal;
}
private Optional<Token> Peek()
{
if (_index < _tokens.Length)
{
return _tokens[_index];
}
return Optional<Token>.Empty();
}
private void Next()
{
_index++;
}
}

View File

@@ -0,0 +1,3 @@
namespace Nub.Lang.Parsing;
public abstract class StatementNode : Node;

View File

@@ -0,0 +1,11 @@
namespace Nub.Lang.Parsing;
public class ESyscallNode(IEnumerable<FuncParameter> parameters) : ExpressionNode
{
public IEnumerable<FuncParameter> Parameters { get; } = parameters;
}
public class SSyscallNode(IEnumerable<FuncParameter> parameters) : StatementNode
{
public IEnumerable<FuncParameter> Parameters { get; } = parameters;
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Parsing;
public class VariableAssignmentNode(string name, ExpressionNode value) : StatementNode
{
public string Name { get; } = name;
public ExpressionNode Value { get; } = value;
}

View File

@@ -0,0 +1,6 @@
using Nub.Lang.Lexing;
var src = File.ReadAllText(args[0]);
var lexer = new Lexer(src);
var tokens = lexer.Lex();

26
Nub.Lang/Nub.Lang/Type.cs Normal file
View File

@@ -0,0 +1,26 @@
namespace Nub.Lang;
public class Type(string name)
{
public static Type Bool => new("bool");
public static Type Char => new("char");
public static Type Int8 => new("int8");
public static Type UInt8 => new("uint8");
public static Type Int16 => new("int16");
public static Type UInt16 => new("uint16");
public static Type Int32 => new("int32");
public static Type UInt32 => new("uint32");
public static Type Int64 => new("int64");
public static Type UInt64 => new("uint64");
public static Type Float => new("char");
public static Type Double => new("double");
public static Type Pointer => new("pointer");
public string Name = name;
}