add inner (of lists) type, add floor, ceil, and roundd standard functions, and a ton of examples

This commit is contained in:
Neemek 2025-07-22 22:24:12 +02:00
parent 5bcab07681
commit efbe5a9f97
Signed by: neemek
GPG key ID: 28360A8951CD0E9B
20 changed files with 330 additions and 46 deletions

View file

@ -404,10 +404,20 @@ func (c *Compiler) compile(tree Node) error {
return c.error(fmt.Sprintf("argument #%d does not have expected type signature: got %s, requires %s", i, sig, f.In[i]), arg)
}
err = c.compile(arg)
if err != nil {
return err
if c.isTreeConstant(arg) {
v, err := c.compute(arg)
if err != nil {
return err
}
c.add(InstructionConstant)
c.addConstant(v)
} else {
err = c.compile(arg)
if err != nil {
return err
}
}
}
err = c.compile(n.source)
@ -716,6 +726,18 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
return nil, c.error(fmt.Sprintf("bad argument count (expected %v, got %v)", len(f.In), len(n.args)), n)
}
var innerType TypeSignature
if n.source.Type() == AccessNodeType {
member, err := c.deduceSignature(n.source.(*AccessNode).source)
if err != nil {
return nil, err
}
if member.Type() == TypeList {
innerType = member.(*ListSignature).Contents
}
}
// type check arguments
for i, arg := range n.args {
sig, err := c.deduceSignature(arg)
@ -723,11 +745,28 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
return nil, err
}
if !sig.Matches(f.In[i]) {
t := f.In[i]
if t.Type() == TypeInner {
if innerType == nil {
return nil, c.error(fmt.Sprintf("function source (%s) has no inner type", sig), n.source)
}
t = innerType
}
if !sig.Matches(t) {
return nil, c.error(fmt.Sprintf("argument #%d has wrong type signature. requires %s, got %s", i, f.In[i], sig), arg)
}
}
if f.Out.Type() == TypeInner {
if innerType == nil {
return nil, c.error(fmt.Sprintf("function source (%s) has no inner type", sig), n.source)
}
return innerType, nil
}
return f.Out, nil
case FunctionNodeType:
@ -954,7 +993,14 @@ func (c *Compiler) isTreeConstant(tree Node) bool {
return true
case BinaryNodeType:
return c.isTreeConstant(tree.(*BinaryNode).Left) && c.isTreeConstant(tree.(*BinaryNode).Right)
case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, CallNodeType, FunctionNodeType,
case CallNodeType:
for _, arg := range tree.(*CallNode).args {
if !c.isTreeConstant(arg) {
return false
}
}
return c.isTreeConstant(tree.(*CallNode).source)
case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, FunctionNodeType,
ReturnNodeType, AccessNodeType, BreakpointNodeType, ReferenceNodeType:
return false
default:
@ -1009,6 +1055,31 @@ func (c *Compiler) compute(tree Node) (Value, error) {
-v.(*NumberValue).Number,
}, nil
case *CallNode:
source, err := c.compute(n.source)
if err != nil {
return nil, err
}
f, ok := source.(*BuiltinFunctionValue)
if !ok {
return nil, nil
}
if !f.Constant {
return nil, nil
}
args := make([]Value, len(f.Signature.In))
for i, arg := range n.args {
args[i], err = c.compute(arg)
if err != nil {
return nil, err
}
}
return f.F(nil, nil, args)
default:
panic(fmt.Sprintf("unexpected node %s, %T", tree.String(), tree))
}

View file

@ -17,6 +17,7 @@ const (
TypeFunction
TypeAny
TypeComposite
TypeInner
)
func (t Type) String() string {
@ -39,6 +40,8 @@ func (t Type) String() string {
return "any"
case TypeComposite:
return "composite"
case TypeInner:
return "inner"
}
panic(fmt.Sprintf("unsupported string conversion for type %v", int(t)))
@ -330,3 +333,17 @@ func (s *CompositeSignature) Matches(other TypeSignature) bool {
func (s *CompositeSignature) String() string {
return fmt.Sprintf("%s|%s", s.A, s.B)
}
type InnerSignature struct{}
func (*InnerSignature) Type() Type {
return TypeInner
}
func (*InnerSignature) Matches(_ TypeSignature) bool {
return false
}
func (*InnerSignature) String() string {
return "inner"
}

View file

@ -434,7 +434,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
[]TypeSignature{
&NumberSignature{},
},
&AnySignature{},
&InnerSignature{},
},
func(_ *VM, this Value, p []Value) (Value, error) {
items := this.(*ListValue).Items

View file

@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"log"
"math"
"os"
"strings"
)
@ -479,6 +480,45 @@ var DefaultGlobals = map[string]Value{
nil,
false,
},
"floor": &BuiltinFunctionValue{
"floor",
&FunctionSignature{
[]TypeSignature{&NumberSignature{}},
&NumberSignature{},
},
func(vm *VM, this Value, args []Value) (Value, error) {
return &NumberValue{math.Floor(args[0].(*NumberValue).Number)}, nil
},
nil,
true,
},
"ceil": &BuiltinFunctionValue{
"ceil",
&FunctionSignature{
[]TypeSignature{&NumberSignature{}},
&NumberSignature{},
},
func(vm *VM, this Value, args []Value) (Value, error) {
return &NumberValue{math.Ceil(args[0].(*NumberValue).Number)}, nil
},
nil,
true,
},
"roundd": &BuiltinFunctionValue{
"roundd",
&FunctionSignature{
[]TypeSignature{&NumberSignature{}, &NumberSignature{}},
&NumberSignature{},
},
func(vm *VM, this Value, args []Value) (Value, error) {
x := args[0].(*NumberValue).Number
decimals := args[1].(*NumberValue).Number
multiplier := math.Pow(10, decimals)
return &NumberValue{math.Round(x*multiplier) / multiplier}, nil
},
nil,
true,
},
}
func NewVM(chunk *Chunk, stackSize Pos, callstackSize Pos) *VM {
@ -813,9 +853,7 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
for vm.chunk.Bytecode[vm.ip] != InstructionReturn && vm.Next() {
}
if vm.HasNext() {
vm.Next()
}
vm.Next()
return vm.stack.Pop(), nil