...
This commit is contained in:
@@ -16,8 +16,8 @@ public static class AstExtensions
|
|||||||
|
|
||||||
return new Location
|
return new Location
|
||||||
{
|
{
|
||||||
Uri = node.Tokens.First().Span.FilePath,
|
Uri = node.Tokens.First().Span.SourcePath,
|
||||||
Range = new Range(node.Tokens.First().Span.Start.Line - 1, node.Tokens.First().Span.Start.Column - 1, node.Tokens.Last().Span.End.Line - 1, node.Tokens.Last().Span.End.Column - 1)
|
Range = new Range(node.Tokens.First().Span.StartLine - 1, node.Tokens.First().Span.StartColumn - 1, node.Tokens.Last().Span.EndLine - 1, node.Tokens.Last().Span.EndColumn - 1)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,20 +25,17 @@ public static class AstExtensions
|
|||||||
{
|
{
|
||||||
return new Location
|
return new Location
|
||||||
{
|
{
|
||||||
Uri = token.Span.FilePath,
|
Uri = token.Span.SourcePath,
|
||||||
Range = new Range(token.Span.Start.Line - 1, token.Span.Start.Column - 1, token.Span.End.Line - 1, token.Span.End.Column - 1)
|
Range = new Range(token.Span.StartLine - 1, token.Span.StartColumn - 1, token.Span.EndLine - 1, token.Span.EndColumn - 1)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool ContainsPosition(this Token token, int line, int character)
|
public static bool ContainsPosition(this Token token, int line, int character)
|
||||||
{
|
{
|
||||||
var start = token.Span.Start;
|
var startLine = token.Span.StartLine - 1;
|
||||||
var end = token.Span.End;
|
var startChar = token.Span.StartColumn - 1;
|
||||||
|
var endLine = token.Span.EndLine - 1;
|
||||||
var startLine = start.Line - 1;
|
var endChar = token.Span.EndColumn - 1;
|
||||||
var startChar = start.Column - 1;
|
|
||||||
var endLine = end.Line - 1;
|
|
||||||
var endChar = end.Column - 1;
|
|
||||||
|
|
||||||
if (line < startLine || line > endLine) return false;
|
if (line < startLine || line > endLine) return false;
|
||||||
|
|
||||||
@@ -69,13 +66,12 @@ public static class AstExtensions
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var start = node.Tokens.First().Span.Start;
|
var span = node.Tokens.First().Span;
|
||||||
var end = node.Tokens.Last().Span.End;
|
|
||||||
|
|
||||||
var startLine = start.Line - 1;
|
var startLine = span.StartLine - 1;
|
||||||
var startChar = start.Column - 1;
|
var startChar = span.StartColumn - 1;
|
||||||
var endLine = end.Line - 1;
|
var endLine = span.EndLine - 1;
|
||||||
var endChar = end.Column - 1;
|
var endChar = span.EndColumn - 1;
|
||||||
|
|
||||||
if (line < startLine || line > endLine) return false;
|
if (line < startLine || line > endLine) return false;
|
||||||
|
|
||||||
@@ -111,8 +107,8 @@ public static class AstExtensions
|
|||||||
return compilationUnit
|
return compilationUnit
|
||||||
.SelectMany(x => x.DescendantsAndSelf())
|
.SelectMany(x => x.DescendantsAndSelf())
|
||||||
.Where(n => n.ContainsPosition(line, character))
|
.Where(n => n.ContainsPosition(line, character))
|
||||||
.OrderBy(n => n.Tokens.First().Span.Start.Line)
|
.OrderBy(n => n.Tokens.First().Span.StartLine)
|
||||||
.ThenBy(n => n.Tokens.First().Span.Start.Column)
|
.ThenBy(n => n.Tokens.First().Span.StartColumn)
|
||||||
.LastOrDefault();
|
.LastOrDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ public class DiagnosticsPublisher
|
|||||||
},
|
},
|
||||||
Message = $"{nubDiagnostic.Message}\n{(nubDiagnostic.Help == null ? "" : $"help: {nubDiagnostic.Help}")}",
|
Message = $"{nubDiagnostic.Message}\n{(nubDiagnostic.Help == null ? "" : $"help: {nubDiagnostic.Help}")}",
|
||||||
Range = nubDiagnostic.Span.HasValue
|
Range = nubDiagnostic.Span.HasValue
|
||||||
? new Range(nubDiagnostic.Span.Value.Start.Line - 1, nubDiagnostic.Span.Value.Start.Column - 1, nubDiagnostic.Span.Value.End.Line - 1, nubDiagnostic.Span.Value.End.Column - 1)
|
? new Range(nubDiagnostic.Span.Value.StartLine - 1, nubDiagnostic.Span.Value.StartColumn - 1, nubDiagnostic.Span.Value.EndLine - 1, nubDiagnostic.Span.Value.EndColumn - 1)
|
||||||
: new Range(),
|
: new Range(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Type {value.Type} is not assignable to {field.Type} for field {field.NameToken.Value}")
|
.Error($"Type {value.Type} is not assignable to {field.Type} for field {field.NameToken.Value}")
|
||||||
.At(field)
|
.At(field, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,7 +144,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Cannot assign {value.Type} to {target.Type}")
|
.Error($"Cannot assign {value.Type} to {target.Type}")
|
||||||
.At(statement.Value)
|
.At(statement.Value, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ public sealed class TypeChecker
|
|||||||
return expression switch
|
return expression switch
|
||||||
{
|
{
|
||||||
FuncCallNode funcCall => new StatementFuncCallNode(statement.Tokens, funcCall),
|
FuncCallNode funcCall => new StatementFuncCallNode(statement.Tokens, funcCall),
|
||||||
_ => throw new CompileException(Diagnostic.Error("Expressions statements can only be function calls").At(statement).Build())
|
_ => throw new CompileException(Diagnostic.Error("Expressions statements can only be function calls").At(statement, _syntaxTree.Tokens).Build())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Cannot assign {assignmentNode.Type} to variable of type {type}")
|
.Error($"Cannot assign {assignmentNode.Type} to variable of type {type}")
|
||||||
.At(statement.Assignment)
|
.At(statement.Assignment, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Cannot infer type of variable {statement.NameToken.Value}")
|
.Error($"Cannot infer type of variable {statement.NameToken.Value}")
|
||||||
.At(statement)
|
.At(statement, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +274,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Cannot iterate over type {target.Type} which does not have size information")
|
.Error($"Cannot iterate over type {target.Type} which does not have size information")
|
||||||
.At(forSyntax.Target)
|
.At(forSyntax.Target, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,7 +337,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Unable to infer target type of cast")
|
.Error("Unable to infer target type of cast")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.WithHelp("Specify target type where value is used")
|
.WithHelp("Specify target type where value is used")
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
@@ -348,7 +348,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
Diagnostics.Add(Diagnostic
|
Diagnostics.Add(Diagnostic
|
||||||
.Warning("Target type of cast is same as the value. Cast is unnecessary")
|
.Warning("Target type of cast is same as the value. Cast is unnecessary")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@@ -452,7 +452,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Array indexer must be of type int")
|
.Error("Array indexer must be of type int")
|
||||||
.At(expression.Index)
|
.At(expression.Index, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,7 +463,7 @@ public sealed class TypeChecker
|
|||||||
NubArrayType arrayType => new ArrayIndexAccessNode(expression.Tokens, arrayType.ElementType, target, index),
|
NubArrayType arrayType => new ArrayIndexAccessNode(expression.Tokens, arrayType.ElementType, target, index),
|
||||||
NubConstArrayType constArrayType => new ConstArrayIndexAccessNode(expression.Tokens, constArrayType.ElementType, target, index),
|
NubConstArrayType constArrayType => new ConstArrayIndexAccessNode(expression.Tokens, constArrayType.ElementType, target, index),
|
||||||
NubSliceType sliceType => new SliceIndexAccessNode(expression.Tokens, sliceType.ElementType, target, index),
|
NubSliceType sliceType => new SliceIndexAccessNode(expression.Tokens, sliceType.ElementType, target, index),
|
||||||
_ => throw new CompileException(Diagnostic.Error($"Cannot use array indexer on type {target.Type}").At(expression).Build())
|
_ => throw new CompileException(Diagnostic.Error($"Cannot use array indexer on type {target.Type}").At(expression, _syntaxTree.Tokens).Build())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,7 +490,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Unable to infer type of array initializer")
|
.Error("Unable to infer type of array initializer")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.WithHelp("Provide a type for a variable assignment")
|
.WithHelp("Provide a type for a variable assignment")
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
@@ -503,7 +503,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Value in array initializer is not the same as the array type")
|
.Error("Value in array initializer is not the same as the array type")
|
||||||
.At(valueExpression)
|
.At(valueExpression, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,7 +548,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Equal and not equal operators must must be used with int, float or bool types")
|
.Error("Equal and not equal operators must must be used with int, float or bool types")
|
||||||
.At(expression.Left)
|
.At(expression.Left, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,7 +557,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
|
.Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
|
||||||
.At(expression.Right)
|
.At(expression.Right, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,7 +573,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Greater than and less than operators must must be used with int or float types")
|
.Error("Greater than and less than operators must must be used with int or float types")
|
||||||
.At(expression.Left)
|
.At(expression.Left, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,7 +582,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
|
.Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
|
||||||
.At(expression.Right)
|
.At(expression.Right, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,7 +596,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Logical and/or must must be used with bool types")
|
.Error("Logical and/or must must be used with bool types")
|
||||||
.At(expression.Left)
|
.At(expression.Left, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,7 +605,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
|
.Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
|
||||||
.At(expression.Right)
|
.At(expression.Right, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,7 +618,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("The plus operator must only be used with int and float types")
|
.Error("The plus operator must only be used with int and float types")
|
||||||
.At(expression.Left)
|
.At(expression.Left, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -643,7 +643,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Math operators must be used with int or float types")
|
.Error("Math operators must be used with int or float types")
|
||||||
.At(expression.Left)
|
.At(expression.Left, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,7 +652,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
|
.Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
|
||||||
.At(expression.Right)
|
.At(expression.Right, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,7 +666,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Bitwise operators must be used with int types")
|
.Error("Bitwise operators must be used with int types")
|
||||||
.At(expression.Left)
|
.At(expression.Left, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,7 +675,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Bitwise operators must be used with int types")
|
.Error("Bitwise operators must be used with int types")
|
||||||
.At(expression.Right)
|
.At(expression.Right, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,7 +690,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Bitwise operators must be used with int types")
|
.Error("Bitwise operators must be used with int types")
|
||||||
.At(expression.Left)
|
.At(expression.Left, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,7 +699,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
|
.Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}")
|
||||||
.At(expression.Right)
|
.At(expression.Right, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,7 +723,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Negation operator must be used with signed integer or float types")
|
.Error("Negation operator must be used with signed integer or float types")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -736,7 +736,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Invert operator must be used with booleans")
|
.Error("Invert operator must be used with booleans")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,7 +756,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Cannot dereference non-pointer type {target.Type}")
|
.Error($"Cannot dereference non-pointer type {target.Type}")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,14 +768,14 @@ public sealed class TypeChecker
|
|||||||
var accessor = CheckExpression(expression.Expression);
|
var accessor = CheckExpression(expression.Expression);
|
||||||
if (accessor.Type is not NubFuncType funcType)
|
if (accessor.Type is not NubFuncType funcType)
|
||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic.Error($"Cannot call non-function type {accessor.Type}").At(expression.Expression).Build());
|
throw new CompileException(Diagnostic.Error($"Cannot call non-function type {accessor.Type}").At(expression.Expression, _syntaxTree.Tokens).Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expression.Parameters.Count != funcType.Parameters.Count)
|
if (expression.Parameters.Count != funcType.Parameters.Count)
|
||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Function {funcType} expects {funcType.Parameters.Count} parameters but got {expression.Parameters.Count}")
|
.Error($"Function {funcType} expects {funcType.Parameters.Count} parameters but got {expression.Parameters.Count}")
|
||||||
.At(expression.Parameters.LastOrDefault(expression))
|
.At(expression.Parameters.LastOrDefault(expression), _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,7 +790,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Parameter {i + 1} does not match the type {expectedParameterType} for function {funcType}")
|
.Error($"Parameter {i + 1} does not match the type {expectedParameterType} for function {funcType}")
|
||||||
.At(parameter)
|
.At(parameter, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -817,7 +817,7 @@ public sealed class TypeChecker
|
|||||||
|
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"There is no identifier named {expression.NameToken.Value}")
|
.Error($"There is no identifier named {expression.NameToken.Value}")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -834,7 +834,7 @@ public sealed class TypeChecker
|
|||||||
|
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Module {expression.ModuleToken.Value} does not export a member named {expression.NameToken.Value}")
|
.Error($"Module {expression.ModuleToken.Value} does not export a member named {expression.NameToken.Value}")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -909,7 +909,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Struct {target.Type} does not have a field with the name {expression.MemberToken.Value}")
|
.Error($"Struct {target.Type} does not have a field with the name {expression.MemberToken.Value}")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -919,7 +919,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Cannot access struct member {expression.MemberToken.Value} on type {target.Type}")
|
.Error($"Cannot access struct member {expression.MemberToken.Value} on type {target.Type}")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -950,7 +950,7 @@ public sealed class TypeChecker
|
|||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Cannot get implicit type of struct")
|
.Error("Cannot get implicit type of struct")
|
||||||
.WithHelp("Specify struct type with struct {type_name} syntax")
|
.WithHelp("Specify struct type with struct {type_name} syntax")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -963,7 +963,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
Diagnostics.AddRange(Diagnostic
|
Diagnostics.AddRange(Diagnostic
|
||||||
.Error($"Struct {structType.Name} does not have a field named {initializer.Key}")
|
.Error($"Struct {structType.Name} does not have a field named {initializer.Key}")
|
||||||
.At(initializer.Value)
|
.At(initializer.Value, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -981,7 +981,7 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
Diagnostics.Add(Diagnostic
|
Diagnostics.Add(Diagnostic
|
||||||
.Warning($"Fields {string.Join(", ", missingFields)} are not initialized")
|
.Warning($"Fields {string.Join(", ", missingFields)} are not initialized")
|
||||||
.At(expression)
|
.At(expression, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1065,7 +1065,7 @@ public sealed class TypeChecker
|
|||||||
|
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Type {customType.NameToken.Value} not found in module {module.Name}")
|
.Error($"Type {customType.NameToken.Value} not found in module {module.Name}")
|
||||||
.At(customType)
|
.At(customType, _syntaxTree.Tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public class Diagnostic
|
|||||||
private readonly string _message;
|
private readonly string _message;
|
||||||
private SourceSpan? _span;
|
private SourceSpan? _span;
|
||||||
private string? _help;
|
private string? _help;
|
||||||
|
private List<Token>? _tokens;
|
||||||
|
|
||||||
public DiagnosticBuilder(DiagnosticSeverity severity, string message)
|
public DiagnosticBuilder(DiagnosticSeverity severity, string message)
|
||||||
{
|
{
|
||||||
@@ -18,18 +19,32 @@ public class Diagnostic
|
|||||||
_message = message;
|
_message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiagnosticBuilder At(SyntaxNode? node)
|
public DiagnosticBuilder At(SyntaxNode? node, List<Token>? tokens = null)
|
||||||
{
|
{
|
||||||
|
if (tokens != null)
|
||||||
|
{
|
||||||
|
_tokens = tokens;
|
||||||
|
}
|
||||||
|
|
||||||
if (node != null)
|
if (node != null)
|
||||||
|
{
|
||||||
|
var first = node.Tokens.FirstOrDefault();
|
||||||
|
if (first != null)
|
||||||
{
|
{
|
||||||
_span = SourceSpan.Merge(node.Tokens.Select(x => x.Span));
|
_span = SourceSpan.Merge(node.Tokens.Select(x => x.Span));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiagnosticBuilder At(Token? token)
|
public DiagnosticBuilder At(Token? token, List<Token>? tokens = null)
|
||||||
{
|
{
|
||||||
|
if (tokens != null)
|
||||||
|
{
|
||||||
|
_tokens = tokens;
|
||||||
|
}
|
||||||
|
|
||||||
if (token != null)
|
if (token != null)
|
||||||
{
|
{
|
||||||
At(token.Span);
|
At(token.Span);
|
||||||
@@ -48,11 +63,11 @@ public class Diagnostic
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiagnosticBuilder At(string filePath, int line, int column)
|
// public DiagnosticBuilder At(string filePath, int line, int column)
|
||||||
{
|
// {
|
||||||
_span = new SourceSpan(filePath, new SourceLocation(line, column), new SourceLocation(line, column));
|
// _span = new SourceSpan(filePath, new SourceLocation(line, column), new SourceLocation(line, column));
|
||||||
return this;
|
// return this;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public DiagnosticBuilder WithHelp(string help)
|
public DiagnosticBuilder WithHelp(string help)
|
||||||
{
|
{
|
||||||
@@ -60,20 +75,23 @@ public class Diagnostic
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Diagnostic Build() => new(_severity, _message, _help, _span);
|
public Diagnostic Build() => new(_severity, _message, _help, _span, _tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DiagnosticBuilder Error(string message) => new(DiagnosticSeverity.Error, message);
|
public static DiagnosticBuilder Error(string message) => new(DiagnosticSeverity.Error, message);
|
||||||
public static DiagnosticBuilder Warning(string message) => new(DiagnosticSeverity.Warning, message);
|
public static DiagnosticBuilder Warning(string message) => new(DiagnosticSeverity.Warning, message);
|
||||||
public static DiagnosticBuilder Info(string message) => new(DiagnosticSeverity.Info, message);
|
public static DiagnosticBuilder Info(string message) => new(DiagnosticSeverity.Info, message);
|
||||||
|
|
||||||
|
private readonly List<Token>? _tokens;
|
||||||
|
|
||||||
public DiagnosticSeverity Severity { get; }
|
public DiagnosticSeverity Severity { get; }
|
||||||
public string Message { get; }
|
public string Message { get; }
|
||||||
public string? Help { get; }
|
public string? Help { get; }
|
||||||
public SourceSpan? Span { get; }
|
public SourceSpan? Span { get; }
|
||||||
|
|
||||||
private Diagnostic(DiagnosticSeverity severity, string message, string? help, SourceSpan? span)
|
private Diagnostic(DiagnosticSeverity severity, string message, string? help, SourceSpan? span, List<Token>? tokens)
|
||||||
{
|
{
|
||||||
|
_tokens = tokens;
|
||||||
Severity = severity;
|
Severity = severity;
|
||||||
Message = message;
|
Message = message;
|
||||||
Help = help;
|
Help = help;
|
||||||
@@ -103,15 +121,12 @@ public class Diagnostic
|
|||||||
if (Span.HasValue)
|
if (Span.HasValue)
|
||||||
{
|
{
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
var text = File.ReadAllText(Span.Value.FilePath);
|
var text = Span.Value.Source;
|
||||||
|
|
||||||
var tokenizer = new Tokenizer();
|
|
||||||
var tokens = tokenizer.Tokenize(Span.Value.FilePath, text);
|
|
||||||
|
|
||||||
var lines = text.Split('\n');
|
var lines = text.Split('\n');
|
||||||
|
|
||||||
var startLine = Span.Value.Start.Line;
|
var startLine = Span.Value.StartLine;
|
||||||
var endLine = Span.Value.End.Line;
|
var endLine = Span.Value.EndLine;
|
||||||
|
|
||||||
const int CONTEXT_LINES = 3;
|
const int CONTEXT_LINES = 3;
|
||||||
|
|
||||||
@@ -144,8 +159,15 @@ public class Diagnostic
|
|||||||
sb.Append("│ ");
|
sb.Append("│ ");
|
||||||
sb.Append(i.ToString().PadRight(numberPadding));
|
sb.Append(i.ToString().PadRight(numberPadding));
|
||||||
sb.Append(" │ ");
|
sb.Append(" │ ");
|
||||||
sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokens));
|
if (_tokens != null)
|
||||||
// sb.Append(line.PadRight(codePadding));
|
{
|
||||||
|
sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, _tokens));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append(line.PadRight(codePadding));
|
||||||
|
}
|
||||||
|
|
||||||
sb.Append(" │");
|
sb.Append(" │");
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
|
|
||||||
@@ -156,12 +178,12 @@ public class Diagnostic
|
|||||||
|
|
||||||
if (i == startLine)
|
if (i == startLine)
|
||||||
{
|
{
|
||||||
markerStartColumn = Span.Value.Start.Column;
|
markerStartColumn = Span.Value.StartColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == endLine)
|
if (i == endLine)
|
||||||
{
|
{
|
||||||
markerEndColumn = Span.Value.End.Column;
|
markerEndColumn = Span.Value.EndColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
var markerLength = markerEndColumn - markerStartColumn;
|
var markerLength = markerEndColumn - markerStartColumn;
|
||||||
@@ -206,8 +228,8 @@ public class Diagnostic
|
|||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
var lineTokens = tokens
|
var lineTokens = tokens
|
||||||
.Where(t => t.Span.Start.Line == lineNumber)
|
.Where(t => t.Span.StartLine == lineNumber)
|
||||||
.OrderBy(t => t.Span.Start.Column)
|
.OrderBy(t => t.Span.StartColumn)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (lineTokens.Count == 0)
|
if (lineTokens.Count == 0)
|
||||||
@@ -219,8 +241,10 @@ public class Diagnostic
|
|||||||
|
|
||||||
foreach (var token in lineTokens)
|
foreach (var token in lineTokens)
|
||||||
{
|
{
|
||||||
var tokenStart = token.Span.Start.Column;
|
if (token is WhitespaceToken) continue;
|
||||||
var tokenEnd = token.Span.End.Column;
|
|
||||||
|
var tokenStart = token.Span.StartColumn;
|
||||||
|
var tokenEnd = token.Span.EndColumn;
|
||||||
|
|
||||||
if (tokenStart > currentColumn && currentColumn - 1 < line.Length)
|
if (tokenStart > currentColumn && currentColumn - 1 < line.Length)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,112 +1,56 @@
|
|||||||
namespace NubLang.Diagnostics;
|
namespace NubLang.Diagnostics;
|
||||||
|
|
||||||
public readonly struct SourceSpan : IEquatable<SourceSpan>, IComparable<SourceSpan>
|
public readonly struct SourceSpan
|
||||||
{
|
{
|
||||||
|
private readonly int _startIndex;
|
||||||
|
private readonly int _endIndex;
|
||||||
|
|
||||||
public static SourceSpan Merge(params IEnumerable<SourceSpan> spans)
|
public static SourceSpan Merge(params IEnumerable<SourceSpan> spans)
|
||||||
{
|
{
|
||||||
var spanArray = spans as SourceSpan[] ?? spans.ToArray();
|
var spanArray = spans as SourceSpan[] ?? spans.ToArray();
|
||||||
if (spanArray.Length == 0)
|
if (spanArray.Length == 0)
|
||||||
{
|
{
|
||||||
return new SourceSpan(string.Empty, new SourceLocation(0, 0), new SourceLocation(0, 0));
|
return new SourceSpan(string.Empty, string.Empty, 0, 0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
var minStart = spanArray.Min(s => s.Start);
|
var first = spanArray.MinBy(x => x._startIndex);
|
||||||
var maxEnd = spanArray.Max(s => s.End);
|
var last = spanArray.MaxBy(x => x._endIndex);
|
||||||
|
|
||||||
return new SourceSpan(spanArray[0].FilePath, minStart, maxEnd);
|
return new SourceSpan(first.SourcePath, first.Source, first._startIndex, last._endIndex, first.StartLine, last.EndLine, first.StartColumn, last.EndColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SourceSpan(string filePath, SourceLocation start, SourceLocation end)
|
public SourceSpan(string sourcePath, string source, int startIndex, int endIndex, int startLine, int startColumn, int endLine, int endColumn)
|
||||||
{
|
{
|
||||||
if (start > end)
|
_startIndex = startIndex;
|
||||||
{
|
_endIndex = endIndex;
|
||||||
throw new ArgumentException("Start location cannot be after end location");
|
SourcePath = sourcePath;
|
||||||
|
Source = source;
|
||||||
|
StartLine = startLine;
|
||||||
|
StartColumn = startColumn;
|
||||||
|
EndLine = endLine;
|
||||||
|
EndColumn = endColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePath = filePath;
|
public int StartLine { get; }
|
||||||
Start = start;
|
public int StartColumn { get; }
|
||||||
End = end;
|
public int EndLine { get; }
|
||||||
}
|
public int EndColumn { get; }
|
||||||
|
|
||||||
public string FilePath { get; }
|
public string SourcePath { get; }
|
||||||
public SourceLocation Start { get; }
|
public string Source { get; }
|
||||||
public SourceLocation End { get; }
|
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
if (Start == End)
|
if (StartLine == EndLine && StartColumn == EndColumn)
|
||||||
{
|
{
|
||||||
return $"{FilePath}:{Start}";
|
return $"{SourcePath}:{StartColumn}:{StartColumn}";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Start.Line == End.Line)
|
if (StartLine == EndLine)
|
||||||
{
|
{
|
||||||
return Start.Column == End.Column ? $"{FilePath}:{Start}" : $"{FilePath}:{Start.Line}:{Start.Column}-{End.Column}";
|
return $"{SourcePath}:{StartLine}:{StartColumn}-{EndColumn}";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"{FilePath}:{Start}-{End}";
|
return $"{SourcePath}:{StartLine}:{StartColumn}-{EndLine}:{EndColumn}";
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(SourceSpan other) => Start == other.Start && End == other.End;
|
|
||||||
public override bool Equals(object? obj) => obj is SourceSpan other && Equals(other);
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(SourceSpan), Start, End);
|
|
||||||
|
|
||||||
public static bool operator ==(SourceSpan left, SourceSpan right) => Equals(left, right);
|
|
||||||
public static bool operator !=(SourceSpan left, SourceSpan right) => !Equals(left, right);
|
|
||||||
|
|
||||||
public static bool operator <(SourceSpan left, SourceSpan right) => left.CompareTo(right) < 0;
|
|
||||||
public static bool operator <=(SourceSpan left, SourceSpan right) => left.CompareTo(right) <= 0;
|
|
||||||
public static bool operator >(SourceSpan left, SourceSpan right) => left.CompareTo(right) > 0;
|
|
||||||
public static bool operator >=(SourceSpan left, SourceSpan right) => left.CompareTo(right) >= 0;
|
|
||||||
|
|
||||||
public int CompareTo(SourceSpan other)
|
|
||||||
{
|
|
||||||
var startComparison = Start.CompareTo(other.Start);
|
|
||||||
return startComparison != 0 ? startComparison : End.CompareTo(other.End);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct SourceLocation : IEquatable<SourceLocation>, IComparable<SourceLocation>
|
|
||||||
{
|
|
||||||
public SourceLocation(int line, int column)
|
|
||||||
{
|
|
||||||
Line = line;
|
|
||||||
Column = column;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Line { get; }
|
|
||||||
public int Column { get; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"{Line}:{Column}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
{
|
|
||||||
return obj is SourceLocation other && Equals(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(SourceLocation other)
|
|
||||||
{
|
|
||||||
return Line == other.Line && Column == other.Column;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return HashCode.Combine(typeof(SourceLocation), Line, Column);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(SourceLocation left, SourceLocation right) => Equals(left, right);
|
|
||||||
public static bool operator !=(SourceLocation left, SourceLocation right) => !Equals(left, right);
|
|
||||||
public static bool operator <(SourceLocation left, SourceLocation right) => left.Line < right.Line || (left.Line == right.Line && left.Column < right.Column);
|
|
||||||
public static bool operator >(SourceLocation left, SourceLocation right) => left.Line > right.Line || (left.Line == right.Line && left.Column > right.Column);
|
|
||||||
public static bool operator <=(SourceLocation left, SourceLocation right) => left.Line <= right.Line || (left.Line == right.Line && left.Column <= right.Column);
|
|
||||||
public static bool operator >=(SourceLocation left, SourceLocation right) => left.Line >= right.Line || (left.Line == right.Line && left.Column >= right.Column);
|
|
||||||
|
|
||||||
public int CompareTo(SourceLocation other)
|
|
||||||
{
|
|
||||||
var lineComparison = Line.CompareTo(other.Line);
|
|
||||||
return lineComparison != 0 ? lineComparison : Column.CompareTo(other.Column);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ public sealed class Parser
|
|||||||
_ => throw new CompileException(Diagnostic
|
_ => throw new CompileException(Diagnostic
|
||||||
.Error($"Expected 'func', 'struct', 'enum', 'import' or 'module' but found '{keyword.Symbol}'")
|
.Error($"Expected 'func', 'struct', 'enum', 'import' or 'module' but found '{keyword.Symbol}'")
|
||||||
.WithHelp("Valid top level statements are 'func', 'struct', 'enum', 'import' and 'module'")
|
.WithHelp("Valid top level statements are 'func', 'struct', 'enum', 'import' and 'module'")
|
||||||
.At(keyword)
|
.At(keyword, _tokens)
|
||||||
.Build())
|
.Build())
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ public sealed class Parser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SyntaxTree(topLevelSyntaxNodes);
|
return new SyntaxTree(topLevelSyntaxNodes, _tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModuleSyntax ParseModule(int startIndex)
|
private ModuleSyntax ParseModule(int startIndex)
|
||||||
@@ -190,7 +190,7 @@ public sealed class Parser
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Value of enum field must be an integer literal")
|
.Error("Value of enum field must be an integer literal")
|
||||||
.At(CurrentToken)
|
.At(CurrentToken, _tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,7 +461,7 @@ public sealed class Parser
|
|||||||
IdentifierToken identifier => ParseIdentifier(startIndex, identifier),
|
IdentifierToken identifier => ParseIdentifier(startIndex, identifier),
|
||||||
SymbolToken symbolToken => symbolToken.Symbol switch
|
SymbolToken symbolToken => symbolToken.Symbol switch
|
||||||
{
|
{
|
||||||
Symbol.Ampersand => ParseAddressOf(startIndex),
|
Symbol.Caret => ParseAddressOf(startIndex),
|
||||||
Symbol.OpenParen => ParseParenthesizedExpression(),
|
Symbol.OpenParen => ParseParenthesizedExpression(),
|
||||||
Symbol.Minus => ParseUnaryNegate(startIndex),
|
Symbol.Minus => ParseUnaryNegate(startIndex),
|
||||||
Symbol.Bang => ParseUnaryInvert(startIndex),
|
Symbol.Bang => ParseUnaryInvert(startIndex),
|
||||||
@@ -472,13 +472,13 @@ public sealed class Parser
|
|||||||
_ => throw new CompileException(Diagnostic
|
_ => throw new CompileException(Diagnostic
|
||||||
.Error($"Unexpected symbol '{symbolToken.Symbol}' in expression")
|
.Error($"Unexpected symbol '{symbolToken.Symbol}' in expression")
|
||||||
.WithHelp("Expected '(', '-', '!', '[' or '{'")
|
.WithHelp("Expected '(', '-', '!', '[' or '{'")
|
||||||
.At(symbolToken)
|
.At(symbolToken, _tokens)
|
||||||
.Build())
|
.Build())
|
||||||
},
|
},
|
||||||
_ => throw new CompileException(Diagnostic
|
_ => throw new CompileException(Diagnostic
|
||||||
.Error($"Unexpected token '{token.GetType().Name}' in expression")
|
.Error($"Unexpected token '{token.GetType().Name}' in expression")
|
||||||
.WithHelp("Expected literal, identifier, or parenthesized expression")
|
.WithHelp("Expected literal, identifier, or parenthesized expression")
|
||||||
.At(token)
|
.At(token, _tokens)
|
||||||
.Build())
|
.Build())
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -698,7 +698,7 @@ public sealed class Parser
|
|||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Arbitrary uint size is not supported")
|
.Error("Arbitrary uint size is not supported")
|
||||||
.WithHelp("Use u8, u16, u32 or u64")
|
.WithHelp("Use u8, u16, u32 or u64")
|
||||||
.At(name)
|
.At(name, _tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,7 +712,7 @@ public sealed class Parser
|
|||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Arbitrary int size is not supported")
|
.Error("Arbitrary int size is not supported")
|
||||||
.WithHelp("Use i8, i16, i32 or i64")
|
.WithHelp("Use i8, i16, i32 or i64")
|
||||||
.At(name)
|
.At(name, _tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,7 +726,7 @@ public sealed class Parser
|
|||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Arbitrary float size is not supported")
|
.Error("Arbitrary float size is not supported")
|
||||||
.WithHelp("Use f32 or f64")
|
.WithHelp("Use f32 or f64")
|
||||||
.At(name)
|
.At(name, _tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -810,7 +810,7 @@ public sealed class Parser
|
|||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Invalid type syntax")
|
.Error("Invalid type syntax")
|
||||||
.WithHelp("Expected type name, '^' for pointer, or '[]' for array")
|
.WithHelp("Expected type name, '^' for pointer, or '[]' for array")
|
||||||
.At(CurrentToken)
|
.At(CurrentToken, _tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -821,7 +821,7 @@ public sealed class Parser
|
|||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Unexpected end of file")
|
.Error("Unexpected end of file")
|
||||||
.WithHelp("Expected more tokens to complete the syntax")
|
.WithHelp("Expected more tokens to complete the syntax")
|
||||||
.At(_tokens[^1])
|
.At(_tokens[^1], _tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -838,7 +838,7 @@ public sealed class Parser
|
|||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Expected symbol, but found {token.GetType().Name}")
|
.Error($"Expected symbol, but found {token.GetType().Name}")
|
||||||
.WithHelp("This position requires a symbol like '(', ')', '{', '}', etc.")
|
.WithHelp("This position requires a symbol like '(', ')', '{', '}', etc.")
|
||||||
.At(token)
|
.At(token, _tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -853,7 +853,7 @@ public sealed class Parser
|
|||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Expected '{expectedSymbol}', but found '{token.Symbol}'")
|
.Error($"Expected '{expectedSymbol}', but found '{token.Symbol}'")
|
||||||
.WithHelp($"Insert '{expectedSymbol}' here")
|
.WithHelp($"Insert '{expectedSymbol}' here")
|
||||||
.At(token)
|
.At(token, _tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -903,7 +903,7 @@ public sealed class Parser
|
|||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Expected identifier, but found {token.GetType().Name}")
|
.Error($"Expected identifier, but found {token.GetType().Name}")
|
||||||
.WithHelp("Provide a valid identifier name here")
|
.WithHelp("Provide a valid identifier name here")
|
||||||
.At(token)
|
.At(token, _tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -931,7 +931,7 @@ public sealed class Parser
|
|||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error($"Expected string literal, but found {token.GetType().Name}")
|
.Error($"Expected string literal, but found {token.GetType().Name}")
|
||||||
.WithHelp("Provide a valid string literal")
|
.WithHelp("Provide a valid string literal")
|
||||||
.At(token)
|
.At(token, _tokens)
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -953,4 +953,4 @@ public sealed class Parser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record SyntaxTree(List<TopLevelSyntaxNode> TopLevelSyntaxNodes);
|
public record SyntaxTree(List<TopLevelSyntaxNode> TopLevelSyntaxNodes, List<Token> Tokens);
|
||||||
@@ -43,6 +43,7 @@ public sealed class Tokenizer
|
|||||||
|
|
||||||
private Token ParseToken()
|
private Token ParseToken()
|
||||||
{
|
{
|
||||||
|
var indexStart = _index;
|
||||||
var lineStart = _line;
|
var lineStart = _line;
|
||||||
var columnStart = _column;
|
var columnStart = _column;
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ public sealed class Tokenizer
|
|||||||
Next();
|
Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WhitespaceToken(CreateSpan(lineStart, columnStart));
|
return new WhitespaceToken(CreateSpan(indexStart, lineStart, columnStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_content[_index] == '/' && _index + 1 < _content.Length && _content[_index + 1] == '/')
|
if (_content[_index] == '/' && _index + 1 < _content.Length && _content[_index + 1] == '/')
|
||||||
@@ -66,23 +67,23 @@ public sealed class Tokenizer
|
|||||||
Next();
|
Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CommentToken(CreateSpan(lineStart, columnStart), _content.AsSpan(startIndex, _index - startIndex).ToString());
|
return new CommentToken(CreateSpan(indexStart, lineStart, columnStart), _content.AsSpan(startIndex, _index - startIndex).ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (char.IsDigit(_content[_index]))
|
if (char.IsDigit(_content[_index]))
|
||||||
{
|
{
|
||||||
return ParseNumber(lineStart, columnStart);
|
return ParseNumber(indexStart, lineStart, columnStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_content[_index] == '"')
|
if (_content[_index] == '"')
|
||||||
{
|
{
|
||||||
return ParseString(lineStart, columnStart);
|
return ParseString(indexStart, lineStart, columnStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
// note(nub31): Look for keywords (longest first in case a keyword fits partially in a larger keyword)
|
// note(nub31): Look for keywords (longest first in case a keyword fits partially in a larger keyword)
|
||||||
for (var i = 8; i >= 1; i--)
|
for (var i = 8; i >= 1; i--)
|
||||||
{
|
{
|
||||||
if (TryMatchSymbol(i, lineStart, columnStart, out var token))
|
if (TryMatchSymbol(i, indexStart, lineStart, columnStart, out var token))
|
||||||
{
|
{
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@@ -90,13 +91,13 @@ public sealed class Tokenizer
|
|||||||
|
|
||||||
if (char.IsLetter(_content[_index]) || _content[_index] == '_')
|
if (char.IsLetter(_content[_index]) || _content[_index] == '_')
|
||||||
{
|
{
|
||||||
return ParseIdentifier(lineStart, columnStart);
|
return ParseIdentifier(indexStart, lineStart, columnStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new CompileException(Diagnostic.Error($"Unknown token '{_content[_index]}'").Build());
|
throw new CompileException(Diagnostic.Error($"Unknown token '{_content[_index]}'").Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Token ParseNumber(int lineStart, int columnStart)
|
private Token ParseNumber(int indexStart, int lineStart, int columnStart)
|
||||||
{
|
{
|
||||||
var start = _index;
|
var start = _index;
|
||||||
var current = _content[_index];
|
var current = _content[_index];
|
||||||
@@ -116,12 +117,12 @@ public sealed class Tokenizer
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Invalid hex literal, no digits found")
|
.Error("Invalid hex literal, no digits found")
|
||||||
.At(_fileName, _line, _column)
|
.At(CreateSpan(_index, _line, _column))
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IntLiteralToken(
|
return new IntLiteralToken(
|
||||||
CreateSpan(lineStart, columnStart),
|
CreateSpan(indexStart, lineStart, columnStart),
|
||||||
_content.Substring(start, _index - start),
|
_content.Substring(start, _index - start),
|
||||||
16);
|
16);
|
||||||
}
|
}
|
||||||
@@ -141,12 +142,12 @@ public sealed class Tokenizer
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Invalid binary literal, no digits found")
|
.Error("Invalid binary literal, no digits found")
|
||||||
.At(_fileName, _line, _column)
|
.At(CreateSpan(_index, _line, _column))
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IntLiteralToken(
|
return new IntLiteralToken(
|
||||||
CreateSpan(lineStart, columnStart),
|
CreateSpan(indexStart, lineStart, columnStart),
|
||||||
_content.Substring(start, _index - start),
|
_content.Substring(start, _index - start),
|
||||||
2);
|
2);
|
||||||
}
|
}
|
||||||
@@ -163,7 +164,7 @@ public sealed class Tokenizer
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("More than one period found in float literal")
|
.Error("More than one period found in float literal")
|
||||||
.At(_fileName, _line, _column)
|
.At(CreateSpan(_index, _line, _column))
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,11 +184,11 @@ public sealed class Tokenizer
|
|||||||
var buffer = _content.Substring(start, _index - start);
|
var buffer = _content.Substring(start, _index - start);
|
||||||
|
|
||||||
return isFloat
|
return isFloat
|
||||||
? new FloatLiteralToken(CreateSpan(lineStart, columnStart), buffer)
|
? new FloatLiteralToken(CreateSpan(indexStart, lineStart, columnStart), buffer)
|
||||||
: new IntLiteralToken(CreateSpan(lineStart, columnStart), buffer, 10);
|
: new IntLiteralToken(CreateSpan(indexStart, lineStart, columnStart), buffer, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
private StringLiteralToken ParseString(int lineStart, int columnStart)
|
private StringLiteralToken ParseString(int indexStart, int lineStart, int columnStart)
|
||||||
{
|
{
|
||||||
Next();
|
Next();
|
||||||
var start = _index;
|
var start = _index;
|
||||||
@@ -198,7 +199,7 @@ public sealed class Tokenizer
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Unclosed string literal")
|
.Error("Unclosed string literal")
|
||||||
.At(_fileName, _line, _column)
|
.At(CreateSpan(_index, _line, _column))
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +209,7 @@ public sealed class Tokenizer
|
|||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Unclosed string literal (newline found)")
|
.Error("Unclosed string literal (newline found)")
|
||||||
.At(_fileName, _line, _column)
|
.At(CreateSpan(_index, _line, _column))
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,14 +217,14 @@ public sealed class Tokenizer
|
|||||||
{
|
{
|
||||||
var buffer = _content.Substring(start, _index - start);
|
var buffer = _content.Substring(start, _index - start);
|
||||||
Next();
|
Next();
|
||||||
return new StringLiteralToken(CreateSpan(lineStart, columnStart), buffer);
|
return new StringLiteralToken(CreateSpan(indexStart, lineStart, columnStart), buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Next();
|
Next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryMatchSymbol(int length, int lineStart, int columnStart, out Token token)
|
private bool TryMatchSymbol(int length, int indexStart, int lineStart, int columnStart, out Token token)
|
||||||
{
|
{
|
||||||
token = null!;
|
token = null!;
|
||||||
|
|
||||||
@@ -237,14 +238,14 @@ public sealed class Tokenizer
|
|||||||
if (span is "true")
|
if (span is "true")
|
||||||
{
|
{
|
||||||
Next(4);
|
Next(4);
|
||||||
token = new BoolLiteralToken(CreateSpan(lineStart, columnStart), true);
|
token = new BoolLiteralToken(CreateSpan(indexStart, lineStart, columnStart), true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (span is "false")
|
if (span is "false")
|
||||||
{
|
{
|
||||||
Next(5);
|
Next(5);
|
||||||
token = new BoolLiteralToken(CreateSpan(lineStart, columnStart), false);
|
token = new BoolLiteralToken(CreateSpan(indexStart, lineStart, columnStart), false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,14 +350,14 @@ public sealed class Tokenizer
|
|||||||
}
|
}
|
||||||
|
|
||||||
Next(length);
|
Next(length);
|
||||||
token = new SymbolToken(CreateSpan(lineStart, columnStart), symbol);
|
token = new SymbolToken(CreateSpan(indexStart, lineStart, columnStart), symbol);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IdentifierToken ParseIdentifier(int lineStart, int columnStart)
|
private IdentifierToken ParseIdentifier(int indexStart, int lineStart, int columnStart)
|
||||||
{
|
{
|
||||||
var start = _index;
|
var start = _index;
|
||||||
|
|
||||||
@@ -373,12 +374,12 @@ public sealed class Tokenizer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IdentifierToken(CreateSpan(lineStart, columnStart), _content.Substring(start, _index - start));
|
return new IdentifierToken(CreateSpan(indexStart, lineStart, columnStart), _content.Substring(start, _index - start));
|
||||||
}
|
}
|
||||||
|
|
||||||
private SourceSpan CreateSpan(int lineStart, int columnStart)
|
private SourceSpan CreateSpan(int indexStart, int lineStart, int columnStart)
|
||||||
{
|
{
|
||||||
return new SourceSpan(_fileName, new SourceLocation(lineStart, columnStart), new SourceLocation(_line, _column));
|
return new SourceSpan(_fileName, _content, indexStart, Math.Min(_index, _content.Length), lineStart, columnStart, _line, _column);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Next(int count = 1)
|
private void Next(int count = 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user