mirror of
https://github.com/Open-Argon/argon-v3.git
synced 2025-12-06 08:56:07 +00:00
add rounding
This commit is contained in:
2
go.mod
2
go.mod
@@ -1,3 +1,5 @@
|
||||
module wbell.dev/m/v2
|
||||
|
||||
go 1.19
|
||||
|
||||
require github.com/wadey/go-rounding v1.1.0 // indirect
|
||||
|
||||
2
go.sum
Normal file
2
go.sum
Normal file
@@ -0,0 +1,2 @@
|
||||
github.com/wadey/go-rounding v1.1.0 h1:RAs9dMkB/uUHFv9ljlbRFC8/kBrQ5jhwt1GQq+2cciY=
|
||||
github.com/wadey/go-rounding v1.1.0/go.mod h1:/uD953tCL6Fea2Yp+LZBBp8d60QSObkMJxY6SPOJ5QE=
|
||||
@@ -1,5 +1,7 @@
|
||||
package main
|
||||
|
||||
import "github.com/wadey/go-rounding"
|
||||
|
||||
var vars = scope{}
|
||||
|
||||
func init() {
|
||||
@@ -41,7 +43,53 @@ func init() {
|
||||
}
|
||||
return newmap, ArErr{}
|
||||
}
|
||||
return nil, ArErr{TYPE: "TypeError", message: "Cannot create map from " + typeof(a[0]), EXISTS: true}
|
||||
return nil, ArErr{TYPE: "TypeError", message: "Cannot create map from '" + typeof(a[0]) + "'", EXISTS: true}
|
||||
}}
|
||||
vars["array"] = builtinFunc{"array", func(a ...any) (any, ArErr) {
|
||||
if len(a) == 0 {
|
||||
return ArArray{}, ArErr{}
|
||||
}
|
||||
switch x := a[0].(type) {
|
||||
case ArArray:
|
||||
return x, ArErr{}
|
||||
case string:
|
||||
newarray := ArArray{}
|
||||
for _, v := range x {
|
||||
newarray = append(newarray, string(v))
|
||||
}
|
||||
return newarray, ArErr{}
|
||||
case ArMap:
|
||||
newarray := ArArray{}
|
||||
for _, v := range x {
|
||||
newarray = append(newarray, v)
|
||||
}
|
||||
return newarray, ArErr{}
|
||||
}
|
||||
return nil, ArErr{TYPE: "TypeError", message: "Cannot create array from '" + typeof(a[0]) + "'", EXISTS: true}
|
||||
}}
|
||||
vars["round"] = builtinFunc{"round", func(a ...any) (any, ArErr) {
|
||||
if len(a) == 0 {
|
||||
return nil, ArErr{TYPE: "round", message: "round takes 1 argument",
|
||||
EXISTS: true}
|
||||
}
|
||||
precision := newNumber()
|
||||
if len(a) > 1 {
|
||||
switch x := a[1].(type) {
|
||||
case number:
|
||||
if !x.IsInt() {
|
||||
return nil, ArErr{TYPE: "TypeError", message: "Cannot round to '" + typeof(a[1]) + "'", EXISTS: true}
|
||||
}
|
||||
precision = x
|
||||
default:
|
||||
return nil, ArErr{TYPE: "TypeError", message: "Cannot round to '" + typeof(a[1]) + "'", EXISTS: true}
|
||||
}
|
||||
}
|
||||
|
||||
switch x := a[0].(type) {
|
||||
case number:
|
||||
return rounding.Round(newNumber().Set(x), int(precision.Num().Int64()), rounding.HalfUp), ArErr{}
|
||||
}
|
||||
return nil, ArErr{TYPE: "TypeError", message: "Cannot round '" + typeof(a[0]) + "'", EXISTS: true}
|
||||
}}
|
||||
vars["time"] = ArTime
|
||||
vars["PI"] = PI
|
||||
|
||||
59
src/factorial.go
Normal file
59
src/factorial.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var factorialCompiled = makeRegex(`( *)(.|\n)+\!( *)`)
|
||||
|
||||
type factorial struct {
|
||||
value any
|
||||
code string
|
||||
line int
|
||||
path string
|
||||
}
|
||||
|
||||
func parseFactorial(code UNPARSEcode, index int, codeline []UNPARSEcode) (factorial, bool, ArErr, int) {
|
||||
trim := strings.TrimSpace(code.code)
|
||||
trim = trim[:len(trim)-1]
|
||||
val, success, err, i := translateVal(UNPARSEcode{code: trim, realcode: code.realcode, line: 1, path: ""}, 0, []UNPARSEcode{}, false)
|
||||
if !success {
|
||||
return factorial{}, false, err, i
|
||||
}
|
||||
|
||||
return factorial{val, code.code, code.line, code.path}, success, ArErr{}, i
|
||||
}
|
||||
|
||||
func isFactorial(code UNPARSEcode) bool {
|
||||
return factorialCompiled.MatchString(code.code)
|
||||
}
|
||||
|
||||
func fact(n number) number {
|
||||
if n.Cmp(newNumber().SetInt64(0)) == 0 {
|
||||
return newNumber().SetInt64(1)
|
||||
}
|
||||
result := newNumber().SetInt64(1)
|
||||
for i := newNumber().SetInt64(2); i.Cmp(n) <= 0; i.Add(i, newNumber().SetInt64(1)) {
|
||||
result.Mul(result, i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func runFactorial(f factorial, stack stack) (any, ArErr) {
|
||||
val, err := runVal(f.value, stack)
|
||||
if err.EXISTS {
|
||||
return nil, err
|
||||
}
|
||||
switch x := val.(type) {
|
||||
case number:
|
||||
if !x.IsInt() {
|
||||
return nil, ArErr{"Runtime Error", "cannot use factorial on non-integer", f.line, f.path, f.code, true}
|
||||
}
|
||||
if x.Cmp(newNumber().SetInt64(0)) == -1 {
|
||||
return nil, ArErr{"Runtime Error", "cannot use factorial on negative number", f.line, f.path, f.code, true}
|
||||
}
|
||||
return fact(x), ArErr{}
|
||||
default:
|
||||
return nil, ArErr{"Runtime Error", "cannot use factorial on non-number of type '" + typeof(val) + "'", f.line, f.path, f.code, true}
|
||||
}
|
||||
}
|
||||
@@ -35,40 +35,19 @@ func isAnyNumber(x any) bool {
|
||||
}
|
||||
|
||||
// converts a number type to a string
|
||||
func numberToString(num number, fraction int, simplify bool) string {
|
||||
if fraction != 0 {
|
||||
str := num.RatString()
|
||||
if fraction == 1 {
|
||||
return str
|
||||
}
|
||||
split := strings.SplitN(str, "/", 2)
|
||||
if len(str) == 1 {
|
||||
return split[0]
|
||||
}
|
||||
numerator := split[0]
|
||||
denominator := split[1]
|
||||
|
||||
super := []string{}
|
||||
for i := 0; i <= len(numerator); i++ {
|
||||
super = append(super, superscript[numerator[i]])
|
||||
}
|
||||
sub := []string{}
|
||||
for i := 0; i < len(denominator); i++ {
|
||||
sub = append(sub, subscript[denominator[i]])
|
||||
}
|
||||
return strings.Join(super, "") + "/" + strings.Join(sub, "")
|
||||
}
|
||||
func numberToString(num number, simplify bool) string {
|
||||
if simplify {
|
||||
divPI, _ := newNumber().Quo(num, PI).Float64()
|
||||
floated := float64(int(divPI * 100))
|
||||
if divPI == 1 {
|
||||
divPI := newNumber().Quo(num, PI)
|
||||
if divPI.Cmp(newNumber().SetInt64(1)) == 0 {
|
||||
return "π"
|
||||
} else if divPI == -1 {
|
||||
} else if divPI.Cmp(newNumber().SetInt64(-1)) == 0 {
|
||||
return "-π"
|
||||
} else if divPI == 0 {
|
||||
} else if divPI.Cmp(newNumber()) == 0 {
|
||||
return "0"
|
||||
} else if (divPI*100) == floated && floated != 0 {
|
||||
return fmt.Sprint(divPI) + "π"
|
||||
} else if divPI.Denom().Cmp(new(big.Int).SetInt64(1000)) <= 0 {
|
||||
num := divPI.RatString()
|
||||
|
||||
return fmt.Sprint(num, "π")
|
||||
}
|
||||
}
|
||||
x, _ := num.Float64()
|
||||
|
||||
@@ -21,6 +21,8 @@ func runVal(line any, stack stack) (any, ArErr) {
|
||||
return x, ArErr{}
|
||||
case call:
|
||||
return runCall(x, stack)
|
||||
case factorial:
|
||||
return runFactorial(x, stack)
|
||||
case accessVariable:
|
||||
return readVariable(x, stack)
|
||||
case ArMapGet:
|
||||
|
||||
@@ -24,6 +24,14 @@ var plain = ArMap{
|
||||
fmt.Println(output...)
|
||||
return nil, ArErr{}
|
||||
}},
|
||||
"print": builtinFunc{"print", func(args ...any) (any, ArErr) {
|
||||
output := []any{}
|
||||
for i := 0; i < len(args); i++ {
|
||||
output = append(output, anyToArgon(args[i], false, false, 3, 0, false, 0))
|
||||
}
|
||||
fmt.Println(output...)
|
||||
return nil, ArErr{}
|
||||
}},
|
||||
}
|
||||
|
||||
var ArTerm = ArMap{
|
||||
|
||||
@@ -48,7 +48,7 @@ func anyToArgon(x any, quote bool, simplify bool, depth int, indent int, color b
|
||||
} else if math.IsInf(num, -1) {
|
||||
output = append(output, "-infinity")
|
||||
} else {
|
||||
output = append(output, numberToString(x, 0, simplify))
|
||||
output = append(output, numberToString(x, simplify))
|
||||
}
|
||||
if color {
|
||||
output = append(output, "\x1b[0m")
|
||||
@@ -85,6 +85,32 @@ func anyToArgon(x any, quote bool, simplify bool, depth int, indent int, color b
|
||||
output = append(output, anyToArgon(key, true, true, depth, (indent+1)*plain, color, plain)+": "+anyToArgon(x[key], true, true, depth-1, indent+1, color, plain))
|
||||
}
|
||||
return "{" + maybenewline + (strings.Repeat(" ", (indent+1)*plain)) + strings.Join(output, ","+maybenewline+(strings.Repeat(" ", (indent+1)*plain))) + maybenewline + (strings.Repeat(" ", indent*plain)) + "}"
|
||||
case ArArray:
|
||||
if len(x) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
output := []string{}
|
||||
if simplify && len(x) >= 100 {
|
||||
for i := 0; i < 10; i++ {
|
||||
item := x[i]
|
||||
output = append(output, anyToArgon(item, true, true, depth-1, indent+1, color, plain))
|
||||
}
|
||||
if color {
|
||||
output = append(output, "\x1b[38;5;240m(...)\x1b[0m")
|
||||
} else {
|
||||
output = append(output, "(...)")
|
||||
}
|
||||
for i := len(x) - 10; i < len(x); i++ {
|
||||
item := x[i]
|
||||
output = append(output, anyToArgon(item, true, true, depth-1, indent+1, color, plain))
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < len(x); i++ {
|
||||
item := x[i]
|
||||
output = append(output, anyToArgon(item, true, true, depth-1, indent+1, color, plain))
|
||||
}
|
||||
}
|
||||
return "[" + maybenewline + (strings.Repeat(" ", (indent+1)*plain)) + strings.Join(output, ","+maybenewline+(strings.Repeat(" ", (indent+1)*plain))) + maybenewline + (strings.Repeat(" ", indent*plain)) + "]"
|
||||
case builtinFunc:
|
||||
if color {
|
||||
output = append(output, "\x1b[38;5;240m")
|
||||
|
||||
@@ -51,6 +51,8 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine b
|
||||
return parseNumber(code)
|
||||
} else if isNegative(code) {
|
||||
return parseNegative(code, index, codelines)
|
||||
} else if isFactorial(code) {
|
||||
return parseFactorial(code, index, codelines)
|
||||
} else if isCall(code) {
|
||||
call, worked, err, step := parseCall(code, index, codelines)
|
||||
if worked {
|
||||
|
||||
Reference in New Issue
Block a user