This commit is contained in:
nub31
2025-09-29 16:23:39 +02:00
parent 857e29d77b
commit c0948e856a
13 changed files with 568 additions and 474 deletions

View File

@@ -21,7 +21,7 @@ public static class Archive
var errors = await process.StandardError.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(errors))
{
await Console.Error.WriteLineAsync("ar error when archiving:\n" + errors);
await Console.Error.WriteLineAsync(errors);
}
return process.ExitCode == 0;

View File

@@ -22,7 +22,7 @@ public static class GCC
var errors = await process.StandardError.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(errors))
{
await Console.Error.WriteLineAsync("gcc error when assembling:\n" + errors);
await Console.Error.WriteLineAsync(errors);
}
return process.ExitCode == 0;

View File

@@ -22,7 +22,7 @@ public static class QBE
var errors = await process.StandardError.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(errors))
{
await Console.Error.WriteLineAsync("qbe error:\n" + errors);
await Console.Error.WriteLineAsync(errors);
}
return process.ExitCode == 0;

View File

@@ -118,7 +118,7 @@ public class Diagnostic
var contextEndLine = Math.Min(lines.Length, endLine + CONTEXT_LINES);
var numberPadding = contextEndLine.ToString().Length;
var codePadding = lines.Skip(contextStartLine - 1).Take(contextEndLine - contextStartLine).Max(x => x.Length);
var codePadding = lines.Skip(contextStartLine - 1).Take(contextEndLine - contextStartLine + 1).Max(x => x.Length);
sb.Append('╭');
sb.Append(new string('─', numberPadding + 2));
@@ -144,16 +144,16 @@ public class Diagnostic
if (i >= startLine && i <= endLine)
{
var markerStartColumn = 1;
var markerEndColumn = line.Length + 1;
var markerEndColumn = line.Length;
if (i == startLine)
{
markerStartColumn = Span.Value.Start.Column;
markerStartColumn = Math.Min(Span.Value.Start.Column, 1);
}
if (i == endLine)
{
markerEndColumn = Span.Value.End.Column;
markerEndColumn = Math.Min(Span.Value.End.Column, line.Length);
}
var markerLength = markerEndColumn - markerStartColumn;

View File

@@ -34,4 +34,12 @@ public class Module
.Where(x => x.Exported || includePrivate)
.ToList();
}
public List<GlobalVariableSyntax> GlobalVariables(bool includePrivate)
{
return _definitions
.OfType<GlobalVariableSyntax>()
.Where(x => x.Exported || includePrivate)
.ToList();
}
}

View File

@@ -86,6 +86,7 @@ public sealed class Parser
{
Symbol.Func => ParseFunc(startIndex, exported, null),
Symbol.Struct => ParseStruct(startIndex, exported),
Symbol.Let => ParseGlobalVariable(startIndex, exported),
_ => throw new ParseException(Diagnostic
.Error($"Expected 'func' or 'struct' but found '{keyword.Symbol}'")
.WithHelp("Valid definition keywords are 'func' and 'struct'")
@@ -113,6 +114,22 @@ public sealed class Parser
return definitions;
}
private GlobalVariableSyntax ParseGlobalVariable(int startIndex, bool exported)
{
var name = ExpectIdentifier();
TypeSyntax? type = null;
if (TryExpectSymbol(Symbol.Colon))
{
type = ParseType();
}
ExpectSymbol(Symbol.Assign);
var value = ParseExpression();
return new GlobalVariableSyntax(GetTokens(startIndex), name.Value, exported, type, value);
}
private FuncSignatureSyntax ParseFuncSignature()
{
var startIndex = _tokenIndex;

View File

@@ -17,3 +17,5 @@ public record StructFuncSyntax(List<Token> Tokens, string Name, string? Hook, Fu
public record StructSyntax(List<Token> Tokens, string Name, bool Exported, List<StructFieldSyntax> Fields, List<StructFuncSyntax> Functions) : DefinitionSyntax(Tokens, Name, Exported);
public record StructTemplateSyntax(List<Token> Tokens, List<string> TemplateArguments, string Name, bool Exported, List<StructFieldSyntax> Fields, List<StructFuncSyntax> Functions) : DefinitionSyntax(Tokens, Name, Exported);
public record GlobalVariableSyntax(List<Token> Tokens, string Name, bool Exported, TypeSyntax? ExplicitType, ExpressionSyntax Value) : DefinitionSyntax(Tokens, Name, Exported);

View File

@@ -5,6 +5,8 @@ namespace NubLang.Tokenization;
public enum LiteralKind
{
Integer,
Hex,
Binary,
Float,
String,
Bool

View File

@@ -159,6 +159,51 @@ public sealed class Tokenizer
var isFloat = false;
var buffer = string.Empty;
if (current == '0' && Peek(1) is 'x')
{
buffer += "0x";
Next();
Next();
while (Peek() != null && Uri.IsHexDigit(Peek()!.Value))
{
buffer += Peek()!.Value;
Next();
}
if (buffer.Length <= 2)
{
throw new TokenizerException(Diagnostic
.Error("Invalid hex literal, no digits found")
.At(_fileName, _line, _column)
.Build());
}
return new LiteralToken(_fileName, CreateSpan(lineStart, columnStart), LiteralKind.Hex, buffer);
}
if (current == '0' && Peek(1) is 'b')
{
buffer += "0b";
Next();
Next();
while (Peek() != null && (Peek() == '0' || Peek() == '1'))
{
buffer += Peek()!.Value;
Next();
}
if (buffer.Length <= 2)
{
throw new TokenizerException(Diagnostic
.Error("Invalid binary literal, no digits found")
.At(_fileName, _line, _column)
.Build());
}
return new LiteralToken(_fileName, CreateSpan(lineStart, columnStart), LiteralKind.Binary, buffer);
}
buffer += current;
while (Peek() != null)
{
var next = Peek()!.Value;
@@ -187,7 +232,12 @@ public sealed class Tokenizer
}
}
return new LiteralToken(_fileName, CreateSpan(lineStart, columnStart), isFloat ? LiteralKind.Float : LiteralKind.Integer, buffer);
return new LiteralToken(
_fileName,
CreateSpan(lineStart, columnStart),
isFloat ? LiteralKind.Float : LiteralKind.Integer,
buffer
);
}
if (current == '"')

View File

@@ -12,4 +12,6 @@ public record StructFieldNode(string Name, NubType Type, ExpressionNode? Value)
public record StructFuncNode(string Name, string? Hook, FuncSignatureNode Signature, BlockNode Body) : Node;
public record StructNode(string Module, string Name, List<StructFieldNode> Fields, List<StructFuncNode> Functions) : DefinitionNode(Module, Name);
public record StructNode(string Module, string Name, List<StructFieldNode> Fields, List<StructFuncNode> Functions) : DefinitionNode(Module, Name);
public record GlobalVariableNode(string Module, string Name, ExpressionNode Value) : DefinitionNode(Module, Name);

View File

@@ -46,27 +46,33 @@ public sealed class TypeChecker
Definitions.Clear();
ReferencedStructTypes.Clear();
foreach (var definition in _syntaxTree.Definitions)
using (BeginRootScope(_syntaxTree.Metadata.ModuleName))
{
try
foreach (var definition in _syntaxTree.Definitions)
{
switch (definition)
try
{
case FuncSyntax funcSyntax:
Definitions.Add(CheckFuncDefinition(funcSyntax));
break;
case StructSyntax structSyntax:
Definitions.Add(CheckStructDefinition(structSyntax));
break;
case StructTemplateSyntax:
break;
default:
throw new ArgumentOutOfRangeException();
switch (definition)
{
case FuncSyntax funcSyntax:
Definitions.Add(CheckFuncDefinition(funcSyntax));
break;
case StructSyntax structSyntax:
Definitions.Add(CheckStructDefinition(structSyntax));
break;
case GlobalVariableSyntax globalVariableSyntax:
Definitions.Add(CheckGlobalVariable(globalVariableSyntax));
break;
case StructTemplateSyntax:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
catch (TypeCheckerException e)
{
Diagnostics.Add(e.Diagnostic);
}
}
catch (TypeCheckerException e)
{
Diagnostics.Add(e.Diagnostic);
}
}
}
@@ -85,9 +91,41 @@ public sealed class TypeChecker
return new ScopeDisposer(this);
}
private ScopeDisposer BeginRootScope(string module)
private ScopeDisposer BeginRootScope(string moduleName)
{
_scopes.Push(new Scope(module));
if (!_visibleModules.TryGetValue(moduleName, out var moduleScope))
{
throw new TypeCheckerException(Diagnostic.Error($"Module with name {moduleName} not found").Build());
}
var scope = new Scope(moduleName);
_scopes.Push(scope);
foreach (var globalVariable in moduleScope.GlobalVariables(true))
{
NubType? type;
if (globalVariable.ExplicitType != null)
{
type = ResolveType(globalVariable.ExplicitType);
var valueExpression = CheckExpression(globalVariable.Value, type);
if (valueExpression.Type != ResolveType(globalVariable.ExplicitType))
{
throw new TypeCheckerException(Diagnostic
.Error("Value does not match explicit type of global variable")
.At(globalVariable.Value)
.Build());
}
}
else
{
type = CheckExpression(globalVariable.Value).Type;
}
scope.DeclareVariable(new Variable(globalVariable.Name, type, VariableKind.LValue));
}
return new ScopeDisposer(this);
}
@@ -105,30 +143,27 @@ public sealed class TypeChecker
private StructNode CheckStructDefinition(StructSyntax node)
{
using (BeginRootScope(_syntaxTree.Metadata.ModuleName))
{
var fieldTypes = node.Fields
.Select(x => new NubStructFieldType(x.Name, ResolveType(x.Type), x.Value != null))
.ToList();
var fieldTypes = node.Fields
.Select(x => new NubStructFieldType(x.Name, ResolveType(x.Type), x.Value != null))
.ToList();
var fieldFunctions = node.Functions
.Select(x =>
{
var parameters = x.Signature.Parameters.Select(y => ResolveType(y.Type)).ToList();
var returnType = ResolveType(x.Signature.ReturnType);
return new NubStructFuncType(x.Name, x.Hook, parameters, returnType);
})
.ToList();
var fieldFunctions = node.Functions
.Select(x =>
{
var parameters = x.Signature.Parameters.Select(y => ResolveType(y.Type)).ToList();
var returnType = ResolveType(x.Signature.ReturnType);
return new NubStructFuncType(x.Name, x.Hook, parameters, returnType);
})
.ToList();
var structType = new NubStructType(CurrentScope.Module, node.Name, fieldTypes, fieldFunctions);
var structType = new NubStructType(CurrentScope.Module, node.Name, fieldTypes, fieldFunctions);
CurrentScope.DeclareVariable(new Variable("this", structType, VariableKind.RValue));
CurrentScope.DeclareVariable(new Variable("this", structType, VariableKind.RValue));
var fields = node.Fields.Select(CheckStructField).ToList();
var functions = node.Functions.Select(CheckStructFunc).ToList();
var fields = node.Fields.Select(CheckStructField).ToList();
var functions = node.Functions.Select(CheckStructFunc).ToList();
return new StructNode(CurrentScope.Module, node.Name, fields, functions);
}
return new StructNode(CurrentScope.Module, node.Name, fields, functions);
}
private StructFuncNode CheckStructFunc(StructFuncSyntax function)
@@ -157,42 +192,44 @@ public sealed class TypeChecker
private FuncNode CheckFuncDefinition(FuncSyntax node)
{
using (BeginRootScope(_syntaxTree.Metadata.ModuleName))
foreach (var parameter in node.Signature.Parameters)
{
foreach (var parameter in node.Signature.Parameters)
{
CurrentScope.DeclareVariable(new Variable(parameter.Name, ResolveType(parameter.Type), VariableKind.RValue));
}
var signature = CheckFuncSignature(node.Signature);
BlockNode? body = null;
if (node.Body != null)
{
_funcReturnTypes.Push(signature.ReturnType);
body = CheckBlock(node.Body);
if (!AlwaysReturns(body))
{
if (signature.ReturnType is NubVoidType)
{
body.Statements.Add(new ReturnNode(null));
}
else
{
Diagnostics.Add(Diagnostic
.Error("Not all code paths return a value")
.At(node.Body)
.Build());
}
}
_funcReturnTypes.Pop();
}
return new FuncNode(CurrentScope.Module, node.Name, node.ExternSymbol, signature, body);
CurrentScope.DeclareVariable(new Variable(parameter.Name, ResolveType(parameter.Type), VariableKind.RValue));
}
var signature = CheckFuncSignature(node.Signature);
BlockNode? body = null;
if (node.Body != null)
{
_funcReturnTypes.Push(signature.ReturnType);
body = CheckBlock(node.Body);
if (!AlwaysReturns(body))
{
if (signature.ReturnType is NubVoidType)
{
body.Statements.Add(new ReturnNode(null));
}
else
{
Diagnostics.Add(Diagnostic
.Error("Not all code paths return a value")
.At(node.Body)
.Build());
}
}
_funcReturnTypes.Pop();
}
return new FuncNode(CurrentScope.Module, node.Name, node.ExternSymbol, signature, body);
}
private GlobalVariableNode CheckGlobalVariable(GlobalVariableSyntax node)
{
return new GlobalVariableNode(CurrentScope.Module, node.Name, CheckExpression(node.Value));
}
private AssignmentNode CheckAssignment(AssignmentSyntax statement)
@@ -701,6 +738,19 @@ public sealed class TypeChecker
var includePrivate = expression.Module == CurrentScope.Module;
var globalVariable = module.GlobalVariables(includePrivate).FirstOrDefault(x => x.Name == expression.Name);
if (globalVariable != null)
{
// todo(nub31): This should be done in the global scope
NubType? type = null;
if (globalVariable.ExplicitType != null)
{
type = ResolveType(globalVariable.ExplicitType);
}
return CheckExpression(globalVariable.Value, type);
}
// First, look for the exported function in the specified module
var function = module.Functions(includePrivate).FirstOrDefault(x => x.Name == expression.Name);
if (function != null)
@@ -733,8 +783,8 @@ public sealed class TypeChecker
{
return floatType.Width switch
{
32 => new Float32LiteralNode(floatType, float.Parse(expression.Value)),
64 => new Float64LiteralNode(floatType, double.Parse(expression.Value)),
32 => new Float32LiteralNode(floatType, float.Parse(expression.Value, CultureInfo.InvariantCulture)),
64 => new Float64LiteralNode(floatType, double.Parse(expression.Value, CultureInfo.InvariantCulture)),
_ => throw new ArgumentOutOfRangeException()
};
}
@@ -749,6 +799,30 @@ public sealed class TypeChecker
? new Float32LiteralNode(type, float.Parse(expression.Value, CultureInfo.InvariantCulture))
: new Float64LiteralNode(type, double.Parse(expression.Value, CultureInfo.InvariantCulture));
}
case LiteralKind.Hex:
{
if (expectedType is NubIntType intType)
{
return intType.Signed
? new IntLiteralNode(intType, Convert.ToInt64(expression.Value, 16))
: new UIntLiteralNode(intType, Convert.ToUInt64(expression.Value, 16));
}
var type = new NubIntType(true, 64);
return new IntLiteralNode(type, Convert.ToInt64(expression.Value, 16));
}
case LiteralKind.Binary:
{
if (expectedType is NubIntType intType)
{
return intType.Signed
? new IntLiteralNode(intType, Convert.ToInt64(expression.Value[2..], 2))
: new UIntLiteralNode(intType, Convert.ToUInt64(expression.Value[2..], 2));
}
var type = new NubIntType(true, 64);
return new IntLiteralNode(type, Convert.ToInt64(expression.Value.Substring(2), 2));
}
case LiteralKind.String:
{
return expectedType switch