separate number into float and integer

This commit is contained in:
Neemek 2026-07-07 23:24:44 +02:00
parent 10f55313b0
commit daab50d54c
Signed by: neemek
GPG key ID: 84FFE4D7D40AB25E
22 changed files with 848 additions and 428 deletions

View file

@ -7,7 +7,9 @@ import (
"fmt"
"log"
"math"
"math/big"
"os"
"strconv"
"strings"
)
@ -20,30 +22,52 @@ const (
// InstructionPop pop and delete the first item on the stack
InstructionPop
// InstructionAdd pop two and add them
InstructionAdd
// InstructionSub pop two and subtract the second from the first
InstructionSub
// InstructionMul pop two and multiply them
InstructionMul
// InstructionDiv pop two and divide the second by the first
InstructionDiv
// InstructionNegate negate the value; if it was positive, make it negative, and vice versa.
InstructionNegate
// InstructionAddFloat pop two floats and add them
InstructionAddFloat
// InstructionSubFloat pop two floats and subtract the second from the first
InstructionSubFloat
// InstructionMulFloat pop two floats and multiply them
InstructionMulFloat
// InstructionDivFloat pop two floats and divide the second by the first
InstructionDivFloat
// InstructionNegateFloat negate the float; if it was positive, make it negative, and vice versa.
InstructionNegateFloat
// InstructionAddInt pop two ints and add them
InstructionAddInt
// InstructionSubInt pop two ints and subtract the second from the first
InstructionSubInt
// InstructionMulInt pop two ints and multiply them
InstructionMulInt
// InstructionDivInt pop two ints and divide the second by the first
InstructionDivInt
// InstructionNegateInt negate the int; if it was positive, make it negative, and vice versa.
InstructionNegateInt
// InstructionEquals whether the two top values on the stack are equal
InstructionEquals
// InstructionNotEqual whether the two top values on the stack are not equal
InstructionNotEqual
// InstructionNot inverts boolean (true => false, false => true)
InstructionNot
// InstructionLess pops two from stack, pushes whether the lowest is less than the highest
InstructionLess
// InstructionLessOrEqual pops two from stack, pushes whether the lowest is less or equal than the highest
InstructionLessOrEqual
// InstructionGreater pops two from stack, pushes whether the lowest is greater than the highest
InstructionGreater
// InstructionGreaterOrEqual pops two from stack, pushes whether the lowest is greater or equal than the highest
InstructionGreaterOrEqual
// InstructionLessFloat pops two from stack, pushes whether the lowest is less than the highest
InstructionLessFloat
// InstructionLessOrEqualFloat pops two from stack, pushes whether the lowest is less or equal than the highest
InstructionLessOrEqualFloat
// InstructionGreaterFloat pops two from stack, pushes whether the lowest is greater than the highest
InstructionGreaterFloat
// InstructionGreaterOrEqualFloat pops two from stack, pushes whether the lowest is greater or equal than the highest
InstructionGreaterOrEqualFloat
// InstructionLessInt pops two from stack, pushes whether the lowest is less than the highest
InstructionLessInt
// InstructionLessOrEqualInt pops two from stack, pushes whether the lowest is less or equal than the highest
InstructionLessOrEqualInt
// InstructionGreaterInt pops two from stack, pushes whether the lowest is greater than the highest
InstructionGreaterInt
// InstructionGreaterOrEqualInt pops two from stack, pushes whether the lowest is greater or equal than the highest
InstructionGreaterOrEqualInt
// InstructionAccessProperty gets a property from a value, and pops it onto the stack
InstructionAccessProperty
@ -116,30 +140,48 @@ func (b Bytecode) String() string {
return "RETURN"
case InstructionPop:
return "POP"
case InstructionAdd:
return "ADD"
case InstructionSub:
return "SUB"
case InstructionMul:
return "MUL"
case InstructionDiv:
return "DIV"
case InstructionNegate:
return "NEGATE"
case InstructionAddFloat:
return "ADD_FLOAT"
case InstructionSubFloat:
return "SUB_FLOAT"
case InstructionMulFloat:
return "MUL_FLOAT"
case InstructionDivFloat:
return "DIV_FLOAT"
case InstructionNegateFloat:
return "NEGATE_FLOAT"
case InstructionAddInt:
return "ADD_INT"
case InstructionSubInt:
return "SUB_INT"
case InstructionMulInt:
return "MUL_INT"
case InstructionDivInt:
return "DIV_INT"
case InstructionNegateInt:
return "NEGATE_INT"
case InstructionEquals:
return "EQUALS"
case InstructionNotEqual:
return "NOT_EQUALS"
case InstructionNot:
return "NOT"
case InstructionLess:
return "LESS"
case InstructionLessOrEqual:
return "LESS_OR_EQUAL"
case InstructionGreater:
return "GREATER_OR_EQUAL"
case InstructionGreaterOrEqual:
return "GREATER_OR_EQUAL"
case InstructionLessFloat:
return "LESS_FLOAT"
case InstructionLessOrEqualFloat:
return "LESS_OR_EQUAL_FLOAT"
case InstructionGreaterFloat:
return "GREATER_FLOAT"
case InstructionGreaterOrEqualFloat:
return "GREATER_OR_EQUAL_FLOAT"
case InstructionLessInt:
return "LESS_INT"
case InstructionLessOrEqualInt:
return "LESS_OR_EQUAL_INT"
case InstructionGreaterInt:
return "GREATER_INT"
case InstructionGreaterOrEqualInt:
return "GREATER_OR_EQUAL_INT"
case InstructionJump:
return "JUMP"
case InstructionJumpFalse:
@ -232,7 +274,7 @@ func NewChunk(bytecode []Bytecode, constants []Value) *Chunk {
func RegisterGOBTypes() {
gob.Register(&StringValue{""})
gob.Register(&BoolValue{false})
gob.Register(&NumberValue{0})
gob.Register(&FloatValue{0})
gob.Register(&FunctionValue{
Name: "",
Params: nil,
@ -241,7 +283,7 @@ func RegisterGOBTypes() {
// Signatures
gob.Register(&NilSignature{})
gob.Register(&NumberSignature{})
gob.Register(&FloatSignature{})
gob.Register(&StringSignature{})
gob.Register(&FunctionSignature{})
gob.Register(&ListSignature{})
@ -368,12 +410,12 @@ var DefaultGlobals = map[string]Value{
"char": &BuiltinFunctionValue{
"char",
&FunctionSignature{
[]TypeSignature{&NumberSignature{}},
[]TypeSignature{&IntegerSignature{}},
&StringSignature{},
},
func(vm *VM, this Value, args []Value) (Value, error) {
n := args[0].(*NumberValue).Number
b := byte(n)
n := args[0].(*IntegerValue).Number
b := n.Bytes()[0]
return &StringValue{
string([]byte{b}),
@ -383,16 +425,16 @@ var DefaultGlobals = map[string]Value{
true,
},
"byte": &BuiltinFunctionValue{
"char",
"byte",
&FunctionSignature{
[]TypeSignature{&StringSignature{}},
&NumberSignature{},
&IntegerSignature{},
},
func(vm *VM, this Value, args []Value) (Value, error) {
s := args[0].(*StringValue).Text
b := []byte(s)[0]
n := new(big.Int).SetBytes([]byte(s))
return &NumberValue{float64(b)}, nil
return &IntegerValue{n}, nil
},
nil,
true,
@ -453,6 +495,66 @@ var DefaultGlobals = map[string]Value{
nil,
true,
},
"int": &BuiltinFunctionValue{
"int",
&FunctionSignature{
[]TypeSignature{&AnySignature{}},
&CompositeSignature{
&IntegerSignature{},
&NilSignature{},
},
},
func(vm *VM, _ Value, args []Value) (Value, error) {
switch v := args[0].(type) {
case *IntegerValue:
return &IntegerValue{v.Number}, nil // this might need to clone the value instead
case *FloatValue:
n := new(big.Int).SetInt64(int64(v.Number))
return &IntegerValue{n}, nil
case *StringValue:
n, success := new(big.Int).SetString(v.Text, 0) // determine base
if !success {
return &NilValue{}, nil
}
return &IntegerValue{n}, nil
default:
return nil, errors.New(fmt.Sprintf("%s cannot become an integer", v))
}
},
nil,
true,
},
"float": &BuiltinFunctionValue{
"float",
&FunctionSignature{
[]TypeSignature{&AnySignature{}},
&CompositeSignature{
&FloatSignature{},
&NilSignature{},
},
},
func(vm *VM, _ Value, args []Value) (Value, error) {
switch v := args[0].(type) {
case *IntegerValue:
n, _ := v.Number.Float64()
return &FloatValue{n}, nil // this might need to clone the value instead
case *FloatValue:
return &FloatValue{v.Number}, nil
case *StringValue:
num, err := strconv.ParseFloat(v.Text, FloatSize)
if err != nil {
return &NilValue{}, nil
}
return &FloatValue{num}, nil
default:
return nil, errors.New(fmt.Sprintf("%s cannot become an integer", v))
}
},
nil,
true,
},
"type": &BuiltinFunctionValue{
Name: "type",
Signature: &FunctionSignature{
@ -470,11 +572,11 @@ var DefaultGlobals = map[string]Value{
"exit": &BuiltinFunctionValue{
"exit",
&FunctionSignature{
[]TypeSignature{&NumberSignature{}},
[]TypeSignature{&FloatSignature{}},
&NilSignature{},
},
func(vm *VM, this Value, args []Value) (Value, error) {
os.Exit(int(args[0].(*NumberValue).Number))
os.Exit(int(args[0].(*FloatValue).Number))
return &NilValue{}, nil
},
nil,
@ -483,11 +585,11 @@ var DefaultGlobals = map[string]Value{
"floor": &BuiltinFunctionValue{
"floor",
&FunctionSignature{
[]TypeSignature{&NumberSignature{}},
&NumberSignature{},
[]TypeSignature{&FloatSignature{}},
&FloatSignature{},
},
func(vm *VM, this Value, args []Value) (Value, error) {
return &NumberValue{math.Floor(args[0].(*NumberValue).Number)}, nil
return &FloatValue{math.Floor(args[0].(*FloatValue).Number)}, nil
},
nil,
true,
@ -495,11 +597,11 @@ var DefaultGlobals = map[string]Value{
"ceil": &BuiltinFunctionValue{
"ceil",
&FunctionSignature{
[]TypeSignature{&NumberSignature{}},
&NumberSignature{},
[]TypeSignature{&FloatSignature{}},
&FloatSignature{},
},
func(vm *VM, this Value, args []Value) (Value, error) {
return &NumberValue{math.Ceil(args[0].(*NumberValue).Number)}, nil
return &FloatValue{math.Ceil(args[0].(*FloatValue).Number)}, nil
},
nil,
true,
@ -507,14 +609,14 @@ var DefaultGlobals = map[string]Value{
"roundd": &BuiltinFunctionValue{
"roundd",
&FunctionSignature{
[]TypeSignature{&NumberSignature{}, &NumberSignature{}},
&NumberSignature{},
[]TypeSignature{&FloatSignature{}, &FloatSignature{}},
&FloatSignature{},
},
func(vm *VM, this Value, args []Value) (Value, error) {
x := args[0].(*NumberValue).Number
decimals := args[1].(*NumberValue).Number
x := args[0].(*FloatValue).Number
decimals := args[1].(*FloatValue).Number
multiplier := math.Pow(10, decimals)
return &NumberValue{math.Round(x*multiplier) / multiplier}, nil
return &FloatValue{math.Round(x*multiplier) / multiplier}, nil
},
nil,
true,
@ -568,34 +670,63 @@ func (vm *VM) Next() bool {
case InstructionConstant:
vm.stack.Push(vm.ReadConstant())
case InstructionAdd:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionAddFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&NumberValue{l + r})
vm.stack.Push(&FloatValue{l + r})
case InstructionSub:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionSubFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&NumberValue{l - r})
vm.stack.Push(&FloatValue{l - r})
case InstructionMul:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionMulFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&NumberValue{l * r})
vm.stack.Push(&FloatValue{l * r})
case InstructionDiv:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionDivFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&NumberValue{l / r})
vm.stack.Push(&FloatValue{l / r})
case InstructionNegate:
v := vm.stack.Pop().(*NumberValue).Number
case InstructionNegateFloat:
v := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&NumberValue{-v})
vm.stack.Push(&FloatValue{-v})
case InstructionAddInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Add(l, r)})
case InstructionSubInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Sub(l, r)})
case InstructionMulInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Mul(l, r)})
case InstructionDivInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Div(l, r)})
case InstructionNegateInt:
v := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Neg(v)})
case InstructionEquals:
vm.stack.Push(
@ -621,30 +752,54 @@ func (vm *VM) Next() bool {
l := vm.stack.Pop().(*BoolValue).Boolean
vm.stack.Push(&BoolValue{l || r})
case InstructionLess:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionLessFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l < r})
case InstructionLessOrEqual:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionLessOrEqualFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l <= r})
case InstructionGreater:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionGreaterFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l > r})
case InstructionGreaterOrEqual:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionGreaterOrEqualFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l >= r})
case InstructionLessInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) == -1})
case InstructionLessOrEqualInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) != 1})
case InstructionGreaterInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) == 1})
case InstructionGreaterOrEqualInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) != -1})
case InstructionCall:
v := vm.stack.Pop()
switch f := v.(type) {