slope/lib.go

3542 lines
101 KiB
Go
Raw Permalink Normal View History

package main
import (
"bufio"
"crypto/md5"
"crypto/sha256"
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"math"
"math/rand"
"net"
2022-09-18 05:34:08 +00:00
"net/http"
"net/url"
"os"
"os/exec"
"os/user"
2021-08-14 15:08:35 +00:00
"path/filepath"
"reflect"
2021-08-11 04:56:06 +00:00
"regexp"
"strconv"
"strings"
"sync"
"syscall"
"time"
"unicode/utf8"
"git.rawtext.club/sloum/slope/termios"
)
var Sysout *IOHandle = &IOHandle{os.Stdout, true, "file (write-only)"}
var stdLibrary = vars{
"sysout": func(a ...expression) expression {
if len(a) == 0 {
return Sysout
}
switch io := a[0].(type) {
case *IOHandle:
if !io.Open {
return exception("'sysout' expected an open io-handle, but a closed io-handle was given")
}
Sysout = io
return make([]expression, 0)
default:
return exception("'sysout' expected an open io-handle, or nothing, but a non-io-handle was given")
}
},
2021-09-04 14:45:35 +00:00
"stdin": &IOHandle{os.Stdin, true, "file (read-only)"},
"stdout": &IOHandle{os.Stdout, true, "file (write-only)"},
"stderr": &IOHandle{os.Stderr, true, "file (write-only)"},
2021-08-29 04:30:55 +00:00
"devnull": func() *IOHandle {
devnull, _ := os.OpenFile(os.DevNull, os.O_WRONLY, 0755)
dn := &IOHandle{devnull, true, "file (write-only)"}
openFiles = append(openFiles, dn)
return dn
}(),
"mod-path": func(a ...expression) expression {
return getModBaseDir()
},
2021-09-03 21:31:25 +00:00
"__SIGINT": func(a ...expression) expression {
SafeExit(1)
return false
},
"signal-catch-sigint": func(a ...expression) expression {
if len(a) < 1 {
return exception("'signal-catch-sigint' expected a procedure but no value was given")
}
switch p := a[0].(type) {
case proc, func(...expression) expression:
globalenv.vars["__SIGINT"] = p
return true
default:
return exception("'signal-catch-sigint' expected a procedure, but a non-procedure value was given")
}
},
"PHI": number(math.Phi),
"PI": number(math.Pi),
"E": number(math.E),
"sys-args": func(a ...expression) expression {
args := os.Args
if len(args) > 0 && args[0] == "slope" || args[0] == "./slope" || args[0] == "../slope" {
args = args[1:]
}
return StringSliceToExpressionSlice(args)
}(),
"exception-mode-panic": func(a ...expression) expression {
panicOnException = true
return make([]expression, 0)
},
"exception-mode-pass": func(a ...expression) expression {
panicOnException = false
return make([]expression, 0)
},
"exception-mode-pass?": func(a ...expression) expression {
return panicOnException == false
},
"exception-mode-panic?": func(a ...expression) expression {
return panicOnException == true
},
2022-07-13 16:30:50 +00:00
"slope-version": func(a ...expression) expression {
out := make([]expression, 0, 4)
s := strings.SplitN(version, ".", -1)
for i := range s {
integer, err := strconv.ParseInt(s[i], 10, 64)
if err != nil {
continue
}
out = append(out, number(integer))
}
var maj, min, patch number
var ok bool
if len(a) == 0 {
return out
}
maj, ok = a[0].(number)
if !ok {
return exception("'slope-version' expected its first argument to be a number representing a major version, an invalid value was given")
}
if len(a) > 1 {
min, ok = a[1].(number)
if !ok {
return exception("'slope-version' expected its second argument to be a number representing a minor version, an invalid value was given")
}
}
if len(a) > 2 {
patch, ok = a[2].(number)
if !ok {
return exception("'slope-version' expected its third argument to be a number representing a patch version, an invalid value was given")
}
}
if out[0].(number) > maj || (out[0].(number) == maj && out[1].(number) > min) || (out[0].(number) == maj && out[1].(number) == min && out[2].(number) >= patch) {
2022-07-13 16:30:50 +00:00
return true
}
return exception(fmt.Sprintf("This program requires a slope interpreter version >= %d.%d.%d\nThe currently installed version is %s\nPlease update your slope interpreter to run this program", int(maj), int(min), int(patch), version))
},
"!": func(a ...expression) expression {
if len(a) < 0 {
return exception("'!' expects a value, no value was given")
}
if panicOnException {
panic(a[0])
}
return exception(String(a[0], false))
},
"positive?": func(a ...expression) expression {
if len(a) > 0 {
if n, ok := a[0].(number); ok {
return n > 0
}
}
return false
},
"negative?": func(a ...expression) expression {
if len(a) > 0 {
if n, ok := a[0].(number); ok {
return n < 0
}
}
return false
},
"zero?": func(a ...expression) expression {
if len(a) > 0 {
if n, ok := a[0].(number); ok {
return n == 0
}
}
return false
},
"exception?": func(a ...expression) expression {
if len(a) > 0 {
if _, ok := a[0].(exception); ok {
return true
}
}
return false
},
"abs": func(a ...expression) expression {
if len(a) < 1 {
return exception("'abs' expected a value, but no value was given")
}
i, ok := a[0].(number)
if !ok {
return exception("'abs' expected a number, but a non-number value was received")
}
if i < 0 {
i = i * -1
}
return i
},
"floor": func(a ...expression) expression {
if len(a) < 1 {
return exception("'floor' expected a value, but no value was given")
}
i, ok := a[0].(number)
if !ok {
return exception("'floor' expected a number, but a non-number value was received")
}
return number(math.Floor(float64(i)))
},
"ceil": func(a ...expression) expression {
if len(a) < 1 {
return exception("'ceil' expected a value, but no value was given")
}
i, ok := a[0].(number)
if !ok {
return exception("'ceil' expected a number, but a non-number value was received")
}
return number(math.Ceil(float64(i)))
},
"type": func(a ...expression) expression {
if len(a) == 0 {
return exception("'type' expected a value, but no value was given")
}
t := reflect.TypeOf(a[0]).String()
switch t {
case "main.number":
return "number"
case "[]main.expression":
return "list"
case "*main.IOHandle":
return "IOHandle"
case "main.symbol":
return "symbol"
case "func(...main.expression) main.expression":
return "compiled-procedure"
case "main.proc":
return "procedure"
case "main.exception":
return "exception"
case "main.macro":
return "macro"
case "main.GUI":
return "GUI"
case "main.guiWidget":
return "Widget"
case "fyne.Widget":
return "Widget"
case "fyne.CanvasObject":
return "Widget"
case "fyne.Container":
return "Container"
case "main.guiContainer":
return "Container"
default:
return t
}
},
"round": func(a ...expression) expression {
if len(a) < 1 {
return exception("'round' expected a value, but no value was given")
}
decimals := number(0.0)
if len(a) >= 2 {
if dec, ok := a[1].(number); ok {
decimals = dec
}
}
num, ok := a[0].(number)
if !ok {
return exception("'round' expected a number, but a non-number value was received")
}
return number(math.Round(float64(num)*math.Pow(10, float64(decimals))) / math.Pow(10, float64(decimals)))
},
2022-07-05 22:59:59 +00:00
"&": func(a ...expression) expression {
if len(a) < 2 {
return exception("'&' expected two numbers, insufficient arguments were given")
}
n1, ok := a[0].(number)
if !ok {
return exception("'&' expected argument one to be a number, a non-number value was given")
}
n2, ok2 := a[1].(number)
if !ok2 {
return exception("'&' expected argument two to be a number, a non-number value was given")
}
return number(int(n1) & int(n2))
},
"|": func(a ...expression) expression {
if len(a) < 2 {
return exception("'|' expected two numbers, insufficient arguments were given")
}
n1, ok := a[0].(number)
if !ok {
return exception("'|' expected argument one to be a number, a non-number value was given")
}
n2, ok2 := a[1].(number)
if !ok2 {
return exception("'|' expected argument two to be a number, a non-number value was given")
}
return number(int(n1) | int(n2))
},
"<<": func(a ...expression) expression {
if len(a) < 2 {
return exception("'<<' expected two numbers, insufficient arguments were given")
}
n1, ok := a[0].(number)
if !ok {
return exception("'<<' expected argument one to be a number, a non-number value was given")
}
n2, ok2 := a[1].(number)
if !ok2 {
return exception("'<<' expected argument two to be a number, a non-number value was given")
}
return number(int(n1) << int(n2))
},
">>": func(a ...expression) expression {
if len(a) < 2 {
return exception("'>>' expected two numbers, insufficient arguments were given")
}
n1, ok := a[0].(number)
if !ok {
return exception("'>>' expected argument one to be a number, a non-number value was given")
}
n2, ok2 := a[1].(number)
if !ok2 {
return exception("'>>' expected argument two to be a number, a non-number value was given")
}
return number(int(n1) >> int(n2))
},
"^": func(a ...expression) expression {
2022-07-06 02:48:58 +00:00
if len(a) < 1 {
2022-07-05 22:59:59 +00:00
return exception("'^' expected two numbers, insufficient arguments were given")
}
n1, ok := a[0].(number)
if !ok {
return exception("'^' expected argument one to be a number, a non-number value was given")
}
if len(a) == 1 {
return number(^int(n1))
}
n2, ok2 := a[1].(number)
if !ok2 {
return exception("'^' expected argument two to be a number, a non-number value was given")
}
return number(int(n1) ^ int(n2))
},
"+": func(a ...expression) expression {
if len(a) == 0 {
return number(0)
}
v, ok := a[0].(number)
if !ok {
return exception("'+' expected a number, but a non-number value was received")
}
for _, i := range a[1:] {
x, ok := i.(number)
if !ok {
return exception("'+' expected a number, but a non-number value was received")
}
v += x
}
return v
},
"-": func(a ...expression) expression {
if len(a) == 0 {
return exception("'-' expected a value, but no value was given")
}
v, ok := a[0].(number)
if !ok {
return exception("'-' expected a number, but a non-number value was received")
}
if len(a) == 1 {
return v * -1
}
for _, i := range a[1:] {
x, ok := i.(number)
if !ok {
return exception("'-' expected a number, but a non-number value was received")
}
v -= x
}
return v
},
"*": func(a ...expression) expression {
if len(a) == 0 {
return number(1)
}
v, ok := a[0].(number)
if !ok {
2021-09-05 03:01:11 +00:00
return exception("'*' expected a number, but a non-number value was received")
}
if len(a) == 1 {
return 1 / v
}
for _, i := range a[1:] {
x, ok := i.(number)
if !ok {
return exception("'*' expected a number, but a non-number value was received")
}
v *= x
}
return v
},
"/": func(a ...expression) expression {
if len(a) == 0 {
return exception("'/' expected a value, but no value was given")
}
v, ok := a[0].(number)
if !ok {
return exception("'/' expected a number, but a non-number value was received")
}
for _, i := range a[1:] {
x, ok := i.(number)
if !ok {
return exception("'/' expected a number, but a non-number value was received")
}
v /= x
}
return v
},
"%": func(a ...expression) expression {
if len(a) < 2 {
return exception("'%' expected two numbers, but too few values were given")
}
v, ok := a[0].(number)
if !ok {
return exception("'%' expected a number, but a non-number value was received")
}
v2, ok2 := a[1].(number)
if !ok2 {
return exception("'%' expected a number, but a non-number value was received")
}
out := math.Mod(float64(v), float64(v2))
if math.IsNaN(out) {
return number(0)
}
return number(out)
},
2022-06-15 21:27:47 +00:00
"exp": func(a ...expression) expression {
if len(a) < 2 {
return exception("'exp' expected two numbers, but too few values were given")
}
base, ok := a[0].(number)
if !ok {
return exception("'exp' expected a number as its first argument, a non-number value was given")
}
exp, ok2 := a[1].(number)
if !ok2 {
return exception("'exp' expected a number as its second argument, a non-number value was given")
}
return number(math.Pow(float64(base), float64(exp)))
},
"sqrt": func(a ...expression) expression {
if len(a) == 0 {
return exception("'sqrt' expected a number, but no value was given")
}
v, ok := a[0].(number)
if !ok {
return exception("'sqrt' expected a number, but a non-number value was received")
}
return number(math.Sqrt(float64(v)))
},
"log": func(a ...expression) expression {
if len(a) == 0 {
return exception("'log' expected a number, but no value was given")
}
v, ok := a[0].(number)
if !ok {
return exception("'log' expected a number, but a non-number value was received")
}
return number(math.Log(float64(v)))
},
"cos": func(a ...expression) expression {
if len(a) == 0 {
return exception("'cos' expected a number, but no value was given")
}
v, ok := a[0].(number)
if !ok {
return exception("'cos' expected a number, but a non-number value was received")
}
return number(math.Cos(float64(v)))
},
"sin": func(a ...expression) expression {
if len(a) == 0 {
return exception("'sin' expected a number, but no value was given")
}
v, ok := a[0].(number)
if !ok {
return exception("'sin' expected a number, but a non-number value was received")
}
return number(math.Sin(float64(v)))
},
"tan": func(a ...expression) expression {
if len(a) == 0 {
return exception("'tan' expected a number, but no value was given")
}
v, ok := a[0].(number)
if !ok {
return exception("'tan' expected a number, but a non-number value was received")
}
return number(math.Tan(float64(v)))
},
"atan": func(a ...expression) expression {
if len(a) == 0 {
return exception("'atan' expected at least one number, but no value was given")
}
v, ok := a[0].(number)
if !ok {
return exception("'atan' expected a number, but a non-number value was received")
}
if len(a) == 1 {
return number(math.Atan(float64(v)))
}
v2, ok2 := a[1].(number)
if !ok2 {
return exception("'atan' expected a number as its second argument, but a non-number value was received")
}
return number(math.Atan2(float64(v), float64(v2)))
},
"min": func(a ...expression) expression {
if len(a) == 0 {
return exception("'min' expected a value, but no value was given")
}
val := number(math.MaxFloat64)
for _, v := range a {
value, ok := v.(number)
if !ok {
return exception("'min' expected a number, but a non-number value was received")
}
if value < val {
val = value
}
}
return val
},
"max": func(a ...expression) expression {
if len(a) == 0 {
return exception("'max' expected a value, but no value was given")
}
val := number(math.SmallestNonzeroFloat64)
for _, v := range a {
value, ok := v.(number)
if !ok {
return exception("'max' expected a number, but a non-number value was received")
}
if value > val {
val = value
}
}
return val
},
"<=": func(a ...expression) expression {
if len(a) < 2 {
return exception("'<=' expected, but did not receive, two values")
}
n1, ok1 := a[0].(number)
n2, ok2 := a[1].(number)
if !ok1 || !ok2 {
return exception("'<=' expected numbers, but a non-number value was received")
}
return n1 <= n2
},
"<": func(a ...expression) expression {
if len(a) < 2 {
return exception("'<' expected, but did not receive, two values")
}
n1, ok1 := a[0].(number)
n2, ok2 := a[1].(number)
if !ok1 || !ok2 {
return exception("'<' expected numbers, but a non-number value was received")
}
return n1 < n2
},
">=": func(a ...expression) expression {
if len(a) < 2 {
return exception("'>=' expected, but did not receive, two values")
}
n1, ok1 := a[0].(number)
n2, ok2 := a[1].(number)
if !ok1 || !ok2 {
return exception("'>=' expected numbers, but a non-number value was received")
}
return n1 >= n2
},
">": func(a ...expression) expression {
if len(a) < 2 {
return exception("'>' expected, but did not receive, two values")
}
n1, ok1 := a[0].(number)
n2, ok2 := a[1].(number)
if !ok1 || !ok2 {
return exception("'>' expected numbers, but a non-number value was received")
}
return n1 > n2
},
"equal?": func(a ...expression) expression {
if len(a) < 1 {
return exception("'equal?' expected, but did not receive, at least one value")
} else if len(a) == 1 {
return true
}
eq := false
for i := 0; i < len(a)-1; i++ {
eq = reflect.DeepEqual(a[i], a[i+1])
if !eq {
return false
}
}
return true
},
"atom?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'atom?' expected a value, but no value was given")
}
switch a[0].(type) {
case []expression:
return false
default:
return true
}
},
"assoc?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'assoc?' expected a value, but no value was given")
}
list, ok := a[0].([]expression)
if !ok {
return false
}
for x := range list {
l, ok := list[x].([]expression)
if !ok || len(l) != 2 {
return false
}
}
return true
},
"assoc-has-key?": func(a ...expression) expression {
if len(a) < 2 {
return exception("'assoc-has-key' expected at least two values, an associative list and a key value; insufficient arguments were given")
}
list, ok := a[0].([]expression)
if !ok {
return exception("'assoc' expected argument 1 to be an association list, a non-list value was given")
}
for x := range list {
l, ok := list[x].([]expression)
if !ok || len(l) != 2 {
return exception("'assoc' expected argument 1 to be an association list, a list was given, but it is not an association list")
}
if reflect.DeepEqual(l[0], a[1]) {
return true
}
}
return false
},
2021-08-24 05:49:20 +00:00
"assoc": func(a ...expression) expression {
if len(a) < 2 {
2021-08-29 04:27:28 +00:00
return exception("'assoc' expected at least two values, a list and a value; insufficient arguments were given")
}
list, ok := a[0].([]expression)
if !ok {
2021-08-24 05:49:20 +00:00
return exception("'assoc' expected argument 1 to be an association list, a non-list value was given")
}
for x := range list {
l, ok := list[x].([]expression)
if !ok || len(l) != 2 {
2021-08-24 05:49:20 +00:00
return exception("'assoc' expected argument 1 to be an association list, a list was given, but it is not an association list")
}
if reflect.DeepEqual(l[0], a[1]) {
2021-08-24 05:49:20 +00:00
if len(a) >= 3 {
if len(a) > 3 && AnythingToBool(a[3]).(bool) {
l[1] = a[2]
list[x] = l
return list
} else {
cpy := make([]expression, len(list))
copy(cpy, list)
cpy[x] = []expression{l[0], a[2]}
return cpy
}
2021-08-24 05:49:20 +00:00
}
return l[1]
}
}
2021-08-24 05:49:20 +00:00
if len(a) >= 3 {
if len(a) > 3 && AnythingToBool(a[3]).(bool) {
// This will almost certainly create a new assoc
// and return it rather than adding to the existing
// assoc, and is thus similar to the else branch,
// but in theory has a chance that a copy will not
// be needed, thus potentially executing faster.
list = append(list, []expression{a[1], a[2]})
return list
} else {
cpy := make([]expression, len(list))
copy(cpy, list)
cpy = append(cpy, []expression{a[1], a[2]})
return cpy
}
}
return exception("'assoc' could not find the key: " + String(a[1], true))
},
"number?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'number?' expected a value, but no value was given")
}
switch a[0].(type) {
case float64, number:
return true
default:
return false
}
},
"byte?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'byte?' expected a value, but no value was given")
}
num, ok := a[0].(number)
if !ok {
return false
}
return int(num) >= 0 && int(num) <= 255
},
"string?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'string?' expected a value, but no value was given")
}
switch a[0].(type) {
case string:
return true
default:
return false
}
},
"io-handle?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'io-handle?' expected a value, but no value was given")
}
switch a[0].(type) {
case *IOHandle:
return true
default:
return false
}
},
"string-buf?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'string-buf?' expected a value, but no value was given")
}
switch v := a[0].(type) {
case *IOHandle:
_, ok := v.Obj.(*strings.Builder)
return ok
default:
return false
}
},
"bool?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'bool?' expected a value, but no value was given")
}
switch a[0].(type) {
case bool:
return true
default:
return false
}
},
"symbol?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'symbol?' expected a value, but no value was given")
}
switch a[0].(type) {
case symbol:
return true
default:
return false
}
},
"null?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'null?' expected a list, but no value was given")
}
switch i := a[0].(type) {
case []expression:
return len(i) == 0
default:
return false
}
},
"pair?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'pair?' expected a value, but no value was given")
}
switch i := a[0].(type) {
case []expression:
return len(i) > 0
default:
return false
}
},
"list?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'list?' expected a value, but no value was given")
}
switch a[0].(type) {
case []expression:
return true
default:
return false
}
},
2022-06-21 22:26:07 +00:00
"macro?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'macro?' expected a value, but no value was given")
}
switch a[0].(type) {
case macro:
return true
default:
return false
}
},
"procedure?": func(a ...expression) expression {
if len(a) == 0 {
return exception("'proc?' expected a value, but no value was given")
}
switch a[0].(type) {
case proc, func(a ...expression) expression:
return true
default:
return false
}
},
"length": func(a ...expression) expression {
switch i := a[0].(type) {
case []expression:
return number(len(i))
case string:
return number(len([]rune(String(i, false))))
case exception:
return number(len([]rune(String(string(i), false))))
case *IOHandle:
switch h := i.Obj.(type) {
case *strings.Builder:
return number(len([]rune(String(h.String(), false))))
default:
return exception("'length' is not defined for a non-string-buf IOHandle")
}
default:
return exception("'length' expected a list or a string, but the given value was not a list or a string")
}
},
"string-make-buf": func(a ...expression) expression {
var b strings.Builder
if len(a) > 0 {
2021-09-04 14:45:35 +00:00
b.WriteString(String(a[0], false))
}
obj := &IOHandle{&b, true, "string-buf"}
openFiles = append(openFiles, obj)
return obj
},
"string-buf-clear": func(a ...expression) expression {
if len(a) == 0 {
return exception("'string-buf-clear' expects an IOHandle for a string-buf as an argument, no argument was given")
}
h, ok := a[0].(*IOHandle)
if !ok {
return exception("'string-buf-clear' expects an IOHandle as its argument, a non-IOHandle argument was given")
}
if !h.Open {
return exception("a closed IOHandle was given to 'string-buf-clear'")
}
switch buf := h.Obj.(type) {
case *strings.Builder:
buf.Reset()
default:
return exception("'string-buf-clear' expects an IOHandle representing a string-buf as its argument, a non-string-buf-IOHandle argument was given")
}
return make([]expression, 0)
},
"string-upper": func(a ...expression) expression {
if len(a) == 0 {
return exception("no value was given to 'string-upper'")
}
s, ok := a[0].(string)
if !ok {
return exception("'string-upper' expected a string but received a non-string value")
}
return strings.ToUpper(s)
},
"string-lower": func(a ...expression) expression {
if len(a) == 0 {
return exception("no value was given to 'string-lower'")
}
s, ok := a[0].(string)
if !ok {
return exception("'string-lower' expected a string but received a non-string value")
}
return strings.ToLower(s)
},
"string-format": func(a ...expression) expression {
if len(a) == 0 {
return exception("no value was given to 'string-format'")