Add literal conversion based on context at compile time
This commit is contained in:
@@ -1,5 +1,17 @@
|
||||
namespace main
|
||||
|
||||
struct Human {
|
||||
age: ^u64
|
||||
}
|
||||
|
||||
export func main(args: []^string) {
|
||||
sys::call(60, 0)
|
||||
}
|
||||
let x = [3]f64
|
||||
|
||||
x[0] = 1
|
||||
x[1.2] = 2
|
||||
x[2] = 3
|
||||
|
||||
c::printf("%d\n", x[0])
|
||||
c::printf("%d\n", x[1])
|
||||
c::printf("%d\n", x[2])
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -171,12 +171,6 @@ public class Lexer
|
||||
buffer += next;
|
||||
Next();
|
||||
}
|
||||
else if (next == 'f')
|
||||
{
|
||||
isFloat = true;
|
||||
Next();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
|
||||
@@ -20,37 +20,6 @@ public interface IFuncSignature
|
||||
public List<FuncParameter> Parameters { get; }
|
||||
public Optional<NubType> ReturnType { get; }
|
||||
|
||||
public bool SignatureMatches(string name, List<NubType> parameters)
|
||||
{
|
||||
if (Name != name) return false;
|
||||
if (Parameters.Count == 0 && parameters.Count == 0) return true;
|
||||
if (Parameters.Count > parameters.Count) return false;
|
||||
|
||||
for (var i = 0; i < parameters.Count; i++)
|
||||
{
|
||||
if (i >= Parameters.Count)
|
||||
{
|
||||
if (Parameters.Count > 0 && Parameters[^1].Variadic)
|
||||
{
|
||||
if (!NubType.IsCompatibleWith(parameters[i], Parameters[^1].Type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!NubType.IsCompatibleWith(parameters[i], Parameters[i].Type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using Nub.Lang.Frontend.Lexing;
|
||||
using Nub.Lang.Frontend.Typing;
|
||||
|
||||
namespace Nub.Lang.Frontend.Parsing.Expressions;
|
||||
|
||||
public class CastNode(IReadOnlyList<Token> tokens, NubType targetType, ExpressionNode expression) : ExpressionNode(tokens)
|
||||
{
|
||||
public NubType TargetType { get; } = targetType;
|
||||
public ExpressionNode Expression { get; } = expression;
|
||||
}
|
||||
// using Nub.Lang.Frontend.Lexing;
|
||||
// using Nub.Lang.Frontend.Typing;
|
||||
//
|
||||
// namespace Nub.Lang.Frontend.Parsing.Expressions;
|
||||
//
|
||||
// public class CastNode(IReadOnlyList<Token> tokens, NubType targetType, ExpressionNode expression) : ExpressionNode(tokens)
|
||||
// {
|
||||
// public NubType TargetType { get; } = targetType;
|
||||
// public ExpressionNode Expression { get; } = expression;
|
||||
// }
|
||||
@@ -8,5 +8,5 @@ public class FuncCallNode(IReadOnlyList<Token> tokens, string @namespace, string
|
||||
public string Name { get; } = name;
|
||||
public List<ExpressionNode> Parameters { get; } = parameters;
|
||||
|
||||
public override string ToString() => $"{Name}()";
|
||||
public override string ToString() => $"{Name}::{Name}()";
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Nub.Lang.Frontend.Lexing;
|
||||
using Nub.Lang.Frontend.Typing;
|
||||
|
||||
namespace Nub.Lang.Frontend.Parsing.Expressions;
|
||||
|
||||
|
||||
@@ -499,16 +499,16 @@ public class Parser
|
||||
expr = expression;
|
||||
break;
|
||||
}
|
||||
case Symbol.LessThan:
|
||||
{
|
||||
var type = ParseType();
|
||||
ExpectSymbol(Symbol.GreaterThan);
|
||||
ExpectSymbol(Symbol.OpenParen);
|
||||
var expressionToCast = ParseExpression();
|
||||
ExpectSymbol(Symbol.CloseParen);
|
||||
expr = new CastNode(GetTokensForNode(startIndex), type, expressionToCast);
|
||||
break;
|
||||
}
|
||||
// case Symbol.LessThan:
|
||||
// {
|
||||
// var type = ParseType();
|
||||
// ExpectSymbol(Symbol.GreaterThan);
|
||||
// ExpectSymbol(Symbol.OpenParen);
|
||||
// var expressionToCast = ParseExpression();
|
||||
// ExpectSymbol(Symbol.CloseParen);
|
||||
// expr = new CastNode(GetTokensForNode(startIndex), type, expressionToCast);
|
||||
// break;
|
||||
// }
|
||||
case Symbol.Ampersand:
|
||||
{
|
||||
var expression = ParsePrimaryExpression();
|
||||
|
||||
@@ -24,6 +24,30 @@ public abstract class NubType
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsInteger => this is NubPrimitiveType
|
||||
{
|
||||
Kind: PrimitiveTypeKind.I8
|
||||
or PrimitiveTypeKind.I16
|
||||
or PrimitiveTypeKind.I32
|
||||
or PrimitiveTypeKind.I64
|
||||
or PrimitiveTypeKind.U8
|
||||
or PrimitiveTypeKind.U16
|
||||
or PrimitiveTypeKind.U32
|
||||
or PrimitiveTypeKind.U64
|
||||
};
|
||||
|
||||
public bool IsFloat32 => this is NubPrimitiveType
|
||||
{
|
||||
Kind: PrimitiveTypeKind.F32
|
||||
};
|
||||
|
||||
public bool IsFloat64 => this is NubPrimitiveType
|
||||
{
|
||||
Kind: PrimitiveTypeKind.F64
|
||||
};
|
||||
|
||||
public bool IsNumber => IsFloat32 || IsFloat64 || IsInteger;
|
||||
|
||||
public abstract override bool Equals(object? obj);
|
||||
public abstract override int GetHashCode();
|
||||
public abstract override string ToString();
|
||||
|
||||
@@ -71,7 +71,7 @@ public class TypeChecker
|
||||
|
||||
if (field.Value.HasValue)
|
||||
{
|
||||
var fieldType = TypeCheckExpression(field.Value.Value);
|
||||
var fieldType = TypeCheckExpression(field.Value.Value, field.Type);
|
||||
if (fieldType != null && !fieldType.Equals(field.Type))
|
||||
{
|
||||
ReportError("Default field initializer does not match the defined type", field.Value.Value);
|
||||
@@ -153,7 +153,7 @@ public class TypeChecker
|
||||
{
|
||||
var memberType = TypeCheckExpression(memberAssignment.MemberAccess);
|
||||
if (memberType == null) return;
|
||||
var valueType = TypeCheckExpression(memberAssignment.Value);
|
||||
var valueType = TypeCheckExpression(memberAssignment.Value, memberType);
|
||||
if (valueType == null) return;
|
||||
|
||||
if (!NubType.IsCompatibleWith(memberType, valueType))
|
||||
@@ -166,7 +166,7 @@ public class TypeChecker
|
||||
{
|
||||
var itemType = TypeCheckExpression(arrayIndexAssignment.ArrayIndexAccess);
|
||||
if (itemType == null) return;
|
||||
var valueType = TypeCheckExpression(arrayIndexAssignment.Value);
|
||||
var valueType = TypeCheckExpression(arrayIndexAssignment.Value, itemType);
|
||||
if (valueType == null) return;
|
||||
|
||||
if (!NubType.IsCompatibleWith(itemType, valueType))
|
||||
@@ -177,19 +177,18 @@ public class TypeChecker
|
||||
|
||||
private void TypeCheckVariableAssignment(VariableAssignmentNode variableAssignment)
|
||||
{
|
||||
var valueType = TypeCheckExpression(variableAssignment.Value);
|
||||
if (valueType == null) return;
|
||||
|
||||
if (!_variables.TryGetValue(variableAssignment.Identifier.Identifier, out var existingVariable))
|
||||
if (!_variables.TryGetValue(variableAssignment.Identifier.Identifier, out var variable))
|
||||
{
|
||||
ReportError($"Variable '{variableAssignment.Identifier}' is not declared", variableAssignment);
|
||||
return;
|
||||
}
|
||||
|
||||
var valueType = TypeCheckExpression(variableAssignment.Value, variable);
|
||||
if (valueType == null) return;
|
||||
|
||||
if (!NubType.IsCompatibleWith(variableAssignment.Value.Type, existingVariable))
|
||||
if (!NubType.IsCompatibleWith(variableAssignment.Value.Type, variable))
|
||||
{
|
||||
ReportError($"Cannot assign expression of type '{variableAssignment.Value.Type}' to variable '{variableAssignment.Identifier}' with type '{existingVariable}'",
|
||||
variableAssignment);
|
||||
ReportError($"Cannot assign expression of type '{variableAssignment.Value.Type}' to variable '{variableAssignment.Identifier}' with type '{variable}'", variableAssignment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,14 +196,14 @@ public class TypeChecker
|
||||
{
|
||||
NubType? type = null;
|
||||
|
||||
if (_variables.TryGetValue(variableDeclaration.Name, out var existingVariable))
|
||||
if (_variables.TryGetValue(variableDeclaration.Name, out var variable))
|
||||
{
|
||||
ReportError($"Cannot redeclare variable '{existingVariable}'", variableDeclaration);
|
||||
ReportError($"Cannot redeclare variable '{variable}'", variableDeclaration);
|
||||
}
|
||||
|
||||
if (variableDeclaration.Value.HasValue)
|
||||
{
|
||||
var valueType = TypeCheckExpression(variableDeclaration.Value.Value);
|
||||
var valueType = TypeCheckExpression(variableDeclaration.Value.Value, variableDeclaration.ExplicitType.Value);
|
||||
if (valueType == null) return;
|
||||
type = valueType;
|
||||
}
|
||||
@@ -254,31 +253,21 @@ public class TypeChecker
|
||||
|
||||
private NubType? TypeCheckFuncCall(FuncCallNode funcCall, Node node)
|
||||
{
|
||||
List<NubType> parameterTypes = [];
|
||||
foreach (var funcCallParameter in funcCall.Parameters)
|
||||
{
|
||||
var parameterType = TypeCheckExpression(funcCallParameter);
|
||||
if (parameterType == null) return null;
|
||||
parameterTypes.Add(parameterType);
|
||||
}
|
||||
|
||||
var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name, parameterTypes);
|
||||
var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name);
|
||||
if (funcDefinition == null)
|
||||
{
|
||||
ReportError($"Function '{funcCall.Name}' is not defined", node);
|
||||
ReportError($"Function '{funcCall}' is not defined", node);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (funcDefinition.Parameters.Take(funcDefinition.Parameters.Count - 1).Any(x => x.Variadic))
|
||||
{
|
||||
ReportError($"Function '{funcCall.Name}' has multiple variadic parameters", node);
|
||||
ReportError($"Function '{funcCall}' has multiple variadic parameters", node);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
for (var i = 0; i < funcCall.Parameters.Count; i++)
|
||||
{
|
||||
var argType = funcCall.Parameters[i].Type;
|
||||
|
||||
NubType paramType;
|
||||
if (i < funcDefinition.Parameters.Count)
|
||||
{
|
||||
@@ -290,13 +279,16 @@ public class TypeChecker
|
||||
}
|
||||
else
|
||||
{
|
||||
ReportError($"Function '{funcCall.Name}' does not take {funcCall.Parameters.Count} parameters", node);
|
||||
ReportError($"Function '{funcCall}' does not take {funcCall.Parameters.Count} parameters", node);
|
||||
continue;
|
||||
}
|
||||
|
||||
var argType = TypeCheckExpression(funcCall.Parameters[i], paramType);
|
||||
if (argType == null) return null;
|
||||
|
||||
if (!NubType.IsCompatibleWith(argType, paramType))
|
||||
{
|
||||
ReportError($"Parameter {i + 1} of function '{funcCall.Name}' expects type '{paramType}', but got '{argType}'", funcCall.Parameters[i]);
|
||||
ReportError($"Parameter {i + 1} of function '{funcCall}' expects type '{paramType}', but got '{argType}'", funcCall.Parameters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +297,7 @@ public class TypeChecker
|
||||
|
||||
private void TypeCheckIf(IfNode ifNode)
|
||||
{
|
||||
var conditionType = TypeCheckExpression(ifNode.Condition);
|
||||
var conditionType = TypeCheckExpression(ifNode.Condition, NubPrimitiveType.Bool);
|
||||
if (conditionType != null && !conditionType.Equals(NubPrimitiveType.Bool))
|
||||
{
|
||||
ReportError($"If condition must be a boolean expression, got '{conditionType}'", ifNode.Condition);
|
||||
@@ -322,7 +314,7 @@ public class TypeChecker
|
||||
|
||||
private void TypeCheckWhile(WhileNode whileNode)
|
||||
{
|
||||
var conditionType = TypeCheckExpression(whileNode.Condition);
|
||||
var conditionType = TypeCheckExpression(whileNode.Condition, NubPrimitiveType.Bool);
|
||||
if (conditionType != null && !conditionType.Equals(NubPrimitiveType.Bool))
|
||||
{
|
||||
ReportError($"While condition must be a boolean expression, got '{conditionType}'", whileNode.Condition);
|
||||
@@ -337,7 +329,7 @@ public class TypeChecker
|
||||
|
||||
if (returnNode.Value.HasValue)
|
||||
{
|
||||
var returnType = TypeCheckExpression(returnNode.Value.Value);
|
||||
var returnType = TypeCheckExpression(returnNode.Value.Value, _currentFunctionReturnType);
|
||||
if (returnType == null) return;
|
||||
|
||||
if (_currentFunctionReturnType == null)
|
||||
@@ -361,7 +353,7 @@ public class TypeChecker
|
||||
{
|
||||
var dereferenceType = TypeCheckExpression(dereferenceAssignment.Dereference);
|
||||
if (dereferenceType == null) return;
|
||||
var valueType = TypeCheckExpression(dereferenceAssignment.Value);
|
||||
var valueType = TypeCheckExpression(dereferenceAssignment.Value, dereferenceType);
|
||||
if (valueType == null) return;
|
||||
|
||||
if (!NubType.IsCompatibleWith(dereferenceType, valueType))
|
||||
@@ -370,17 +362,17 @@ public class TypeChecker
|
||||
}
|
||||
}
|
||||
|
||||
private NubType? TypeCheckExpression(ExpressionNode expression)
|
||||
private NubType? TypeCheckExpression(ExpressionNode expression, NubType? expectedType = null)
|
||||
{
|
||||
var resultType = expression switch
|
||||
{
|
||||
AddressOfNode addressOf => TypeCheckAddressOf(addressOf),
|
||||
ArrayIndexAccessNode arrayIndex => TypeCheckArrayIndex(arrayIndex),
|
||||
ArrayInitializerNode arrayInitializer => TypeCheckArrayInitializer(arrayInitializer),
|
||||
LiteralNode literal => TypeCheckLiteral(literal),
|
||||
LiteralNode literal => TypeCheckLiteral(literal, expectedType),
|
||||
IdentifierNode identifier => TypeCheckIdentifier(identifier),
|
||||
BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr),
|
||||
CastNode cast => TypeCheckCast(cast),
|
||||
// CastNode cast => TypeCheckCast(cast),
|
||||
DereferenceNode dereference => TypeCheckDereference(dereference),
|
||||
FixedArrayInitializerNode fixedArray => TypeCheckFixedInitializerArray(fixedArray),
|
||||
FuncCallNode funcCallExpr => TypeCheckFuncCall(funcCallExpr, funcCallExpr),
|
||||
@@ -398,15 +390,34 @@ public class TypeChecker
|
||||
return resultType;
|
||||
}
|
||||
|
||||
private NubType TypeCheckLiteral(LiteralNode literal)
|
||||
private NubType? TypeCheckLiteral(LiteralNode literal, NubType? expectedType = null)
|
||||
{
|
||||
if (expectedType != null)
|
||||
{
|
||||
if (expectedType.IsNumber && literal.Kind is not LiteralKind.Integer and not LiteralKind.Float)
|
||||
{
|
||||
ReportError("Expression expects a numeric literal", literal);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (expectedType.IsInteger && literal.Kind == LiteralKind.Float)
|
||||
{
|
||||
if (literal.Kind == LiteralKind.Float)
|
||||
{
|
||||
ReportWarning("Possible loss of precision when using float in integer context", literal);
|
||||
}
|
||||
}
|
||||
|
||||
return expectedType;
|
||||
}
|
||||
|
||||
return literal.Kind switch
|
||||
{
|
||||
LiteralKind.Integer => NubPrimitiveType.I64,
|
||||
LiteralKind.Float => NubPrimitiveType.F64,
|
||||
LiteralKind.String => NubPrimitiveType.String,
|
||||
LiteralKind.Bool => NubPrimitiveType.Bool,
|
||||
_ => throw new UnreachableException()
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -414,10 +425,10 @@ public class TypeChecker
|
||||
{
|
||||
var expressionType = TypeCheckExpression(arrayIndexAccess.Array);
|
||||
if (expressionType == null) return null;
|
||||
var indexType = TypeCheckExpression(arrayIndexAccess.Index);
|
||||
if (indexType != null && !IsInteger(indexType))
|
||||
var indexType = TypeCheckExpression(arrayIndexAccess.Index, NubPrimitiveType.U64);
|
||||
if (indexType is { IsInteger: false })
|
||||
{
|
||||
ReportError("Array index type must be an integer", arrayIndexAccess.Index);
|
||||
ReportError("Array index type must be a number", arrayIndexAccess.Index);
|
||||
}
|
||||
|
||||
if (expressionType is NubArrayType arrayType)
|
||||
@@ -436,8 +447,8 @@ public class TypeChecker
|
||||
|
||||
private NubType TypeCheckArrayInitializer(ArrayInitializerNode arrayInitializer)
|
||||
{
|
||||
var capacityType = TypeCheckExpression(arrayInitializer.Capacity);
|
||||
if (capacityType != null && !IsInteger(capacityType))
|
||||
var capacityType = TypeCheckExpression(arrayInitializer.Capacity, NubPrimitiveType.U64);
|
||||
if (capacityType is { IsInteger: false })
|
||||
{
|
||||
ReportError("Array capacity type must be an integer", arrayInitializer.Capacity);
|
||||
}
|
||||
@@ -516,12 +527,12 @@ public class TypeChecker
|
||||
}
|
||||
}
|
||||
|
||||
private NubType? TypeCheckCast(CastNode cast)
|
||||
{
|
||||
TypeCheckExpression(cast.Expression);
|
||||
// TODO: Check if castable
|
||||
return cast.TargetType;
|
||||
}
|
||||
// private NubType? TypeCheckCast(CastNode cast)
|
||||
// {
|
||||
// TypeCheckExpression(cast.Expression, cast.TargetType);
|
||||
// // TODO: Check if castable
|
||||
// return cast.TargetType;
|
||||
// }
|
||||
|
||||
private NubType? TypeCheckStructInitializer(StructInitializerNode structInit)
|
||||
{
|
||||
@@ -543,7 +554,7 @@ public class TypeChecker
|
||||
continue;
|
||||
}
|
||||
|
||||
var initializerType = TypeCheckExpression(initializer.Value);
|
||||
var initializerType = TypeCheckExpression(initializer.Value, definitionField.Type);
|
||||
if (initializerType != null && !NubType.IsCompatibleWith(initializerType, definitionField.Type))
|
||||
{
|
||||
ReportError($"Cannot initialize field '{initializer.Key}' of type '{definitionField.Type}' with expression of type '{initializerType}'", initializer.Value);
|
||||
@@ -654,6 +665,12 @@ public class TypeChecker
|
||||
_diagnostics.Add(diagnostic);
|
||||
}
|
||||
|
||||
private void ReportWarning(string message, Node node)
|
||||
{
|
||||
var diagnostic = Diagnostic.Warning(message).At(node).Build();
|
||||
_diagnostics.Add(diagnostic);
|
||||
}
|
||||
|
||||
private static bool IsNumeric(NubType type)
|
||||
{
|
||||
if (type is not NubPrimitiveType primitiveType)
|
||||
@@ -679,36 +696,13 @@ public class TypeChecker
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsInteger(NubType type)
|
||||
{
|
||||
if (type is not NubPrimitiveType primitiveType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (primitiveType.Kind)
|
||||
{
|
||||
case PrimitiveTypeKind.I8:
|
||||
case PrimitiveTypeKind.I16:
|
||||
case PrimitiveTypeKind.I32:
|
||||
case PrimitiveTypeKind.I64:
|
||||
case PrimitiveTypeKind.U8:
|
||||
case PrimitiveTypeKind.U16:
|
||||
case PrimitiveTypeKind.U32:
|
||||
case PrimitiveTypeKind.U64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private IFuncSignature? LookupFuncSignature(string @namespace, string name, List<NubType> parameters)
|
||||
private IFuncSignature? LookupFuncSignature(string @namespace, string name)
|
||||
{
|
||||
return _sourceFiles
|
||||
.Where(f => f.Namespace == @namespace)
|
||||
.SelectMany(f => f.Definitions)
|
||||
.OfType<IFuncSignature>()
|
||||
.FirstOrDefault(f => f.SignatureMatches(name, parameters));
|
||||
.FirstOrDefault(f => f.Name == name);
|
||||
}
|
||||
|
||||
private StructDefinitionNode? LookupStructDefinition(string @namespace, string name)
|
||||
@@ -717,6 +711,6 @@ public class TypeChecker
|
||||
.Where(f => f.Namespace == @namespace)
|
||||
.SelectMany(f => f.Definitions)
|
||||
.OfType<StructDefinitionNode>()
|
||||
.FirstOrDefault(d => d.Name == name);
|
||||
.SingleOrDefault(d => d.Name == name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user