From 87155de49d98e5b05ecdb200d0a01d3bf7abbffa Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 28 Sep 2025 22:48:32 +0200 Subject: [PATCH] Add float to int builtin --- .../NubLang/Generation/QBE/QBEGenerator.cs | 28 +++++++++- compiler/NubLang/Parsing/Parser.cs | 18 +++++-- .../Parsing/Syntax/ExpressionSyntax.cs | 6 ++- .../TypeChecking/Node/ExpressionNode.cs | 4 +- compiler/NubLang/TypeChecking/TypeChecker.cs | 51 ++++++++++++++++--- example/src/main.nub | 19 +++++++ 6 files changed, 111 insertions(+), 15 deletions(-) diff --git a/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/compiler/NubLang/Generation/QBE/QBEGenerator.cs index 66ce678..5426ceb 100644 --- a/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -645,7 +645,8 @@ public class QBEGenerator StructFuncCallNode expr => EmitStructFuncCall(expr), StructInitializerNode expr => EmitStructInitializer(expr), UnaryExpressionNode expr => EmitUnaryExpression(expr), - SizeCompilerMacroNode expr => $"{SizeOf(expr.TargetType)}", + SizeBuiltinNode expr => $"{SizeOf(expr.TargetType)}", + FloatToIntBuiltinNode expr => EmitFloatToIntBuiltin(expr), _ => throw new ArgumentOutOfRangeException(nameof(rValue)) }; } @@ -1106,6 +1107,31 @@ public class QBEGenerator return result; } + private string EmitFloatToIntBuiltin(FloatToIntBuiltinNode floatToInt) + { + var value = EmitExpression(floatToInt.Value); + + var method = floatToInt.TargetType.Signed switch + { + true => floatToInt.ValueType.Width switch + { + 32 => "stosi", + 64 => "dtosi", + _ => throw new ArgumentOutOfRangeException() + }, + false => floatToInt.ValueType.Width switch + { + 32 => "stoui", + 64 => "dtoui", + _ => throw new ArgumentOutOfRangeException() + } + }; + + var result = TmpName(); + _writer.Indented($"{result} {QBEAssign(floatToInt.TargetType)} {method} {value}"); + return result; + } + private string EmitFuncCall(FuncCallNode funcCall) { var funcPointer = EmitExpression(funcCall.Expression); diff --git a/compiler/NubLang/Parsing/Parser.cs b/compiler/NubLang/Parsing/Parser.cs index 8d1dace..c21b21f 100644 --- a/compiler/NubLang/Parsing/Parser.cs +++ b/compiler/NubLang/Parsing/Parser.cs @@ -461,7 +461,7 @@ public sealed class Parser Symbol.OpenBracket => ParseArrayInitializer(startIndex), Symbol.OpenBrace => new StructInitializerSyntax(GetTokens(startIndex), null, ParseStructInitializerBody()), Symbol.Struct => ParseStructInitializer(startIndex), - Symbol.At => ParseCompilerMacro(startIndex), + Symbol.At => ParseBuiltinFunction(startIndex), _ => throw new ParseException(Diagnostic .Error($"Unexpected symbol '{symbolToken.Symbol}' in expression") .WithHelp("Expected '(', '-', '!', '[' or '{'") @@ -478,7 +478,7 @@ public sealed class Parser return ParsePostfixOperators(expr); } - private ExpressionSyntax ParseCompilerMacro(int startIndex) + private ExpressionSyntax ParseBuiltinFunction(int startIndex) { var name = ExpectIdentifier(); ExpectSymbol(Symbol.OpenParen); @@ -489,7 +489,7 @@ public sealed class Parser { var type = ParseType(); ExpectSymbol(Symbol.CloseParen); - return new SizeCompilerMacroSyntax(GetTokens(startIndex), type); + return new SizeBuiltinSyntax(GetTokens(startIndex), type); } case "interpret": { @@ -497,11 +497,19 @@ public sealed class Parser ExpectSymbol(Symbol.Comma); var expression = ParseExpression(); ExpectSymbol(Symbol.CloseParen); - return new InterpretCompilerMacroSyntax(GetTokens(startIndex), type, expression); + return new InterpretBuiltinSyntax(GetTokens(startIndex), type, expression); + } + case "floatToInt": + { + var type = ParseType(); + ExpectSymbol(Symbol.Comma); + var expression = ParseExpression(); + ExpectSymbol(Symbol.CloseParen); + return new FloatToIntBuiltinSyntax(GetTokens(startIndex), type, expression); } default: { - throw new ParseException(Diagnostic.Error("Unknown compiler macro").At(name).Build()); + throw new ParseException(Diagnostic.Error($"Unknown builtin {name.Value}").At(name).Build()); } } } diff --git a/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs b/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs index 38860ec..cb504c6 100644 --- a/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs +++ b/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs @@ -58,6 +58,8 @@ public record StructInitializerSyntax(List Tokens, TypeSyntax? StructType public record DereferenceSyntax(List Tokens, ExpressionSyntax Target) : ExpressionSyntax(Tokens); -public record SizeCompilerMacroSyntax(List Tokens, TypeSyntax Type) : ExpressionSyntax(Tokens); +public record SizeBuiltinSyntax(List Tokens, TypeSyntax Type) : ExpressionSyntax(Tokens); -public record InterpretCompilerMacroSyntax(List Tokens, TypeSyntax Type, ExpressionSyntax Target) : ExpressionSyntax(Tokens); \ No newline at end of file +public record InterpretBuiltinSyntax(List Tokens, TypeSyntax Type, ExpressionSyntax Target) : ExpressionSyntax(Tokens); + +public record FloatToIntBuiltinSyntax(List Tokens, TypeSyntax Type, ExpressionSyntax Value) : ExpressionSyntax(Tokens); \ No newline at end of file diff --git a/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs b/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs index 358f502..dc4b1f7 100644 --- a/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs +++ b/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs @@ -78,4 +78,6 @@ public record ConvertIntNode(NubType Type, ExpressionNode Value, NubIntType Valu public record ConvertFloatNode(NubType Type, ExpressionNode Value, NubFloatType ValueType, NubFloatType TargetType) : RValueExpressionNode(Type); -public record SizeCompilerMacroNode(NubType Type, NubType TargetType) : RValueExpressionNode(Type); \ No newline at end of file +public record SizeBuiltinNode(NubType Type, NubType TargetType) : RValueExpressionNode(Type); + +public record FloatToIntBuiltinNode(NubType Type, ExpressionNode Value, NubFloatType ValueType, NubIntType TargetType) : RValueExpressionNode(Type); \ No newline at end of file diff --git a/compiler/NubLang/TypeChecking/TypeChecker.cs b/compiler/NubLang/TypeChecking/TypeChecker.cs index 36419d1..1be3894 100644 --- a/compiler/NubLang/TypeChecking/TypeChecker.cs +++ b/compiler/NubLang/TypeChecking/TypeChecker.cs @@ -305,8 +305,9 @@ public sealed class TypeChecker LiteralSyntax expression => CheckLiteral(expression, expectedType), StructFieldAccessSyntax expression => CheckStructFieldAccess(expression), StructInitializerSyntax expression => CheckStructInitializer(expression, expectedType), - InterpretCompilerMacroSyntax expression => CheckExpression(expression.Target) with { Type = ResolveType(expression.Type) }, - SizeCompilerMacroSyntax expression => new SizeCompilerMacroNode(new NubIntType(false, 64), ResolveType(expression.Type)), + InterpretBuiltinSyntax expression => CheckExpression(expression.Target) with { Type = ResolveType(expression.Type) }, + SizeBuiltinSyntax expression => new SizeBuiltinNode(new NubIntType(false, 64), ResolveType(expression.Type)), + FloatToIntBuiltinSyntax expression => CheckFloatToInt(expression), _ => throw new ArgumentOutOfRangeException(nameof(node)) }; @@ -334,6 +335,29 @@ public sealed class TypeChecker throw new TypeCheckerException(Diagnostic.Error($"Cannot convert {result.Type} to {expectedType}").At(node).Build()); } + private FloatToIntBuiltinNode CheckFloatToInt(FloatToIntBuiltinSyntax expression) + { + var value = CheckExpression(expression.Value); + if (value.Type is not NubFloatType sourceFloatType) + { + throw new TypeCheckerException(Diagnostic + .Error("Source type of float to int conversion must be an float") + .At(expression.Value) + .Build()); + } + + var targetType = ResolveType(expression.Type); + if (targetType is not NubIntType targetIntType) + { + throw new TypeCheckerException(Diagnostic + .Error("Target type of float to int conversion must be an integer") + .At(expression.Type) + .Build()); + } + + return new FloatToIntBuiltinNode(targetIntType, value, sourceFloatType, targetIntType); + } + private AddressOfNode CheckAddressOf(AddressOfSyntax expression) { var target = CheckExpression(expression.Target); @@ -652,10 +676,25 @@ public sealed class TypeChecker { case LiteralKind.Integer: { - var type = expectedType as NubIntType ?? new NubIntType(true, 64); - return type.Signed - ? new IntLiteralNode(type, long.Parse(expression.Value)) - : new UIntLiteralNode(type, ulong.Parse(expression.Value)); + if (expectedType is NubIntType intType) + { + return intType.Signed + ? new IntLiteralNode(intType, long.Parse(expression.Value)) + : new UIntLiteralNode(intType, ulong.Parse(expression.Value)); + } + + if (expectedType is NubFloatType floatType) + { + return floatType.Width switch + { + 32 => new Float32LiteralNode(floatType, float.Parse(expression.Value)), + 64 => new Float64LiteralNode(floatType, double.Parse(expression.Value)), + _ => throw new ArgumentOutOfRangeException() + }; + } + + var type = new NubIntType(true, 64); + return new IntLiteralNode(type, long.Parse(expression.Value)); } case LiteralKind.Float: { diff --git a/example/src/main.nub b/example/src/main.nub index 8c210d6..7db1572 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -6,11 +6,30 @@ module "main" extern "main" func main(args: []cstring): i64 { raylib::InitWindow(1600, 900, "Hi from nub-lang") + raylib::SetTargetFPS(240) + + let x: i32 = 0 + let y: i32 = 0 + + let width: i32 = 100 + let height: i32 = 100 + + let direction: raylib::Vector2 = { x = 1 y = 1 } + + let bgColor: raylib::Color = { r = 0 g = 0 b = 0 a = 255 } + let color: raylib::Color = { r = 255 g = 255 b = 255 a = 255 } while !raylib::WindowShouldClose() { raylib::BeginDrawing() + { + raylib::ClearBackground(bgColor); + raylib::DrawRectangle(x, y, width, height, color) + } raylib::EndDrawing() + + x = x + @floatToInt(i32, direction.x) + y = y + @floatToInt(i32, direction.y) } return 0