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
41 changes: 41 additions & 0 deletions Crater/Compilation/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,9 @@ private void CompileExpression(Expression expression)
case BooleanLiteral booleanLiteral:
CompileBooleanLiteral(booleanLiteral);
break;
case TableLiteral tableLiteral:
CompileTableLiteral(tableLiteral);
break;
case ArrayLiteral arrayLiteral:
CompileArrayLiteral(arrayLiteral);
break;
Expand All @@ -336,6 +339,12 @@ private void CompileExpression(Expression expression)
case FunctionCall functionCall:
CompileFunctionCall(functionCall);
break;
case BracketIndexing bracketIndexing:
CompileBracketIndexing(bracketIndexing);
break;
case DotIndexing dotIndexing:
CompileDotIndexing(dotIndexing);
break;
default:
throw new SwitchExpressionException(expression);
}
Expand Down Expand Up @@ -369,6 +378,24 @@ private void CompileBooleanLiteral(BooleanLiteral booleanLiteral)
_writer.Write(booleanLiteral.value);
}

private void CompileTableLiteral(TableLiteral tableLiteral)
{
_writer.Write("{");

for (var i = 0; i < tableLiteral.values.Count; i++)
{
var value = tableLiteral.values[i];

_writer.Write($"{value.index} = ");
CompileExpression(value.value);

if (i < tableLiteral.values.Count - 1)
_writer.Write(", ");
}

_writer.Write("}");
}

private void CompileArrayLiteral(ArrayLiteral arrayLiteral)
{
_writer.Write("{");
Expand Down Expand Up @@ -416,4 +443,18 @@ private void CompileFunctionCall(FunctionCall functionCall)

_writer.Write(")");
}

private void CompileBracketIndexing(BracketIndexing bracketIndexing)
{
CompileExpression(bracketIndexing.prefix);
_writer.Write("[");
CompileExpression(bracketIndexing.index);
_writer.Write("]");
}

private void CompileDotIndexing(DotIndexing dotIndexing)
{
CompileExpression(dotIndexing.prefix);
_writer.Write($".{dotIndexing.key}");
}
}
1 change: 1 addition & 0 deletions Crater/CraterLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ SLASH : '/';
LESSER : '<';
GREATER : '>';

DOT : '.';
COMMA : ',';
COLON : ':';
ASSIGN : '=';
Expand Down
15 changes: 14 additions & 1 deletion Crater/CraterParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ breakStatement: BREAK;

typeName: typeName (QMARK | LSQRBRACKET RSQRBRACKET) | primaryType;

primaryType: IDENTIFIER;
primaryType
: IDENTIFIER # NamedType
| tableDefinition # TableType
;

tableDefinition: LBRACKET (variableDeclarator (COMMA variableDeclarator)*)? RBRACKET;

expressionList: expression (COMMA expression)*;

Expand All @@ -75,6 +80,7 @@ expression
| left=expression logicalOperator right=expression # LogicalOperation
| left=expression AND right=expression # AndOperation
| left=expression OR right=expression # OrOperation
| LBRACKET tableValues? RBRACKET # TableLiteral
| LBRACKET expressionList? RBRACKET # ArrayLiteral
| NUMBER # NumberLiteral
| STRING # StringLiteral
Expand All @@ -91,12 +97,19 @@ prefixExpression
postfixExpression
: postfixFunctionCall
| postfixBracketIndexing
| postfixDotIndexing
;

postfixFunctionCall: LPAREN expressionList? RPAREN;

postfixBracketIndexing: LSQRBRACKET expression RSQRBRACKET;

postfixDotIndexing: DOT IDENTIFIER;

tableValues: tableValue (COMMA tableValue)* COMMA?;

tableValue: IDENTIFIER ASSIGN expression;

logicalOperator
: EQUAL
| NOT_EQUAL
Expand Down
101 changes: 88 additions & 13 deletions Crater/SemanticAnalysis/SemanticAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ private void AnalyzeVariableDeclaration(VariableDeclaration variableDeclaration)
{
if (initializer.Item1 is NilType)
_reporter.Report(new Diagnostic(TypeErrors.NilAssignment, $"Cannot assign nil to '{name}' as it is declared with the non-nullable type '{type}'", DiagnosticSeverity.Error, variableDeclaration.source));
else if (type is TableType && initializer.Item1 is TableType)
_reporter.Report(new Diagnostic(TypeErrors.TypeMismatch, $"The contents of the assigned table does not match the required declaration", DiagnosticSeverity.Error, initializer.Item2));
else
_reporter.Report(new Diagnostic(TypeErrors.TypeMismatch, $"The value assigned to '{name}' of type '{initializer.Item1}' is incompatible with the declared type of '{type}'", DiagnosticSeverity.Error, variableDeclaration.source));
}
Expand Down Expand Up @@ -255,7 +257,10 @@ private void AnalyzeAssignment(Assignment assignment)
}
else if (!variableType.CanHold(valueType.Item1))
{
_reporter.Report(new Diagnostic(TypeErrors.TypeMismatch, $"Cannot assign value of type '{valueType.Item1}' to variable of type '{variableType}'", DiagnosticSeverity.Error, valueType.Item2));
if (variableType is TableType && valueType.Item1 is TableType)
_reporter.Report(new Diagnostic(TypeErrors.TypeMismatch, $"The contents of the assigned table do not match the declared type of '{variable}'", DiagnosticSeverity.Error, valueType.Item2));
else
_reporter.Report(new Diagnostic(TypeErrors.TypeMismatch, $"Cannot assign value of type '{valueType.Item1}' to variable of type '{variableType}'", DiagnosticSeverity.Error, valueType.Item2));
}
}
}
Expand Down Expand Up @@ -386,6 +391,7 @@ private Type AnalyzeTypeName(TypeName typeName)
NullableTypeName nullableTypeName => AnalyzeNullableTypeName(nullableTypeName),
ArrayTypeName arrayTypeName => AnalyzeArrayTypeName(arrayTypeName),
NamedTypeName namedTypeName => AnalyzeNamedTypeName(namedTypeName),
TableTypeName tableTypeName => AnalyzeTableTypeName(tableTypeName),
_ => throw new SwitchExpressionException(typeName)
};
}
Expand All @@ -412,6 +418,24 @@ private Type AnalyzeNamedTypeName(NamedTypeName namedTypeName)
return TypeRegistry.UnknownType;
}

private Type AnalyzeTableTypeName(TableTypeName tableTypeName)
{
var fields = new Dictionary<string, Type>();
foreach (var field in tableTypeName.fields)
{
var type = AnalyzeTypeName(field.type);

if (fields.TryAdd(field.name, type))
continue;

// TODO: Error code for duplicate field
_reporter.Report(new Diagnostic("0", $"Duplicate field '{field.name}' in table type definition", DiagnosticSeverity.Error, field.source));
fields[field.name] = TypeRegistry.UnknownType;
}

return new TableType(fields, TypeRegistry.AnyType);
}

private List<Type> AnalyzeExpression(Expression expression)
{
return expression switch
Expand All @@ -421,12 +445,14 @@ private List<Type> AnalyzeExpression(Expression expression)
NumberLiteral numberLiteral => AnalyzeNumberLiteral(numberLiteral),
StringLiteral stringLiteral => AnalyzeStringLiteral(stringLiteral),
BooleanLiteral booleanLiteral => AnalyzeBooleanLiteral(booleanLiteral),
TableLiteral tableLiteral => AnalyzeTableLiteral(tableLiteral),
ArrayLiteral arrayLiteral => AnalyzeArrayLiteral(arrayLiteral),
NilLiteral nilLiteral => AnalyzeNilLiteral(nilLiteral),
ParenthesizedExpression parenthesizedExpression => AnalyzeParenthesizedExpression(parenthesizedExpression),
VariableReference variableReference => AnalyzeVariableReference(variableReference),
FunctionCall functionCall => AnalyzeFunctionCall(functionCall),
BracketIndexing bracketIndexing => AnalyzeBracketIndexing(bracketIndexing),
DotIndexing dotIndexing => AnalyzeDotIndexing(dotIndexing),
_ => throw new SwitchExpressionException(expression)
};
}
Expand Down Expand Up @@ -497,29 +523,66 @@ private List<Type> AnalyzeBooleanLiteral(BooleanLiteral booleanLiteral)
return [TypeRegistry.BooleanType];
}

private List<Type> AnalyzeTableLiteral(TableLiteral tableLiteral)
{
var fields = new Dictionary<string, Type>();
foreach (var value in tableLiteral.values)
{
var name = value.index;
var type = AnalyzeExpression(value.value).FirstOrDefault();

if (type == null)
{
_reporter.Report(new Diagnostic(TypeErrors.FailedTypeResolution, "Could not determine type of value", DiagnosticSeverity.Error, value.value.source));
continue;
}

if (fields.TryAdd(name, type))
continue;

// TODO: Error code for duplicate fields in table
_reporter.Report(new Diagnostic("0", $"Duplicate field '{name}' in table literal", DiagnosticSeverity.Error, value.source));
}

return [new TableType(fields, TypeRegistry.AnyType)];
}

private List<Type> AnalyzeArrayLiteral(ArrayLiteral arrayLiteral)
{
Type? common = null;
if (arrayLiteral.values.Count == 0)
return [new EmptyArrayType()];

var values = ExpandExpressionList(arrayLiteral.values);
foreach (var value in values)
Type? common = null;
foreach (var value in arrayLiteral.values)
{
if (common == null)
common = value.Item1;
else
common = Type.GetCommonType(common, value.Item1);
var type = AnalyzeExpression(value).FirstOrDefault() ?? TypeRegistry.NilType;
common = common == null ? type : Type.GetCommonType(common, type);

if (common == null)
{
_reporter.Report(new Diagnostic(TypeErrors.FailedTypeInference, $"Could not find a common type for array initializer", DiagnosticSeverity.Error, arrayLiteral.source));
break;
_reporter.Report(new Diagnostic(TypeErrors.FailedTypeInference, "Could not find a common type for array literal", DiagnosticSeverity.Error, arrayLiteral.source));
return [TypeRegistry.UnknownType];
}
}

if (common == null)
return [new EmptyArrayType()];
return [new ArrayType(common!, TypeRegistry.AnyType)];
}

return [new ArrayType(common, TypeRegistry.AnyType)];
private List<Type> AnalyzeArrayValues(List<Expression> values, Source source)
{
Type? common = null;
foreach (var (type, valueSource) in ExpandExpressionList(values))
{
common = common == null ? type : Type.GetCommonType(common, type);

if (common == null)
{
_reporter.Report(new Diagnostic(TypeErrors.FailedTypeInference, "Could not find a common type for array initializer", DiagnosticSeverity.Error, source));
return [TypeRegistry.UnknownType];
}
}

return [new ArrayType(common!, TypeRegistry.AnyType)];
}

private List<Type> AnalyzeNilLiteral(NilLiteral nilLiteral)
Expand Down Expand Up @@ -597,6 +660,18 @@ private List<Type> AnalyzeBracketIndexing(BracketIndexing bracketIndexing)
return [TypeRegistry.UnknownType];
}

private List<Type> AnalyzeDotIndexing(DotIndexing dotIndexing)
{
var prefixType = AnalyzeExpression(dotIndexing.prefix).FirstOrDefault() ?? TypeRegistry.NilType;
var resultType = prefixType.ResolveMemberAccess(dotIndexing.key);
if (resultType != null)
return [resultType];

// TODO: Error code for member access not supported
_reporter.Report(new Diagnostic("0", $"Cannot perform member access on '{prefixType}'", DiagnosticSeverity.Error, dotIndexing.source));
return [TypeRegistry.UnknownType];
}

private bool IsTernaryPattern(BinaryOperation binaryOperation, [NotNullWhen(true)] out Expression? valueA, [NotNullWhen(true)] out Expression? valueB)
{
valueA = null;
Expand Down
5 changes: 5 additions & 0 deletions Crater/SemanticAnalysis/Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,10 @@ public virtual bool IsSameType(Type other)
return null;
}

public virtual Type? ResolveMemberAccess(string key)
{
return null;
}

public override string ToString() => GetName();
}
39 changes: 39 additions & 0 deletions Crater/SemanticAnalysis/Types/TableType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace Crater.SemanticAnalysis.Types;

public class TableType(IReadOnlyDictionary<string, Type> fields, Type baseType) : Type("table", baseType)
{
public readonly IReadOnlyDictionary<string, Type> Fields = fields;

public override bool CanHold(Type other)
{
if (other is not TableType)
return false;

return IsSameType(other);
}

public override bool IsSameType(Type other)
{
if (other is not TableType otherTable)
return false;

if (Fields.Count != otherTable.Fields.Count)
return false;

foreach (var (key, type) in Fields)
{
if (!otherTable.Fields.TryGetValue(key, out var fieldType))
return false;

if (!type.IsSameType(fieldType))
return false;
}

return true;
}

public override Type? ResolveMemberAccess(string key)
{
return Fields.GetValueOrDefault(key);
}
}
8 changes: 8 additions & 0 deletions Crater/SyntaxTree/Nodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public sealed record NullableTypeName(TypeName baseTypeName, Source source) : Ty

public sealed record ArrayTypeName(TypeName baseTypeName, Source source) : TypeName(source);

public sealed record TableTypeName(List<VariableDeclarator> fields, Source source) : TypeName(source);

public abstract record Expression(Source source) : Node(source);

public sealed record ParenthesizedExpression(Expression innerExpression, Source source) : Expression(source);
Expand All @@ -56,6 +58,8 @@ public sealed record FunctionCall(Expression function, List<Expression> argument

public sealed record BracketIndexing(Expression prefix, Expression index, Source source) : Expression(source);

public sealed record DotIndexing(Expression prefix, string key, Source source) : Expression(source);

public sealed record UnaryOperation(string op, Expression expression, Source source) : Expression(source);

public sealed record BinaryOperation(Expression left, string op, Expression right, Source source) : Expression(source);
Expand All @@ -66,6 +70,10 @@ public sealed record StringLiteral(string value, Source source) : Expression(sou

public sealed record BooleanLiteral(string value, Source source) : Expression(source);

public sealed record TableLiteral(List<TableValue> values, Source source) : Expression(source);

public sealed record ArrayLiteral(List<Expression> values, Source source) : Expression(source);

public sealed record TableValue(string index, Expression value, Source source) : Node(source);

public sealed record NilLiteral(string value, Source source) : Expression(source);
Loading
Loading