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

43
bad.ang
View file

@ -1,5 +1,40 @@
import "lib/list.ang" import "lib/math.ang"
write(str(map([1, 2, 3], func(n: number) number { primes := [2]
return n*n
}))) func is_prime(x: number) boolean {
i := 0
while i < primes.length() && primes.at(i)*primes.at(i) < x {
if mod(x, primes.at(i)) == 0 {
return false
}
i = i + 1
}
return true
}
n := 1
max := 100000
while n < max {
n = n + 2
if is_prime(n) {
primes.append(n)
# Update counter
print(char(0x0D))
print(str(n))
print("/")
print(str(max))
print(char(0x09))
print(str(roundd(n/max*100, 2)))
print("%")
print(char(0x09))
print(str(primes.length()))
print(" primes")
}
}
write(str(primes))

15
chars.ang Normal file
View file

@ -0,0 +1,15 @@
MAX_WIDTH := 16
print(" ")
w := 1
n := 0x21
while n < 0xA0 {
print(char(n))
n = n + 1
w = w + 1
if w >= MAX_WIDTH {
write("")
w = 0
}
}

24
codegen.ang Normal file
View file

@ -0,0 +1,24 @@
passphrase := "Hello world!".split("")
start := [0, 0, 0]
modulus := 10
base := byte("!")
i := 0
n := 0
while n < passphrase.length() {
b := byte(passphrase.at(n))
v = start.at(i) + b - base
while v >= modulus {
v = v - modulus
}
start.put(i, v)
if i >= 3 {
i = 0
}
n = n + 1
}

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) 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 c.isTreeConstant(arg) {
if err != nil { v, err := c.compute(arg)
return err 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) 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) 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 // type check arguments
for i, arg := range n.args { for i, arg := range n.args {
sig, err := c.deduceSignature(arg) sig, err := c.deduceSignature(arg)
@ -723,11 +745,28 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
return nil, err 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) 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 return f.Out, nil
case FunctionNodeType: case FunctionNodeType:
@ -954,7 +993,14 @@ func (c *Compiler) isTreeConstant(tree Node) bool {
return true return true
case BinaryNodeType: case BinaryNodeType:
return c.isTreeConstant(tree.(*BinaryNode).Left) && c.isTreeConstant(tree.(*BinaryNode).Right) 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: ReturnNodeType, AccessNodeType, BreakpointNodeType, ReferenceNodeType:
return false return false
default: default:
@ -1009,6 +1055,31 @@ func (c *Compiler) compute(tree Node) (Value, error) {
-v.(*NumberValue).Number, -v.(*NumberValue).Number,
}, nil }, 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: default:
panic(fmt.Sprintf("unexpected node %s, %T", tree.String(), tree)) panic(fmt.Sprintf("unexpected node %s, %T", tree.String(), tree))
} }

View file

@ -17,6 +17,7 @@ const (
TypeFunction TypeFunction
TypeAny TypeAny
TypeComposite TypeComposite
TypeInner
) )
func (t Type) String() string { func (t Type) String() string {
@ -39,6 +40,8 @@ func (t Type) String() string {
return "any" return "any"
case TypeComposite: case TypeComposite:
return "composite" return "composite"
case TypeInner:
return "inner"
} }
panic(fmt.Sprintf("unsupported string conversion for type %v", int(t))) 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 { func (s *CompositeSignature) String() string {
return fmt.Sprintf("%s|%s", s.A, s.B) 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{ []TypeSignature{
&NumberSignature{}, &NumberSignature{},
}, },
&AnySignature{}, &InnerSignature{},
}, },
func(_ *VM, this Value, p []Value) (Value, error) { func(_ *VM, this Value, p []Value) (Value, error) {
items := this.(*ListValue).Items items := this.(*ListValue).Items

View file

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"log" "log"
"math"
"os" "os"
"strings" "strings"
) )
@ -479,6 +480,45 @@ var DefaultGlobals = map[string]Value{
nil, nil,
false, 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 { 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() { for vm.chunk.Bytecode[vm.ip] != InstructionReturn && vm.Next() {
} }
if vm.HasNext() { vm.Next()
vm.Next()
}
return vm.stack.Pop(), nil return vm.stack.Pop(), nil

2
emoji.ang Normal file
View file

@ -0,0 +1,2 @@
write(char(0x12) + char(0x85) + char(0x07))

0
examples/brainfuck.ang Normal file
View file

View file

@ -1,15 +1,14 @@
# This program computes the fibonacci numbers using recursion (O(2^n))
# fibonacci sequence # It is very slow
func fib(n) { func fib(x: number) number {
if n <= 1 { if x <= 1 {
return n return x
} }
return fib(x - 1) + fib(x - 2)
return fib(n - 1) + fib(n - 2)
} }
n := 0 n := 0
while n < 10 { while n < 100 {
write(fib(n)) write(str(fib(n)))
n = n + 1 n = n + 1
} }

13
examples/solving.ang Normal file
View file

@ -0,0 +1,13 @@
import "../lib/math.ang"
a := 120
b := 10
# find log_b(a)
func f(x: number)number {
return pow(b, x) - a
}
log_b := newtons(f)
write(str(log_b))
write("inverse: "+str(pow(b, log_b))+" = "+str(a))

4
fails.ang Normal file
View file

@ -0,0 +1,4 @@
import "lib/honning.ang"
write(_bell+_italic+"Hello "+_underline+"world "+_strike+"micheal"+_reset)

15
imp.ang Normal file
View file

@ -0,0 +1,15 @@
func is_cool(x: number|string) boolean {
if x == "cool" {
return true
} else if x == 69 {
return true
}
return nil
}
write(str(is_cool("not cool")))
write(str(is_cool("cool")))
write(str(is_cool(0)))
write(str(is_cool(69)))

15
lib/honning.ang Normal file
View file

@ -0,0 +1,15 @@
_bell := char(0x07)
ESC := char(0x1B)
CSI := ESC + "["
_reset := CSI + "0m"
_bold := CSI + "1m"
_faint := CSI + "2m"
_italic := CSI + "3m"
_underline := CSI + "4m"
_slow_blink := CSI + "5m"
_rapid_blink := CSI + "6m"
_strike := CSI + "9m"
_primary_font := CSI + "10m"

View file

@ -17,6 +17,24 @@ func abs(x: number) number {
return x return x
} }
DERIVE_DX := 0.00000001
func derive(f: func(number)number, x: number) number {
return (f(x + DERIVE_DX) - f(x))/DERIVE_DX
}
NEWTONS_ACC := 0.000000000001
func newtons(f: func(number)number) number {
pg := 0
g := 1
while abs(g - pg) > NEWTONS_ACC {
pg = g
g = pg - f(pg) / derive(f, pg)
}
return g
}
MAX_SQRT_DX := 0.0000001 MAX_SQRT_DX := 0.0000001
# sqrt(x) # sqrt(x)
@ -42,18 +60,14 @@ func sqrt(x: number) number {
# Return the whole number part of the number. if x is a whole number, # Return the whole number part of the number. if x is a whole number,
# the returned value is x. If x is not a whole number, the closest # the returned value is x. If x is not a whole number, the closest
# whole number which is less than or equal to x is returned. # whole number which is less than or equal to x is returned.
func floor(x: number) number {
# todo
}
# ceil(x) # ceil(x)
# x: number # x: number
# Return the whole number part of the number. if x is a whole number, # Return the whole number part of the number. if x is a whole number,
# the returned value is x. If x is not a whole number, the closest # the returned value is x. If x is not a whole number, the closest
# whole number which is greater than or equal to x is returned. # whole number which is greater than or equal to x is returned.
func ceil(x: number) number {
# todo
}
# round(x) # round(x)
# x: number # x: number
@ -72,7 +86,7 @@ func round(x: number) number {
# x: number; any number # x: number; any number
# n: number; the number to divide by # n: number; the number to divide by
# Return the rest from a division of x by n. # Return the rest from a division of x by n.
func mod(x: number, n: number) { func mod(x: number, n: number) number {
if x == 0 { if x == 0 {
return 0 return 0
} }
@ -136,6 +150,23 @@ func exp(x: number) number {
} }
} }
# ln(x)
# x: number; any number
# Get the approximate value of the natural logarithm
# This function uses newton's method to approximate.
LN_ACC := 0.0000000001
func ln(x: number) number {
pg := 0
g := 1
while abs(pg - g) > LN_ACC {
pg = g
g = pg + x / exp(pg) - 1
}
return g
}
# pow(x, p) # pow(x, p)
# x: number; any number. The base # x: number; any number. The base
# p: number; the value of the exponent # p: number; the value of the exponent
@ -144,18 +175,21 @@ func pow(x: number, p: number) number {
return exp(p*ln(x)) return exp(p*ln(x))
} }
# ln(x) # log(x, b)
# x: number; any number # x: number; any number greater than 0
# Get the approximate value of the natural logarithm # b: number; any number as the base
# This function uses newton's method to approximate. # Calculate the approximate value of the logarithm
LN_ACC := 0.000000001 # of a with b as base.
func ln(x: number) number { LOG_ACC := 0.0000001
func log(a: number, b: number) number {
ln_b := ln(b)
pg := 0 pg := 0
g := 1 g := 1
while abs(pg - g) > LN_ACC { while abs(g - pg) > LOG_ACC {
pg = g pg = g
g = pg + x / exp(pg) - 1 g = pg - 1/ln_b - a/(ln_b*pow(b, pg))
} }
return g return g

View file

@ -38,9 +38,9 @@ done
if [ 0 -ne "$(wc -w <<< "${errors[@]}")" ]; then if [ 0 -ne "$(wc -w <<< "${errors[@]}")" ]; then
echo "== Errors occured while executing ==" echo "=x= Errors occured while executing =x="
echo "erroring files: $(printf '%s ' "${errors[@]}")" echo "erroring files: $(printf '%s ' "${errors[@]}")"
exit 1 exit 1
else else
echo '== Successfully ran all tests ==' echo '=+= Successfully ran all tests =+='
fi fi

View file

@ -3,9 +3,8 @@ assertEq(1, 1)
assertEq(0, 0) assertEq(0, 0)
assertEq("", "") assertEq("", "")
assertEq([], []) assertEq([]number, []number)
assertEq([3, 1, 4, 1], [3, 1, 4, 1]) assertEq([3, 1, 4, 1], [3, 1, 4, 1])
assertEq([true, 1024, nil, "Hello world!"], [true, 1024, nil, "Hello world!"])
# Inequality # Inequality
assertNotEq(2, 3) assertNotEq(2, 3)

View file

@ -1,4 +1,4 @@
list := [] list := []number
x := 1 x := 1
while x <= 1000 { while x <= 1000 {
@ -14,7 +14,7 @@ func sum(a: number, b: number) number {
return a + b return a + b
} }
list = [] list = []number
x = 1 x = 1
while x <= 100 { while x <= 100 {
list.append(2*x - 1) list.append(2*x - 1)

View file

@ -1,5 +1,5 @@
list := [] list := []number
list.append(1) list.append(1)
list.append(2) list.append(2)

View file

@ -4,9 +4,12 @@ a := 2
{ {
a := 3 a := 3
assertEq(a, 3) assertEq(a, 3)
breakpoint
a = 4 a = 4
assertEq(a, 4) assertEq(a, 4)
breakpoint
} }
assertEq(a, 2) assertEq(a, 2)
breakpoint