Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Crater/Crater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ public static int Main(string[] args)
var tokenStream = new CommonTokenStream(craterLexer);
var craterParser = new CraterParser(tokenStream);

var syntaxTreeConverter = new SyntaxTreeConverter();
var reporter = new DiagnosticBag();

var syntaxTreeConverter = new SyntaxTreeConverter(reporter);
var node = syntaxTreeConverter.Visit(craterParser.program());

if (node is not Program program)
throw new Exception("Failed to convert resulting tree.");

var reporter = new DiagnosticBag();

var semanticAnalyzer = new SemanticAnalyzer(reporter);
semanticAnalyzer.AnalyzeProgram(program);

Expand Down
2 changes: 2 additions & 0 deletions Crater/CraterLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ NOT_EQUAL : '~=';
LESSER_EQUAL : '<=';
GREATER_EQUAL : '>=';

VARARGS : '...';

// Single-Character
PLUS : '+';
MINUS : '-';
Expand Down
5 changes: 4 additions & 1 deletion Crater/CraterParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ functionDeclaration: LOCAL? FUNCTION name=IDENTIFIER LPAREN parameters? RPAREN C

parameters: parameter (COMMA parameter)*;

parameter: name=IDENTIFIER COLON typeName;
parameter
: name=IDENTIFIER COLON typeName # NamedParameter
| VARARGS COLON typeName # VarargParameter
;

returnTypes: VOID | typeName (COMMA typeName)*;

Expand Down
15 changes: 14 additions & 1 deletion Crater/SemanticAnalysis/SemanticAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,15 @@ private void AnalyzeFunctionDeclaration(FunctionDeclaration functionDeclaration)
foreach (var parameter in functionDeclaration.parameters)
parameterTypes.Add(AnalyzeTypeName(parameter.type));

Type? varargType = null;
if (functionDeclaration.varargParameter is not null)
varargType = AnalyzeTypeName(functionDeclaration.varargParameter.type);

var returnTypes = new List<Type>();
foreach (var returnType in functionDeclaration.returnTypes)
returnTypes.Add(AnalyzeTypeName(returnType));

env.Define(functionDeclaration.name, new FunctionType(parameterTypes, returnTypes, TypeRegistry.FunctionType));
env.Define(functionDeclaration.name, new FunctionType(parameterTypes, varargType, returnTypes, TypeRegistry.FunctionType));

EnterScope();

Expand Down Expand Up @@ -430,6 +434,15 @@ private List<Type> AnalyzeFunctionCall(FunctionCall functionCall)
}
}

if (functionType.VarargType == null)
return functionType.ReturnTypes.ToList();

for (var i = functionType.ParameterTypes.Count; i < argumentTypes.Count; i++)
{
if (!functionType.VarargType.CanHold(argumentTypes[i].Item1))
_reporter.Report(new Diagnostic(TypeErrors.TypeMismatch, $"Cannot pass value of type '{argumentTypes[i].Item1}' to vararg of type '{functionType.VarargType}'", DiagnosticSeverity.Error, argumentTypes[i].Item2));
}

return functionType.ReturnTypes.ToList();
}

Expand Down
2 changes: 1 addition & 1 deletion Crater/SemanticAnalysis/TypeRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public static class TypeRegistry
public static readonly Type StringType = new StringType(AnyType);
public static readonly Type BooleanType = new BooleanType(AnyType);

public static readonly Type FunctionType = new FunctionType([], [], AnyType);
public static readonly Type FunctionType = new FunctionType([], null, [], AnyType);

public static readonly Type NilType = new NilType();
public static readonly Type UnknownType = new UnknownType();
Expand Down
4 changes: 3 additions & 1 deletion Crater/SemanticAnalysis/Types/FunctionType.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
namespace Crater.SemanticAnalysis.Types;

public class FunctionType(IReadOnlyList<Type> parameterTypes, IReadOnlyList<Type> returnTypes, Type baseType) : Type(BuildSignature(parameterTypes, returnTypes), baseType)
public class FunctionType(IReadOnlyList<Type> parameterTypes, Type? varargType, IReadOnlyList<Type> returnTypes, Type baseType) : Type(BuildSignature(parameterTypes, returnTypes), baseType)
{
public readonly IReadOnlyList<Type> ParameterTypes = parameterTypes;
public readonly IReadOnlyList<Type> ReturnTypes = returnTypes;

public readonly Type? VarargType = varargType;

public override bool CanHold(Type other)
{
if (BaseType == TypeRegistry.AnyType && other is FunctionType)
Expand Down
6 changes: 4 additions & 2 deletions Crater/SyntaxTree/Nodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ public sealed record VariableDeclaration(bool local, List<VariableDeclarator> de

public sealed record VariableDeclarator(string name, TypeName type, Source source) : Node(source);

public sealed record FunctionDeclaration(bool local, string name, List<Parameter> parameters, List<TypeName> returnTypes, Block block, Source source) : Node(source);
public sealed record FunctionDeclaration(bool local, string name, List<Parameter> parameters, VarargParameter? varargParameter, List<TypeName> returnTypes, Block block, Source source) : Node(source);

public sealed record Parameter(string name, TypeName type, Source source) : Node(source);
public record Parameter(string name, TypeName type, Source source) : Node(source);

public sealed record VarargParameter(TypeName type, Source source) : Parameter("...", type, source);

public sealed record DoStatement(Block block, Source source) : Node(source);

Expand Down
29 changes: 26 additions & 3 deletions Crater/SyntaxTree/SyntaxTreeConverter.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using Antlr4.Runtime;
using Crater.Antlr;
using Crater.Diagnostics;

namespace Crater.SyntaxTree;

public class SyntaxTreeConverter : CraterParserBaseVisitor<object>
public class SyntaxTreeConverter(IDiagnosticReporter reporter) : CraterParserBaseVisitor<object>
{
private readonly IDiagnosticReporter _reporter = reporter;

private T Get<T>(ParserRuleContext context)
{
var node = Visit(context);
Expand Down Expand Up @@ -66,10 +69,24 @@ public override object VisitFunctionDeclaration(CraterParser.FunctionDeclaration
else
parameters = [];

VarargParameter? varargParameter = null;
for (var i = 0; i < parameters.Count; i++)
{
if (parameters[i] is not VarargParameter varargParam)
continue;

if (i != parameters.Count - 1)
_reporter.Report(new Diagnostic("0", $"Vararg parameter must be the last parameter in a function declaration", DiagnosticSeverity.Error, varargParam.source));

varargParameter = varargParam;
}

parameters.RemoveAll(parameter => parameter is VarargParameter);

var returnTypes = Get<List<TypeName>>(context.returnTypes());
var block = Get<Block>(context.block());

return new FunctionDeclaration(local, name, parameters, returnTypes, block, Source.FromContext(context));
return new FunctionDeclaration(local, name, parameters, varargParameter, returnTypes, block, Source.FromContext(context));
}

public override object VisitParameters(CraterParser.ParametersContext context)
Expand All @@ -81,14 +98,20 @@ public override object VisitParameters(CraterParser.ParametersContext context)
return parameters;
}

public override object VisitParameter(CraterParser.ParameterContext context)
public override object VisitNamedParameter(CraterParser.NamedParameterContext context)
{
var name = context.name.Text;
var type = Get<TypeName>(context.typeName());

return new Parameter(name, type, Source.FromContext(context));
}

public override object VisitVarargParameter(CraterParser.VarargParameterContext context)
{
var type = Get<TypeName>(context.typeName());
return new VarargParameter(type, Source.FromContext(context));
}

public override object VisitReturnTypes(CraterParser.ReturnTypesContext context)
{
var returnTypes = new List<TypeName>();
Expand Down
8 changes: 4 additions & 4 deletions Tests/TypeSystemTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void NonNullablesCannotHoldNullables()
[Test]
public void BaseFunctionCanHoldOtherFunctions()
{
var complexFunction = new FunctionType([TypeRegistry.AnyType], [], TypeRegistry.FunctionType);
var complexFunction = new FunctionType([TypeRegistry.AnyType], null, [], TypeRegistry.FunctionType);

Assert.Multiple(() =>
{
Expand All @@ -80,15 +80,15 @@ public void BaseFunctionCanHoldOtherFunctions()
[Test]
public void ComplexFunctionsCannotHoldBaseFunctions()
{
var complexFunction = new FunctionType([TypeRegistry.AnyType], [], TypeRegistry.FunctionType);
var complexFunction = new FunctionType([TypeRegistry.AnyType], null, [], TypeRegistry.FunctionType);
Assert.That(!complexFunction.CanHold(TypeRegistry.FunctionType));
}

[Test]
public void ComplexFunctionsCanHoldSameSignature()
{
var complexFunctionA = new FunctionType([TypeRegistry.NumberType], [TypeRegistry.StringType], TypeRegistry.FunctionType);
var complexFunctionB = new FunctionType([TypeRegistry.NumberType], [TypeRegistry.StringType], TypeRegistry.FunctionType);
var complexFunctionA = new FunctionType([TypeRegistry.NumberType], null, [TypeRegistry.StringType], TypeRegistry.FunctionType);
var complexFunctionB = new FunctionType([TypeRegistry.NumberType], null, [TypeRegistry.StringType], TypeRegistry.FunctionType);

Assert.That(complexFunctionA.CanHold(complexFunctionB));
}
Expand Down
Loading