This commit is contained in:
nub31
2025-05-24 20:37:26 +02:00
parent 010df49dc4
commit 757e3490f5
9 changed files with 132 additions and 69 deletions

View File

@@ -1,9 +1,14 @@
import c import c
// Test
// Test2
// Test3
// Test4
global func main(args: []string) { global func main(args: []string) {
i = 0 i = 0
printf("%d\n", args.count) printf("%d\n", args.count)
while i < args.count { while i < args.count {
// Test
printf("%s\n", args[i]) printf("%s\n", args[i])
i = i + 1 i = i + 1
} }

View File

@@ -0,0 +1,6 @@
namespace Nub.Lang.Frontend.Lexing;
public class CommentToken(string comment) : Token
{
public string Comment { get; } = comment;
}

View File

@@ -3,7 +3,7 @@
public class Lexer public class Lexer
{ {
private static readonly Dictionary<string, Symbol> Keywords = new() private static readonly Dictionary<string, Symbol> Keywords = new()
{ {
["func"] = Symbol.Func, ["func"] = Symbol.Func,
["import"] = Symbol.Import, ["import"] = Symbol.Import,
["if"] = Symbol.If, ["if"] = Symbol.If,
@@ -15,9 +15,9 @@ public class Lexer
["new"] = Symbol.New, ["new"] = Symbol.New,
["struct"] = Symbol.Struct, ["struct"] = Symbol.Struct,
}; };
private static readonly Dictionary<string, Modifier> Modifers = new() private static readonly Dictionary<string, Modifier> Modifers = new()
{ {
["global"] = Modifier.Global, ["global"] = Modifier.Global,
["extern"] = Modifier.Extern, ["extern"] = Modifier.Extern,
}; };
@@ -29,7 +29,7 @@ public class Lexer
[['<', '=']] = Symbol.LessThanOrEqual, [['<', '=']] = Symbol.LessThanOrEqual,
[['>', '=']] = Symbol.GreaterThanOrEqual, [['>', '=']] = Symbol.GreaterThanOrEqual,
}; };
private static readonly Dictionary<char, Symbol> Chars = new() private static readonly Dictionary<char, Symbol> Chars = new()
{ {
[';'] = Symbol.Semicolon, [';'] = Symbol.Semicolon,
@@ -53,7 +53,7 @@ public class Lexer
['^'] = Symbol.Caret, ['^'] = Symbol.Caret,
['&'] = Symbol.Ampersand, ['&'] = Symbol.Ampersand,
}; };
private string _src = string.Empty; private string _src = string.Empty;
private int _index; private int _index;
@@ -61,40 +61,58 @@ public class Lexer
{ {
_src = src; _src = src;
_index = 0; _index = 0;
List<Token> tokens = []; List<Token> tokens = [];
while (Peek().HasValue) while (Peek().TryGetValue(out var character))
{ {
tokens.Add(ParseToken()); if (char.IsWhiteSpace(character))
{
Next();
continue;
}
tokens.Add(ParseToken(character));
} }
return tokens; return tokens;
} }
private Token ParseToken() private Token ParseToken(char current)
{ {
var current = Peek(); if (current == '/' && Peek(1) is { Value: '/' })
{
if (char.IsLetter(current.Value) || current.Value == '_') Next();
Next();
var buffer = string.Empty;
while (Peek() is not { Value: '\n' })
{
buffer += Peek().Value;
Next();
}
Next();
return new CommentToken(buffer);
}
if (char.IsLetter(current) || current == '_')
{ {
var buffer = string.Empty; var buffer = string.Empty;
while (current.HasValue && (char.IsLetterOrDigit(current.Value) || current.Value == '_')) while (Peek().TryGetValue(out var next) && (char.IsLetterOrDigit(next) || next == '_'))
{ {
buffer += current.Value; buffer += next;
Next(); Next();
current = Peek();
} }
if (Keywords.TryGetValue(buffer, out var keywordSymbol)) if (Keywords.TryGetValue(buffer, out var keywordSymbol))
{ {
return new SymbolToken(keywordSymbol); return new SymbolToken(keywordSymbol);
} }
if (Modifers.TryGetValue(buffer, out var modifer)) if (Modifers.TryGetValue(buffer, out var modifer))
{ {
return new ModifierToken(modifer); return new ModifierToken(modifer);
} }
if (buffer is "true" or "false") if (buffer is "true" or "false")
{ {
return new LiteralToken(NubPrimitiveType.Bool, buffer); return new LiteralToken(NubPrimitiveType.Bool, buffer);
@@ -103,31 +121,30 @@ public class Lexer
return new IdentifierToken(buffer); return new IdentifierToken(buffer);
} }
if (char.IsDigit(current.Value)) if (char.IsDigit(current))
{ {
var isFloat = false; var isFloat = false;
var buffer = string.Empty; var buffer = string.Empty;
while (current.HasValue) while (Peek().TryGetValue(out var next))
{ {
if (current.Value == '.') if (next == '.')
{ {
if (isFloat) if (isFloat)
{ {
throw new Exception("More than one period found in float literal"); throw new Exception("More than one period found in float literal");
} }
isFloat = true; isFloat = true;
buffer += current.Value; buffer += next;
Next(); Next();
current = Peek();
}
else if (char.IsDigit(current.Value))
{
buffer += current.Value;
Next();
current = Peek();
} }
else if (current.Value == 'f') else if (char.IsDigit(next))
{
buffer += next;
Next();
}
else if (next == 'f')
{ {
isFloat = true; isFloat = true;
Next(); Next();
@@ -138,15 +155,15 @@ public class Lexer
break; break;
} }
} }
return new LiteralToken(isFloat ? NubPrimitiveType.F64 : NubPrimitiveType.I64, buffer); return new LiteralToken(isFloat ? NubPrimitiveType.F64 : NubPrimitiveType.I64, buffer);
} }
// TODO: Revisit this // TODO: Revisit this
foreach (var chain in Chians) foreach (var chain in Chians)
{ {
if (current.Value != chain.Key[0]) continue; if (current != chain.Key[0]) continue;
for (var i = 1; i < chain.Key.Length; i++) for (var i = 1; i < chain.Key.Length; i++)
{ {
var c = Peek(i); var c = Peek(i);
@@ -164,36 +181,38 @@ public class Lexer
} }
} }
if (Chars.TryGetValue(current.Value, out var charSymbol)) if (Chars.TryGetValue(current, out var charSymbol))
{ {
Next(); Next();
return new SymbolToken(charSymbol); return new SymbolToken(charSymbol);
} }
if (current.Value == '"') if (current == '"')
{ {
Next(); Next();
var buffer = string.Empty; var buffer = string.Empty;
while (true) while (true)
{ {
current = Peek(); if (!Peek().TryGetValue(out var next))
{
throw new Exception("Unclosed string literal");
}
if (next == '"')
{
Next();
break;
}
buffer += next;
Next(); Next();
if (!current.HasValue) throw new Exception("Unclosed string literal");
if (current.Value == '"') break;
buffer += current.Value;
} }
return new LiteralToken(NubPrimitiveType.String, buffer); return new LiteralToken(NubPrimitiveType.String, buffer);
} }
if (char.IsWhiteSpace(current.Value)) throw new Exception($"Unknown character {current}");
{
Next();
return new SymbolToken(Symbol.Whitespace);
}
throw new Exception($"Unknown character {current.Value}");
} }
private Optional<char> Peek(int offset = 0) private Optional<char> Peek(int offset = 0)
@@ -202,7 +221,7 @@ public class Lexer
{ {
return _src[_index + offset]; return _src[_index + offset];
} }
return Optional<char>.Empty(); return Optional<char>.Empty();
} }

View File

@@ -7,7 +7,6 @@ public class SymbolToken(Symbol symbol) : Token
public enum Symbol public enum Symbol
{ {
Whitespace,
Import, Import,
Func, Func,
Return, Return,

View File

@@ -1,10 +1,11 @@
namespace Nub.Lang.Frontend.Parsing; namespace Nub.Lang.Frontend.Parsing;
public class ExternFuncDefinitionNode(string name, List<FuncParameter> parameters, Optional<NubType> returnType) : DefinitionNode public class ExternFuncDefinitionNode(string name, List<FuncParameter> parameters, Optional<NubType> returnType, Optional<string> documentation) : DefinitionNode
{ {
public string Name { get; } = name; public string Name { get; } = name;
public List<FuncParameter> Parameters { get; } = parameters; public List<FuncParameter> Parameters { get; } = parameters;
public Optional<NubType> ReturnType { get; } = returnType; public Optional<NubType> ReturnType { get; } = returnType;
public Optional<string> Documentation { get; set; } = documentation;
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}"; public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
} }

View File

@@ -1,12 +1,13 @@
namespace Nub.Lang.Frontend.Parsing; namespace Nub.Lang.Frontend.Parsing;
public class LocalFuncDefinitionNode(string name, List<FuncParameter> parameters, BlockNode body, Optional<NubType> returnType, bool global) : DefinitionNode public class LocalFuncDefinitionNode(string name, List<FuncParameter> parameters, BlockNode body, Optional<NubType> returnType, bool global, Optional<string> documentation) : DefinitionNode
{ {
public string Name { get; } = name; public string Name { get; } = name;
public List<FuncParameter> Parameters { get; } = parameters; public List<FuncParameter> Parameters { get; } = parameters;
public BlockNode Body { get; } = body; public BlockNode Body { get; } = body;
public Optional<NubType> ReturnType { get; } = returnType; public Optional<NubType> ReturnType { get; } = returnType;
public bool Global { get; } = global; public bool Global { get; } = global;
public Optional<string> Documentation { get; set; } = documentation;
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}"; public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
} }

View File

@@ -36,6 +36,15 @@ public class Parser
{ {
List<Modifier> modifiers = []; List<Modifier> modifiers = [];
List<string> documentationParts = [];
while (_index < _tokens.Count && _tokens[_index] is CommentToken commentToken)
{
documentationParts.Add(commentToken.Comment);
_index++;
}
var documentation = documentationParts.Count == 0 ? null : string.Join('\n', documentationParts);
while (TryExpectModifier(out var modifier)) while (TryExpectModifier(out var modifier))
{ {
modifiers.Add(modifier); modifiers.Add(modifier);
@@ -44,13 +53,13 @@ public class Parser
var keyword = ExpectSymbol(); var keyword = ExpectSymbol();
return keyword.Symbol switch return keyword.Symbol switch
{ {
Symbol.Func => ParseFuncDefinition(modifiers), Symbol.Func => ParseFuncDefinition(modifiers, Optional.OfNullable(documentation)),
Symbol.Struct => ParseStruct(modifiers), Symbol.Struct => ParseStruct(modifiers, Optional.OfNullable(documentation)),
_ => throw new Exception("Unexpected symbol: " + keyword.Symbol) _ => throw new Exception("Unexpected symbol: " + keyword.Symbol)
}; };
} }
private DefinitionNode ParseFuncDefinition(List<Modifier> modifiers) private DefinitionNode ParseFuncDefinition(List<Modifier> modifiers, Optional<string> documentation)
{ {
var name = ExpectIdentifier(); var name = ExpectIdentifier();
List<FuncParameter> parameters = []; List<FuncParameter> parameters = [];
@@ -77,7 +86,7 @@ public class Parser
throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for an extern function"); throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for an extern function");
} }
return new ExternFuncDefinitionNode(name.Value, parameters, returnType); return new ExternFuncDefinitionNode(name.Value, parameters, returnType, documentation);
} }
var body = ParseBlock(); var body = ParseBlock();
@@ -88,10 +97,10 @@ public class Parser
throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for a local function"); throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for a local function");
} }
return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType, global); return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType, global, documentation);
} }
private StructDefinitionNode ParseStruct(List<Modifier> _) private StructDefinitionNode ParseStruct(List<Modifier> _, Optional<string> documentation)
{ {
var name = ExpectIdentifier().Value; var name = ExpectIdentifier().Value;
@@ -115,7 +124,7 @@ public class Parser
variables.Add(new StructField(variableName, variableType, variableValue)); variables.Add(new StructField(variableName, variableType, variableValue));
} }
return new StructDefinitionNode(name, variables); return new StructDefinitionNode(name, variables, documentation);
} }
private FuncParameter ParseFuncParameter() private FuncParameter ParseFuncParameter()
@@ -230,7 +239,7 @@ public class Parser
private ExpressionNode ParseExpression(int precedence = 0) private ExpressionNode ParseExpression(int precedence = 0)
{ {
var left = ParsePrimaryExpression(); var left = ParsePrimaryExpression();
while (true) while (true)
{ {
var token = Peek(); var token = Peek();
@@ -308,7 +317,7 @@ public class Parser
private ExpressionNode ParsePrimaryExpression() private ExpressionNode ParsePrimaryExpression()
{ {
ExpressionNode expr; ExpressionNode expr;
var token = ExpectToken(); var token = ExpectToken();
switch (token) switch (token)
{ {
@@ -341,6 +350,7 @@ public class Parser
break; break;
} }
} }
break; break;
} }
case SymbolToken symbolToken: case SymbolToken symbolToken:
@@ -403,6 +413,7 @@ public class Parser
throw new Exception($"Unknown symbol: {symbolToken.Symbol}"); throw new Exception($"Unknown symbol: {symbolToken.Symbol}");
} }
} }
break; break;
} }
default: default:
@@ -463,7 +474,7 @@ public class Parser
{ {
return NubType.Parse(name); return NubType.Parse(name);
} }
if (TryExpectSymbol(Symbol.Caret)) if (TryExpectSymbol(Symbol.Caret))
{ {
var baseType = ParseType(); var baseType = ParseType();
@@ -558,14 +569,15 @@ public class Parser
private Optional<Token> Peek() private Optional<Token> Peek()
{ {
while (_index < _tokens.Count && _tokens.ElementAt(_index) is SymbolToken { Symbol: Symbol.Whitespace }) var peekIndex = _index;
while (peekIndex < _tokens.Count && _tokens[peekIndex] is CommentToken)
{ {
Next(); peekIndex++;
} }
if (_index < _tokens.Count) if (peekIndex < _tokens.Count)
{ {
return _tokens.ElementAt(_index); return _tokens[peekIndex];
} }
return Optional<Token>.Empty(); return Optional<Token>.Empty();
@@ -573,6 +585,11 @@ public class Parser
private void Next() private void Next()
{ {
while (_index < _tokens.Count && _tokens[_index] is CommentToken)
{
_index++;
}
_index++; _index++;
} }
} }

View File

@@ -1,7 +1,8 @@
namespace Nub.Lang.Frontend.Parsing; namespace Nub.Lang.Frontend.Parsing;
public class StructDefinitionNode(string name, List<StructField> fields) : DefinitionNode public class StructDefinitionNode(string name, List<StructField> fields, Optional<string> documentation) : DefinitionNode
{ {
public string Name { get; } = name; public string Name { get; } = name;
public List<StructField> Fields { get; } = fields; public List<StructField> Fields { get; } = fields;
public Optional<string> Documentation { get; set; } = documentation;
} }

View File

@@ -44,5 +44,19 @@ public readonly struct Optional<TValue>
[MemberNotNullWhen(true, nameof(Value))] [MemberNotNullWhen(true, nameof(Value))]
public bool HasValue { get; } public bool HasValue { get; }
[MemberNotNullWhen(true, nameof(Value))]
public bool TryGetValue([NotNullWhen(true)] out TValue? value)
{
if (HasValue)
{
value = Value;
return true;
}
value = default;
return false;
}
public static implicit operator Optional<TValue>(TValue value) => new(value); public static implicit operator Optional<TValue>(TValue value) => new(value);
} }