2021-07-26 05:31:22 +00:00
package main
import (
"bufio"
2021-08-23 22:39:48 +00:00
"crypto/md5"
"crypto/sha256"
2021-07-28 05:45:52 +00:00
"crypto/tls"
2021-07-26 05:31:22 +00:00
"fmt"
2021-07-27 03:19:59 +00:00
"io"
2021-07-27 22:35:00 +00:00
"io/ioutil"
2021-07-26 05:31:22 +00:00
"math"
2021-08-23 05:38:54 +00:00
"math/rand"
2021-07-27 22:35:00 +00:00
"net"
2022-09-18 05:34:08 +00:00
"net/http"
2021-07-29 03:47:05 +00:00
"net/url"
2021-07-26 05:31:22 +00:00
"os"
2021-08-13 06:02:54 +00:00
"os/exec"
2022-07-06 20:52:18 +00:00
"os/user"
2021-08-14 15:08:35 +00:00
"path/filepath"
2021-07-26 05:31:22 +00:00
"reflect"
2021-08-11 04:56:06 +00:00
"regexp"
2021-07-26 05:31:22 +00:00
"strconv"
"strings"
2022-10-10 15:58:25 +00:00
"sync"
2021-08-13 06:02:54 +00:00
"syscall"
2021-07-28 05:45:52 +00:00
"time"
2021-09-05 23:04:08 +00:00
"unicode/utf8"
2021-08-15 06:16:29 +00:00
"git.rawtext.club/sloum/slope/termios"
2021-07-26 05:31:22 +00:00
)
2022-04-28 05:59:02 +00:00
var Sysout * IOHandle = & IOHandle { os . Stdout , true , "file (write-only)" }
2021-07-26 05:31:22 +00:00
var stdLibrary = vars {
2022-04-28 05:59:02 +00:00
"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)" } ,
2021-08-24 21:04:47 +00:00
"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
} ( ) ,
2021-09-04 21:38:58 +00:00
"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" )
}
} ,
2021-09-04 14:55:22 +00:00
"PHI" : number ( math . Phi ) ,
"PI" : number ( math . Pi ) ,
"E" : number ( math . E ) ,
2021-08-14 06:15:10 +00:00
"sys-args" : func ( a ... expression ) expression {
args := os . Args
2021-08-20 21:58:28 +00:00
if len ( args ) > 0 && args [ 0 ] == "slope" || args [ 0 ] == "./slope" || args [ 0 ] == "../slope" {
2021-08-14 06:15:10 +00:00
args = args [ 1 : ]
}
return StringSliceToExpressionSlice ( args )
2021-08-20 21:58:28 +00:00
} ( ) ,
2021-08-09 22:53:27 +00:00
"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 )
} ,
2021-09-04 14:55:22 +00:00
"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" )
}
}
2022-07-13 21:53:52 +00:00
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 ) )
} ,
2021-08-09 22:53:27 +00:00
"!" : func ( a ... expression ) expression {
if len ( a ) < 0 {
return exception ( "'!' expects a value, no value was given" )
}
2021-08-11 22:24:10 +00:00
if panicOnException {
panic ( a [ 0 ] )
}
2021-08-12 21:20:02 +00:00
return exception ( String ( a [ 0 ] , false ) )
2021-08-09 22:53:27 +00:00
} ,
2021-07-26 05:31:22 +00:00
"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
} ,
2021-08-09 22:53:27 +00:00
"exception?" : func ( a ... expression ) expression {
if len ( a ) > 0 {
if _ , ok := a [ 0 ] . ( exception ) ; ok {
return true
}
}
return false
} ,
2021-07-26 05:31:22 +00:00
"abs" : func ( a ... expression ) expression {
if len ( a ) < 1 {
2021-08-10 03:16:09 +00:00
return exception ( "'abs' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
i , ok := a [ 0 ] . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'abs' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
if i < 0 {
i = i * - 1
}
return i
} ,
"floor" : func ( a ... expression ) expression {
if len ( a ) < 1 {
2021-08-10 03:16:09 +00:00
return exception ( "'floor' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
i , ok := a [ 0 ] . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'floor' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
return number ( math . Floor ( float64 ( i ) ) )
} ,
"ceil" : func ( a ... expression ) expression {
if len ( a ) < 1 {
2021-08-10 03:16:09 +00:00
return exception ( "'ceil' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
i , ok := a [ 0 ] . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'ceil' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
return number ( math . Ceil ( float64 ( i ) ) )
} ,
2021-08-10 23:09:00 +00:00
"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"
2021-08-15 06:16:29 +00:00
case "func(...main.expression) main.expression" :
return "compiled-procedure"
case "main.proc" :
2021-08-10 23:09:00 +00:00
return "procedure"
case "main.exception" :
return "exception"
2022-06-21 03:22:45 +00:00
case "main.macro" :
return "macro"
2021-11-09 06:50:26 +00:00
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"
2021-08-10 23:09:00 +00:00
default :
return t
}
} ,
2021-07-26 05:31:22 +00:00
"round" : func ( a ... expression ) expression {
if len ( a ) < 1 {
2021-08-10 03:16:09 +00:00
return exception ( "'round' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
decimals := number ( 0.0 )
if len ( a ) >= 2 {
if dec , ok := a [ 1 ] . ( number ) ; ok {
decimals = dec
}
}
num , ok := a [ 0 ] . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'round' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
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 ) )
} ,
2021-07-26 05:31:22 +00:00
"+" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return number ( 0 )
}
v , ok := a [ 0 ] . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'+' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
for _ , i := range a [ 1 : ] {
x , ok := i . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'+' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
v += x
}
return v
} ,
"-" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'-' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
v , ok := a [ 0 ] . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'-' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
if len ( a ) == 1 {
return v * - 1
}
for _ , i := range a [ 1 : ] {
x , ok := i . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'-' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
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" )
2021-07-26 05:31:22 +00:00
}
if len ( a ) == 1 {
return 1 / v
}
for _ , i := range a [ 1 : ] {
x , ok := i . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'*' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
v *= x
}
return v
} ,
"/" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'/' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
v , ok := a [ 0 ] . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'/' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
for _ , i := range a [ 1 : ] {
x , ok := i . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'/' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
v /= x
}
return v
} ,
2022-05-30 16:00:41 +00:00
"%" : 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 ) ) )
} ,
2022-05-30 16:00:41 +00:00
"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 ) ) )
} ,
2021-07-26 05:31:22 +00:00
"min" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'min' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
val := number ( math . MaxFloat64 )
for _ , v := range a {
value , ok := v . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'min' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
if value < val {
val = value
}
}
return val
} ,
"max" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'max' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
val := number ( math . SmallestNonzeroFloat64 )
for _ , v := range a {
value , ok := v . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'max' expected a number, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
if value > val {
val = value
}
}
return val
} ,
"<=" : func ( a ... expression ) expression {
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'<=' expected, but did not receive, two values" )
2021-07-26 05:31:22 +00:00
}
n1 , ok1 := a [ 0 ] . ( number )
2021-07-27 03:19:59 +00:00
n2 , ok2 := a [ 1 ] . ( number )
2021-07-26 05:31:22 +00:00
if ! ok1 || ! ok2 {
2021-08-10 03:16:09 +00:00
return exception ( "'<=' expected numbers, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
return n1 <= n2
} ,
"<" : func ( a ... expression ) expression {
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'<' expected, but did not receive, two values" )
2021-07-26 05:31:22 +00:00
}
n1 , ok1 := a [ 0 ] . ( number )
2021-07-27 03:19:59 +00:00
n2 , ok2 := a [ 1 ] . ( number )
2021-07-26 05:31:22 +00:00
if ! ok1 || ! ok2 {
2021-08-10 03:16:09 +00:00
return exception ( "'<' expected numbers, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
return n1 < n2
} ,
">=" : func ( a ... expression ) expression {
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'>=' expected, but did not receive, two values" )
2021-07-26 05:31:22 +00:00
}
n1 , ok1 := a [ 0 ] . ( number )
2021-07-27 03:19:59 +00:00
n2 , ok2 := a [ 1 ] . ( number )
2021-07-26 05:31:22 +00:00
if ! ok1 || ! ok2 {
2021-08-10 03:16:09 +00:00
return exception ( "'>=' expected numbers, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
return n1 >= n2
} ,
">" : func ( a ... expression ) expression {
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'>' expected, but did not receive, two values" )
2021-07-26 05:31:22 +00:00
}
n1 , ok1 := a [ 0 ] . ( number )
2021-07-27 03:19:59 +00:00
n2 , ok2 := a [ 1 ] . ( number )
2021-07-26 05:31:22 +00:00
if ! ok1 || ! ok2 {
2021-08-10 03:16:09 +00:00
return exception ( "'>' expected numbers, but a non-number value was received" )
2021-07-26 05:31:22 +00:00
}
return n1 > n2
} ,
"equal?" : func ( a ... expression ) expression {
2022-04-12 06:23:21 +00:00
if len ( a ) < 1 {
return exception ( "'equal?' expected, but did not receive, at least one value" )
} else if len ( a ) == 1 {
return true
2021-07-26 05:31:22 +00:00
}
2022-04-12 06:23:21 +00:00
eq := false
for i := 0 ; i < len ( a ) - 1 ; i ++ {
eq = reflect . DeepEqual ( a [ i ] , a [ i + 1 ] )
if ! eq {
return false
}
}
return true
2021-07-26 05:31:22 +00:00
} ,
2021-07-31 06:31:08 +00:00
"atom?" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'atom?' expected a value, but no value was given" )
2021-07-31 06:31:08 +00:00
}
switch a [ 0 ] . ( type ) {
case [ ] expression :
return false
default :
return true
}
} ,
2021-08-10 03:16:09 +00:00
"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
} ,
2022-12-21 04:32:40 +00:00
"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 {
2021-08-10 03:16:09 +00:00
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" )
2021-08-10 03:16:09 +00:00
}
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" )
2021-08-10 03:16:09 +00:00
}
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" )
2021-08-10 03:16:09 +00:00
}
if reflect . DeepEqual ( l [ 0 ] , a [ 1 ] ) {
2021-08-24 05:49:20 +00:00
if len ( a ) >= 3 {
2022-07-09 05:23:09 +00:00
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
}
2021-08-10 03:16:09 +00:00
return l [ 1 ]
}
}
2021-08-24 05:49:20 +00:00
if len ( a ) >= 3 {
2022-07-09 05:23:09 +00:00
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
}
2021-08-10 03:16:09 +00:00
}
2022-05-01 02:48:37 +00:00
return exception ( "'assoc' could not find the key: " + String ( a [ 1 ] , true ) )
2021-08-10 03:16:09 +00:00
} ,
2021-07-26 05:31:22 +00:00
"number?" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'number?' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
switch a [ 0 ] . ( type ) {
case float64 , number :
return true
default :
return false
}
} ,
2022-07-07 21:31:01 +00:00
"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
} ,
2021-07-26 05:31:22 +00:00
"string?" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'string?' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
switch a [ 0 ] . ( type ) {
case string :
return true
default :
return false
}
} ,
2021-08-17 22:56:26 +00:00
"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
}
} ,
2021-07-26 05:31:22 +00:00
"bool?" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'bool?' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
switch a [ 0 ] . ( type ) {
case bool :
return true
default :
return false
}
} ,
"symbol?" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'symbol?' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
switch a [ 0 ] . ( type ) {
case symbol :
return true
default :
return false
}
} ,
2021-07-31 06:31:08 +00:00
"null?" : func ( a ... expression ) expression {
2021-07-27 03:19:59 +00:00
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'null?' expected a list, but no value was given" )
2021-07-27 03:19:59 +00:00
}
switch i := a [ 0 ] . ( type ) {
case [ ] expression :
return len ( i ) == 0
default :
2021-07-31 06:31:08 +00:00
return false
2021-07-27 03:19:59 +00:00
}
} ,
2021-07-26 05:31:22 +00:00
"pair?" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'pair?' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
2021-07-31 20:25:20 +00:00
switch i := a [ 0 ] . ( type ) {
2021-07-31 06:31:08 +00:00
case [ ] expression :
return len ( i ) > 0
default :
return false
}
} ,
"list?" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'list?' expected a value, but no value was given" )
2021-07-31 06:31:08 +00:00
}
2021-07-26 05:31:22 +00:00
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
}
} ,
2021-07-26 05:31:22 +00:00
"procedure?" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'proc?' expected a value, but no value was given" )
2021-07-26 05:31:22 +00:00
}
switch a [ 0 ] . ( type ) {
case proc , func ( a ... expression ) expression :
return true
default :
return false
}
} ,
2021-07-31 20:25:20 +00:00
"length" : func ( a ... expression ) expression {
2021-07-26 05:31:22 +00:00
switch i := a [ 0 ] . ( type ) {
case [ ] expression :
return number ( len ( i ) )
2021-08-08 03:03:46 +00:00
case string :
2021-09-04 23:02:58 +00:00
return number ( len ( [ ] rune ( String ( i , false ) ) ) )
2021-08-09 22:53:27 +00:00
case exception :
2021-09-04 23:02:58 +00:00
return number ( len ( [ ] rune ( String ( string ( i ) , false ) ) ) )
2021-08-10 23:09:00 +00:00
case * IOHandle :
switch h := i . Obj . ( type ) {
case * strings . Builder :
2021-09-04 23:02:58 +00:00
return number ( len ( [ ] rune ( String ( h . String ( ) , false ) ) ) )
2021-08-10 23:09:00 +00:00
default :
return exception ( "'length' is not defined for a non-string-buf IOHandle" )
}
2021-07-26 05:31:22 +00:00
default :
2021-08-10 03:16:09 +00:00
return exception ( "'length' expected a list or a string, but the given value was not a list or a string" )
2021-07-26 05:31:22 +00:00
}
} ,
2021-08-10 23:09:00 +00:00
"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 ) )
2021-08-10 23:09:00 +00:00
}
2021-08-24 21:04:47 +00:00
obj := & IOHandle { & b , true , "string-buf" }
2021-08-10 23:09:00 +00:00
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 )
} ,
2021-07-29 22:36:11 +00:00
"string-format" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "no value was given to 'string-format'" )
2021-07-29 22:36:11 +00:00
}
format , ok := a [ 0 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'string-format' expected a format string and was given a non-string value as its first argument" )
2021-07-29 22:36:11 +00:00
}
if len ( a ) == 1 {
return format
}
2021-08-16 05:45:05 +00:00
var outputBuf , percentBuf strings . Builder
parsePercent := false
varNum := 1
for _ , c := range format {
if parsePercent {
if c == 'v' {
parsePercent = false
if varNum < len ( a ) {
s , err := formatValue ( percentBuf . String ( ) , a [ varNum ] )
if err != nil {
return exception ( "'string-format' was given an invalid formatting specifier" )
}
outputBuf . WriteString ( s )
percentBuf . Reset ( )
2021-08-16 05:52:33 +00:00
varNum ++
2021-08-16 05:45:05 +00:00
} else {
return exception ( "'string-format' was not given enough values to format the string" )
}
} else {
percentBuf . WriteRune ( c )
}
2021-09-06 22:54:47 +00:00
} else if c == '%' {
2021-08-16 05:45:05 +00:00
parsePercent = true
} else if c == '%' {
outputBuf . WriteRune ( c )
} else {
outputBuf . WriteRune ( c )
}
2021-07-29 22:36:11 +00:00
}
2021-08-16 05:45:05 +00:00
2021-09-06 22:54:47 +00:00
return unescapeString ( outputBuf . String ( ) )
2021-07-29 22:36:11 +00:00
} ,
2021-08-08 03:03:46 +00:00
"string-index-of" : func ( a ... expression ) expression {
// (string-index-of [haystack] [needle])
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'string-index-of' expects two strings, a haystack and a needle, insufficient arguments were given" )
2021-08-08 03:03:46 +00:00
}
haystack , ok := a [ 0 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'string-index-of' expects a string as its first argument; a non-string value was given" )
2021-08-08 03:03:46 +00:00
}
needle , ok := a [ 1 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'string-index-of' expects a string as its second argument; a non-string value was given" )
2021-08-08 03:03:46 +00:00
}
return number ( strings . Index ( haystack , needle ) )
} ,
"string-ref" : func ( a ... expression ) expression {
2022-05-09 21:28:10 +00:00
// TODO remove this proc entirely someday?
return globalenv . vars [ "ref" ] . ( func ( ... expression ) expression ) ( a ... )
2021-08-08 03:03:46 +00:00
} ,
2021-07-26 05:31:22 +00:00
"cons" : func ( a ... expression ) expression {
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( fmt . Sprintf ( "'cons' expected two arguments, %d given" , len ( a ) ) )
2021-07-26 05:31:22 +00:00
}
switch car := a [ 0 ] ; cdr := a [ 1 ] . ( type ) {
case [ ] expression :
return append ( [ ] expression { car } , cdr ... )
default :
return [ ] expression { car , cdr }
}
} ,
"car" : func ( a ... expression ) expression {
if len ( a ) < 1 {
2021-08-10 03:16:09 +00:00
return exception ( fmt . Sprintf ( "%q expected a list with at least one item in it, but a nil value was given" , "car" ) )
2021-07-26 05:31:22 +00:00
}
switch i := a [ 0 ] . ( type ) {
case [ ] expression :
return a [ 0 ] . ( [ ] expression ) [ 0 ]
default :
2021-08-10 03:16:09 +00:00
return exception ( fmt . Sprintf ( "%q expected a list, but a %T was given" , "car" , i ) )
2021-07-26 05:31:22 +00:00
}
} ,
"cdr" : func ( a ... expression ) expression {
if len ( a ) < 1 {
2021-08-10 03:16:09 +00:00
return exception ( "\"cdr\" expected a list with at least one item in it, but a nil value was given" )
2021-07-26 05:31:22 +00:00
}
switch i := a [ 0 ] . ( type ) {
case [ ] expression :
return a [ 0 ] . ( [ ] expression ) [ 1 : ]
default :
2021-08-10 03:16:09 +00:00
return exception ( fmt . Sprintf ( "%q expected a list, but a %T was given" , "car" , i ) )
2021-07-26 05:31:22 +00:00
}
} ,
2021-07-29 03:47:05 +00:00
"list->string" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "insufficient number of arguments given to 'list->string', expected a list and a string" )
2021-07-29 03:47:05 +00:00
}
listIn , ok := a [ 0 ] . ( [ ] expression )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'list->string' expected a list, but was given a non-list value" )
2021-07-29 03:47:05 +00:00
}
joinOn := ""
if len ( a ) >= 2 {
switch sp := a [ 1 ] . ( type ) {
case string :
joinOn = sp
2021-08-09 22:53:27 +00:00
case exception :
joinOn = string ( sp )
2021-07-29 03:47:05 +00:00
case symbol :
joinOn = string ( sp )
case number :
joinOn = strconv . Itoa ( int ( sp ) )
case [ ] expression :
2021-08-12 21:20:02 +00:00
joinOn = String ( sp , false )
2021-07-29 03:47:05 +00:00
default :
2021-08-10 03:16:09 +00:00
return exception ( "'list->string' could not convert the given join point to a string upon which to join" )
2021-07-29 03:47:05 +00:00
}
}
sSlice := make ( [ ] string , len ( listIn ) )
for i := range listIn {
2021-09-04 14:45:35 +00:00
sSlice [ i ] = String ( listIn [ i ] , false )
2021-07-29 03:47:05 +00:00
}
return strings . Join ( sSlice , joinOn )
} ,
2022-06-03 21:01:06 +00:00
"range" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return make ( [ ] expression , 0 )
}
start := number ( 0 )
step := number ( 1 )
count , ok := a [ 0 ] . ( number )
if ! ok {
return exception ( "'range' expected a number representing a count, a non-number value was given" )
}
if int ( count ) < 1 {
return exception ( "'range' expected a number greater than zero as its first argument, an invalid value was given" )
}
if len ( a ) > 1 {
start , ok = a [ 1 ] . ( number )
if ! ok {
return exception ( "'range' expected a number representing a starting point as its second argument, a non-number value was given" )
}
}
if len ( a ) > 2 {
step , ok = a [ 2 ] . ( number )
if ! ok {
return exception ( "'range' expected a number representing a step count as its third argument, a non-number value was given" )
}
}
out := make ( [ ] expression , 0 , int ( count ) )
for ; count > 0 ; count -- {
out = append ( out , start )
start += step
}
return out
} ,
2021-09-06 22:40:15 +00:00
"list-seed" : func ( a ... expression ) expression {
if len ( a ) < 2 {
return exception ( "insufficient number of arguments given to 'list-seed', expected number and value" )
}
count , ok := a [ 0 ] . ( number )
if ! ok {
return exception ( "'list-seed' expected a number as its first argument, but was given a non-number value" )
}
if count < 1 {
return exception ( "'list-seed' expects a positive non-zero number as its first argument, a number less than 1 was given" )
}
l := make ( [ ] expression , int ( count ) )
2021-09-28 21:00:58 +00:00
switch t := a [ 1 ] . ( type ) {
case [ ] expression :
for i := range l {
s := make ( [ ] expression , len ( t ) )
copy ( s , t )
l [ i ] = s
}
default :
for i := range l {
l [ i ] = t
}
2021-09-06 22:40:15 +00:00
}
return l
} ,
2021-09-04 14:45:35 +00:00
"list-sort" : func ( a ... expression ) expression {
2021-08-12 21:20:02 +00:00
if len ( a ) == 0 {
return exception ( "insufficient number of arguments given to 'list-sort', expected a list" )
}
listIn , ok := a [ 0 ] . ( [ ] expression )
if ! ok {
return exception ( "'list-sort' expected a list, but was given a non-list value" )
}
2021-08-12 22:53:43 +00:00
if len ( a ) > 1 {
n , ok := a [ 1 ] . ( number )
if ! ok {
return exception ( "'list-sort' expected a number as its optional second argument, but a non-number was given" )
}
return MergeSort ( listIn , int ( n ) )
}
2021-09-04 14:45:35 +00:00
return MergeSort ( listIn , - 1 )
} ,
2022-05-10 03:41:57 +00:00
"list-join" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return make ( [ ] expression , 0 )
}
base , ok := a [ 0 ] . ( [ ] expression )
if ! ok {
return exception ( "'list-join' expected a list but was given a non-list value" )
}
2022-11-09 05:16:07 +00:00
out := DeepCopySlice ( base ) . ( [ ] expression )
2022-05-10 03:41:57 +00:00
for _ , v := range a [ 1 : ] {
val , ok2 := v . ( [ ] expression )
if ! ok2 {
return exception ( "'list-join' expected a list but was given a non-list value" )
}
2022-11-09 05:16:07 +00:00
out = append ( out , val ... )
2022-05-10 03:41:57 +00:00
}
2022-11-09 05:16:07 +00:00
return out
2022-05-10 03:41:57 +00:00
} ,
2021-07-26 05:31:22 +00:00
"append" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return make ( [ ] expression , 0 )
}
2022-05-09 21:54:28 +00:00
switch base := a [ 0 ] . ( type ) {
case [ ] expression :
2022-07-09 05:23:09 +00:00
base = append ( base , a [ 1 : ] ... )
2022-05-09 21:54:28 +00:00
return base
default :
var out strings . Builder
for i := range a {
out . WriteString ( String ( a [ i ] , false ) )
2021-07-26 05:31:22 +00:00
}
2022-05-09 21:54:28 +00:00
return out . String ( )
2021-07-26 05:31:22 +00:00
}
} ,
2022-05-09 21:28:10 +00:00
"ref" : func ( a ... expression ) expression {
2021-07-31 06:31:08 +00:00
if len ( a ) < 2 {
2022-05-09 21:28:10 +00:00
return exception ( "'ref' expects at least two arguments, a string|list and an index number, insufficient arguments were given" )
2021-07-31 06:31:08 +00:00
}
2022-05-09 21:28:10 +00:00
2022-09-06 21:50:39 +00:00
switch t := a [ 1 ] . ( type ) {
case number :
i := int ( t )
switch v := a [ 0 ] . ( type ) {
case [ ] expression :
2022-09-10 20:46:56 +00:00
if i >= len ( v ) {
2022-09-06 21:50:39 +00:00
return exception ( "'ref' was given an index number that is out of bounds for the given list" )
2022-09-10 20:46:56 +00:00
} else if i < 0 {
i = len ( v ) + i
if i < 0 {
return exception ( "'ref' was given an index number that is out of bounds for the given list" )
}
2022-07-09 05:23:09 +00:00
}
2022-09-06 21:50:39 +00:00
if len ( a ) >= 3 {
if len ( a ) > 3 && AnythingToBool ( a [ 3 ] ) . ( bool ) {
v [ int ( i ) ] = a [ 2 ]
return v
} else {
cpy := DeepCopySlice ( v )
cpy . ( [ ] expression ) [ int ( i ) ] = a [ 2 ]
return cpy
}
}
return v [ int ( i ) ]
case string :
2022-09-10 20:46:56 +00:00
if i >= len ( v ) {
return exception ( "'ref' was given an index number that is out of bounds for the given list" )
} else if i < 0 {
i = len ( v ) + i
if i < 0 {
return exception ( "'ref' was given an index number that is out of bounds for the given list" )
}
2022-09-06 21:50:39 +00:00
}
if len ( a ) >= 3 {
s := String ( a [ 2 ] , false )
if len ( s ) > 0 {
return v [ : i ] + s + v [ i + 1 : ]
} else {
return v [ : i ] + v [ i + 1 : ]
}
}
return string ( v [ int ( i ) ] )
default :
return exception ( "'ref' was given a non-string non-list value" )
2022-05-09 21:28:10 +00:00
}
2022-09-06 21:50:39 +00:00
case [ ] expression :
switch v := a [ 0 ] . ( type ) {
case [ ] expression :
if len ( t ) == 0 {
return exception ( "'ref' was given an index list that is empty" )
}
var val expression
var copyList bool = ! ( len ( a ) > 3 && AnythingToBool ( a [ 3 ] ) . ( bool ) )
var newList expression
if copyList {
newList = DeepCopySlice ( v )
val = newList
2022-05-09 21:28:10 +00:00
} else {
2022-09-06 21:50:39 +00:00
val = v
}
for count , i := range t {
if list , ok := val . ( [ ] expression ) ; ok {
if num , ok := i . ( number ) ; ok {
2022-09-10 20:46:56 +00:00
if int ( num ) >= len ( list ) {
2022-09-06 21:50:39 +00:00
return exception ( "'ref' was given a list of indexes containing an out of bounds reference" )
2022-09-10 20:46:56 +00:00
} else if int ( num ) < 0 {
num = number ( len ( list ) + int ( num ) )
if int ( num ) < 0 {
return exception ( "'ref' was given a list of indexes containing an out of bounds reference" )
}
2022-09-06 21:50:39 +00:00
}
val = list [ int ( num ) ]
if len ( a ) >= 3 && count == len ( t ) - 1 {
list [ int ( num ) ] = a [ 2 ]
if ! copyList {
return v
} else {
return newList
}
}
} else {
return exception ( "'ref' was given a list of indexes containing a non-number value" )
}
} else {
return exception ( "'ref' was given a list of indexes that does not correspond to the depth of the given list" )
}
}
return val
case string :
if len ( t ) == 0 {
2022-09-10 20:46:56 +00:00
return exception ( "'ref' was given an empty string, which cannot be indexed" )
2022-09-06 21:50:39 +00:00
}
i := int ( t [ 0 ] . ( number ) )
2022-09-10 20:46:56 +00:00
if i >= len ( v ) {
return exception ( "'ref' was given an index number that is out of bounds for the given list" )
} else if i < 0 {
i = len ( v ) + i
if i < 0 {
return exception ( "'ref' was given an index number that is out of bounds for the given list" )
}
2022-09-06 21:50:39 +00:00
}
if len ( a ) >= 3 {
s := String ( a [ 2 ] , false )
if len ( s ) > 0 {
return v [ : i ] + s + v [ i + 1 : ]
} else {
return v [ : i ] + v [ i + 1 : ]
}
2022-05-09 21:28:10 +00:00
}
2022-09-06 21:50:39 +00:00
return string ( v [ int ( i ) ] )
default :
return exception ( "'ref' was given a non-string non-list value" )
2022-05-09 21:28:10 +00:00
}
default :
2022-09-06 21:50:39 +00:00
return exception ( "'ref' expects argument two to be an index number or a list of index numbers (for sublists), a non-number/non-list argument was given" )
2021-08-01 22:38:40 +00:00
}
2022-09-06 21:50:39 +00:00
2022-05-09 21:28:10 +00:00
} ,
2023-05-09 16:25:56 +00:00
"list" : func ( a ... expression ) expression {
return a
} ,
2022-05-09 21:28:10 +00:00
"list-ref" : func ( a ... expression ) expression {
// TODO remove this proc entirely someday?
return globalenv . vars [ "ref" ] . ( func ( ... expression ) expression ) ( a ... )
2021-07-31 06:31:08 +00:00
} ,
2021-08-10 03:16:09 +00:00
"member?" : func ( a ... expression ) expression {
if len ( a ) < 2 {
return exception ( "'member?' expects a list and a value to search for, insufficient arguments were given" )
}
list , ok := a [ 0 ] . ( [ ] expression )
if ! ok {
return exception ( "'member?' expects argument one to be a list, a non-list argument was given" )
}
for i := range list {
if reflect . DeepEqual ( list [ i ] , a [ 1 ] ) {
return true
}
}
return false
} ,
2021-07-31 06:31:08 +00:00
"reverse" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return make ( [ ] expression , 0 )
}
switch l := a [ 0 ] . ( type ) {
case [ ] expression :
out := make ( [ ] expression , len ( l ) )
for i , n := range l {
2021-07-31 20:25:20 +00:00
j := len ( l ) - i - 1
2021-07-31 06:31:08 +00:00
out [ j ] = n
}
return out
2021-08-11 02:57:57 +00:00
case string :
var b strings . Builder
r := [ ] rune ( l )
2021-09-04 14:45:35 +00:00
for i := len ( r ) - 1 ; i >= 0 ; i -- {
2021-08-11 02:57:57 +00:00
b . WriteRune ( r [ i ] )
}
return b . String ( )
2021-07-31 06:31:08 +00:00
default :
2021-08-10 03:16:09 +00:00
return exception ( "'reverse' expected a list but was given a non-list value" )
2021-07-31 06:31:08 +00:00
}
} ,
2021-07-26 05:31:22 +00:00
"not" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'not' expects a value but no value was given" )
2021-07-26 05:31:22 +00:00
}
if i , ok := a [ 0 ] . ( bool ) ; ok && ! i {
return true
}
return false
} ,
2021-07-29 22:36:11 +00:00
"~bool" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "`~bool` expects a value, but n value was given" )
2021-07-29 22:36:11 +00:00
}
switch v := a [ 0 ] . ( type ) {
case string :
if v == "" {
return false
}
return true
case number :
if v == 0 {
return false
}
return true
case * IOHandle :
2021-08-11 01:41:32 +00:00
return v . Open
2021-07-29 22:36:11 +00:00
case [ ] expression :
if len ( v ) > 0 {
return true
}
return false
case bool :
return v
default :
return true
}
} ,
2022-05-25 16:33:21 +00:00
"filter" : func ( a ... expression ) expression {
if len ( a ) < 2 {
return exception ( "'filter' expects two arguments: a procedure and an argument list, too few arguments were given" )
}
switch p := a [ 0 ] . ( type ) {
2022-06-22 03:58:29 +00:00
case proc , func ( ... expression ) expression , macro :
2022-05-25 16:33:21 +00:00
list , ok := a [ 1 ] . ( [ ] expression )
if ! ok {
return exception ( "'filter' expects a list as its second argument, a non-list value was given" )
}
out := make ( [ ] expression , 0 , len ( list ) )
for i := range list {
2022-06-22 03:58:29 +00:00
if AnythingToBool ( apply ( p , [ ] expression { list [ i ] } , "Filter routine" ) ) . ( bool ) {
2022-05-25 16:33:21 +00:00
out = append ( out , list [ i ] )
}
}
return out
default :
return exception ( "filter expects a procedure followed by a list" )
}
} ,
2021-07-26 05:31:22 +00:00
"map" : func ( a ... expression ) expression {
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'map' expects a procedure followed by at least one list" )
2021-07-26 05:31:22 +00:00
}
ml , err := MergeLists ( a [ 1 : ] )
if err != nil {
2021-08-10 03:16:09 +00:00
return exception ( "map error: " + err . Error ( ) )
2021-07-26 05:31:22 +00:00
}
mergedLists := ml . ( [ ] expression )
switch i := a [ 0 ] . ( type ) {
case proc , func ( ... expression ) expression :
out := make ( [ ] expression , 0 , len ( mergedLists ) )
for merge := range mergedLists {
2022-06-22 03:58:29 +00:00
out = append ( out , apply ( i , mergedLists [ merge ] . ( [ ] expression ) , "Map routine" ) )
2021-07-26 05:31:22 +00:00
}
return out
default :
2021-08-10 03:16:09 +00:00
return exception ( "map expects a procedure followed by at least one list" )
2021-07-26 05:31:22 +00:00
}
} ,
2022-05-24 21:05:46 +00:00
"reduce" : func ( a ... expression ) expression {
if len ( a ) < 3 {
return exception ( "'reduce' expects a procedure followed by an initializer value and a list, too few arguments given" )
}
switch p := a [ 0 ] . ( type ) {
case proc , func ( ... expression ) expression :
init := a [ 1 ]
list , ok := a [ 2 ] . ( [ ] expression )
if ! ok {
return exception ( "'reduce' expects a list as its third argument, a non-list value was given" )
}
for i := range list {
2022-06-22 03:58:29 +00:00
init = apply ( p , [ ] expression { list [ i ] , init } , "Reduce routine" )
2022-05-24 21:05:46 +00:00
}
return init
default :
return exception ( "'reduce' expects a procedure as its first argument, a non-procedure value was given" )
}
} ,
2021-07-26 05:31:22 +00:00
"for-each" : func ( a ... expression ) expression {
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "for-each expects a procedure followed by at least one list" )
2021-07-26 05:31:22 +00:00
}
ml , err := MergeLists ( a [ 1 : ] )
if err != nil {
2021-08-10 03:16:09 +00:00
return exception ( "for-each error: " + err . Error ( ) )
2021-07-26 05:31:22 +00:00
}
mergedLists := ml . ( [ ] expression )
switch i := a [ 0 ] . ( type ) {
case proc , func ( ... expression ) expression :
for merge := range mergedLists {
2022-06-22 03:58:29 +00:00
apply ( i , mergedLists [ merge ] . ( [ ] expression ) , "For-each routine" )
2021-07-26 05:31:22 +00:00
}
return make ( [ ] expression , 0 )
default :
2021-08-10 03:16:09 +00:00
return exception ( "for-each expects a procedure followed by at least one list" )
2021-07-26 05:31:22 +00:00
}
} ,
"display" : func ( a ... expression ) expression {
var out strings . Builder
for i := range a {
2021-09-04 14:45:35 +00:00
out . WriteString ( String ( a [ i ] , false ) )
2021-07-26 05:31:22 +00:00
}
2022-04-28 05:59:02 +00:00
SysoutPrint ( out . String ( ) , Sysout )
2021-07-26 05:31:22 +00:00
return make ( [ ] expression , 0 )
} ,
2021-07-27 16:33:06 +00:00
"display-lines" : func ( a ... expression ) expression {
var out strings . Builder
for i := range a {
switch val := a [ i ] . ( type ) {
2021-08-11 01:04:46 +00:00
case * IOHandle :
2021-08-11 01:42:35 +00:00
buf , ok := val . Obj . ( * strings . Builder )
2021-08-11 01:04:46 +00:00
if ok {
out . WriteString ( buf . String ( ) )
} else {
2021-08-12 21:20:02 +00:00
out . WriteString ( String ( val , true ) )
2021-08-11 01:04:46 +00:00
}
out . WriteRune ( '\n' )
2021-07-27 16:33:06 +00:00
default :
2021-08-12 21:20:02 +00:00
out . WriteString ( String ( val , false ) )
2021-07-27 16:33:06 +00:00
out . WriteRune ( '\n' )
}
}
2022-04-28 05:59:02 +00:00
SysoutPrint ( out . String ( ) , Sysout )
2021-07-27 16:33:06 +00:00
return make ( [ ] expression , 0 )
} ,
2021-07-26 05:31:22 +00:00
"write" : func ( a ... expression ) expression {
if len ( a ) == 1 {
2022-04-28 05:59:02 +00:00
SysoutPrint ( String ( a [ 0 ] , false ) , Sysout )
2021-08-15 03:02:54 +00:00
return make ( [ ] expression , 0 )
} else if len ( a ) > 1 {
2021-09-04 14:45:35 +00:00
stringOut := String ( a [ 0 ] , false )
2021-07-27 16:33:06 +00:00
obj , ok := a [ 1 ] . ( * IOHandle )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'write' expected an IO handle as its second argument, but was not given an IO handle" )
2021-07-27 16:33:06 +00:00
}
if ! obj . Open {
2021-08-10 03:16:09 +00:00
return exception ( "'write' was given an IO handle that is already closed" )
2021-07-27 16:33:06 +00:00
}
switch ft := obj . Obj . ( type ) {
2021-07-26 05:31:22 +00:00
case * os . File :
ft . WriteString ( stringOut )
2021-07-27 22:35:00 +00:00
case * net . Conn :
( * ft ) . Write ( [ ] byte ( stringOut ) )
2021-07-28 05:45:52 +00:00
case * tls . Conn :
ft . Write ( [ ] byte ( stringOut ) )
2021-08-10 23:09:00 +00:00
case * strings . Builder :
ft . WriteString ( stringOut )
2021-07-26 05:31:22 +00:00
default :
2021-08-13 16:34:42 +00:00
return exception ( "'write' was given an IO handle that is not supported" )
2021-07-26 05:31:22 +00:00
}
2021-08-10 23:09:00 +00:00
return a [ 1 ] . ( * IOHandle )
2021-07-26 05:31:22 +00:00
}
2021-08-15 03:02:54 +00:00
return exception ( "'write' received insufficient arguments" )
2021-07-26 05:31:22 +00:00
} ,
2022-07-07 21:31:01 +00:00
"write-bytes" : func ( a ... expression ) expression {
if len ( a ) == 1 {
a = append ( a , & IOHandle { os . Stdout , true , "file (write-only)" } )
}
obj , ok := a [ 1 ] . ( * IOHandle )
if ! ok {
return exception ( "'write-bytes' expected an IO handle as its argument, but was not given an IO handle" )
}
if ! obj . Open {
return exception ( "'write-bytes' was given an IO handle that is already closed" )
}
switch val := a [ 0 ] . ( type ) {
case number :
n := byte ( val )
if n < 0 || n > 255 {
return exception ( "'write-bytes' was given a number that is not a byte" )
}
switch ft := obj . Obj . ( type ) {
case * os . File :
ft . Write ( [ ] byte { n } )
case * net . Conn :
( * ft ) . Write ( [ ] byte { n } )
case * tls . Conn :
ft . Write ( [ ] byte { n } )
case * strings . Builder :
ft . Write ( [ ] byte { n } )
default :
return false
}
return a [ 1 ] . ( * IOHandle )
case [ ] expression :
bys := make ( [ ] byte , 0 , len ( val ) )
for _ , v := range val {
n , ok := v . ( number )
if ! ok {
return exception ( "'write-bytes' was given a non-number value (inside of a list)" )
}
if n < 0 || n > 255 {
return exception ( "'write-bytes' was given a number that is not a byte" )
}
bys = append ( bys , byte ( n ) )
}
switch ft := obj . Obj . ( type ) {
case * os . File :
ft . Write ( bys )
case * net . Conn :
( * ft ) . Write ( bys )
case * tls . Conn :
ft . Write ( bys )
case * strings . Builder :
ft . Write ( bys )
default :
return false
}
return a [ 1 ] . ( * IOHandle )
}
return make ( [ ] expression , 0 )
} ,
2021-07-27 03:19:59 +00:00
"write-raw" : func ( a ... expression ) expression {
if len ( a ) == 1 {
2022-04-28 05:59:02 +00:00
SysoutPrint ( String ( a [ 0 ] , true ) , Sysout )
2021-07-27 16:33:06 +00:00
} else if len ( a ) >= 2 {
obj , ok := a [ 1 ] . ( * IOHandle )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'write-raw' expected an IO handle as its argument, but was not given an IO handle" )
2021-07-27 16:33:06 +00:00
}
if ! obj . Open {
2021-08-10 03:16:09 +00:00
return exception ( "'write-raw' was given an IO handle that is already closed" )
2021-07-27 16:33:06 +00:00
}
switch ft := obj . Obj . ( type ) {
2021-07-27 03:19:59 +00:00
case * os . File :
2021-08-12 21:20:02 +00:00
ft . WriteString ( String ( a [ 0 ] , true ) )
2021-07-27 22:35:00 +00:00
case * net . Conn :
2021-08-12 21:20:02 +00:00
( * ft ) . Write ( [ ] byte ( String ( a [ 0 ] , true ) ) )
2021-07-28 05:45:52 +00:00
case * tls . Conn :
2021-08-12 21:20:02 +00:00
ft . Write ( [ ] byte ( [ ] byte ( String ( a [ 0 ] , true ) ) ) )
2021-08-10 23:09:00 +00:00
case * strings . Builder :
2021-08-12 21:20:02 +00:00
ft . WriteString ( String ( a [ 0 ] , true ) )
2021-07-27 03:19:59 +00:00
default :
return false
}
2021-08-10 23:09:00 +00:00
return a [ 1 ] . ( * IOHandle )
2021-07-27 03:19:59 +00:00
}
return make ( [ ] expression , 0 )
} ,
2021-07-27 22:35:00 +00:00
"read-all" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2022-05-08 16:14:14 +00:00
text , err := ioutil . ReadAll ( os . Stdin )
2021-07-27 22:35:00 +00:00
if err != nil {
return false
}
2022-05-08 16:14:14 +00:00
return string ( text )
2021-07-27 22:35:00 +00:00
} else {
obj , ok := a [ 0 ] . ( * IOHandle )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'read-all' expected an IO handle as its argument, but was not given an IO handle" )
2021-07-27 22:35:00 +00:00
}
if ! obj . Open {
2021-08-10 03:16:09 +00:00
return exception ( "'read-all' was given an IO handle that is already closed" )
2021-07-27 22:35:00 +00:00
}
switch f := obj . Obj . ( type ) {
case * os . File :
b , err := ioutil . ReadAll ( f )
if err != nil {
2021-08-13 16:34:42 +00:00
return exception ( "'read-all' could not read from the given IOHandle" )
2021-07-27 22:35:00 +00:00
}
return string ( b )
case * net . Conn :
b , err := ioutil . ReadAll ( * f )
if err != nil {
2021-08-13 16:34:42 +00:00
return exception ( "'read-all' could not read from the given IOHandle" )
2021-07-27 22:35:00 +00:00
}
return string ( b )
2021-07-28 05:45:52 +00:00
case * tls . Conn :
b , err := ioutil . ReadAll ( f )
if err != nil {
2021-08-13 16:34:42 +00:00
return exception ( "'read-all' could not read from the given IOHandle" )
2021-07-28 05:45:52 +00:00
}
return string ( b )
2021-08-10 23:09:00 +00:00
case * strings . Builder :
return f . String ( )
2021-07-27 22:35:00 +00:00
default :
2021-08-10 03:16:09 +00:00
return exception ( "'read-all' has not been implemented for this object type" )
2021-07-28 05:45:52 +00:00
}
}
} ,
2022-07-07 21:31:01 +00:00
"read-all-bytes" : func ( a ... expression ) expression {
if len ( a ) == 0 {
a = append ( a , & IOHandle { os . Stdin , true , "file (read-only)" } )
}
obj , ok := a [ 0 ] . ( * IOHandle )
if ! ok {
return exception ( "'read-all-bytes' expected an IO handle as its argument, but was not given an IO handle" )
}
if ! obj . Open {
return exception ( "'read-all-bytes' was given an IO handle that is already closed" )
}
switch f := obj . Obj . ( type ) {
case * os . File :
b , err := ioutil . ReadAll ( f )
if err != nil {
return exception ( "'read-all-bytes' could not read from the given IOHandle" )
}
return ByteSliceToExpressionSlice ( b )
case * net . Conn :
b , err := ioutil . ReadAll ( * f )
if err != nil {
return exception ( "'read-all-bytes' could not read from the given IOHandle" )
}
return ByteSliceToExpressionSlice ( b )
case * tls . Conn :
b , err := ioutil . ReadAll ( f )
if err != nil {
return exception ( "'read-all-bytes' could not read from the given IOHandle" )
}
return ByteSliceToExpressionSlice ( b )
case * strings . Builder :
return ByteSliceToExpressionSlice ( [ ] byte ( f . String ( ) ) )
default :
return exception ( "'read-all-bytes' has not been implemented for this object type" )
}
} ,
2021-07-28 05:45:52 +00:00
"read-all-lines" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2022-05-08 20:52:16 +00:00
a = append ( a , & IOHandle { os . Stdin , true , "file (read-only)" } )
}
obj , ok := a [ 0 ] . ( * IOHandle )
if ! ok {
return exception ( "'read-all-lines' expected an IO handle as its argument, but was not given an IO handle" )
}
if ! obj . Open {
return exception ( "'read-all-lines' was given an IO handle that is already closed" )
}
switch f := obj . Obj . ( type ) {
case * os . File :
b , err := ioutil . ReadAll ( f )
2021-07-28 05:45:52 +00:00
if err != nil {
2022-05-08 20:52:16 +00:00
return exception ( "'read-all-lines' could not read from the given IOHandle" )
2021-07-28 05:45:52 +00:00
}
2022-05-08 20:52:16 +00:00
o := strings . SplitN ( string ( b ) , "\n" , - 1 )
return StringSliceToExpressionSlice ( o ) . ( [ ] expression )
case * net . Conn :
b , err := ioutil . ReadAll ( * f )
if err != nil {
return exception ( "'read-all-lines' could not read from the given IOHandle" )
2021-07-28 05:45:52 +00:00
}
2022-05-08 20:52:16 +00:00
o := strings . SplitN ( string ( b ) , "\n" , - 1 )
return StringSliceToExpressionSlice ( o ) . ( [ ] expression )
case * tls . Conn :
b , err := ioutil . ReadAll ( f )
if err != nil {
return exception ( "'read-all-lines' could not read from the given IOHandle" )
2021-07-27 22:35:00 +00:00
}
2022-05-08 20:52:16 +00:00
o := strings . SplitN ( string ( b ) , "\n" , - 1 )
return StringSliceToExpressionSlice ( o ) . ( [ ] expression )
case * strings . Builder :
s := f . String ( )
o := strings . SplitN ( s , "\n" , - 1 )
return StringSliceToExpressionSlice ( o ) . ( [ ] expression )
default :
2022-07-07 21:31:01 +00:00
return exception ( "'read-all-lines' has not been implemented for this object type" )
}
} ,
2022-07-09 02:48:02 +00:00
"read-bytes" : func ( a ... expression ) expression {
2022-07-07 21:31:01 +00:00
if len ( a ) == 0 {
reader := bufio . NewReader ( os . Stdin )
b , err := reader . ReadByte ( )
if err != nil {
2022-07-09 02:48:02 +00:00
return exception ( "'read-bytes' could not read from stdin" )
2022-07-07 21:31:01 +00:00
}
return number ( b )
} else {
obj , ok := a [ 0 ] . ( * IOHandle )
if ! ok {
return exception ( "'read-char' expected an IO handle as its argument, but was not given an IO handle" )
}
if ! obj . Open {
return exception ( "'read-char' was given an IO handle that is already closed" )
}
count := 1
if len ( a ) > 1 {
c , ok := a [ 1 ] . ( number )
if ! ok {
2022-07-09 02:48:02 +00:00
return exception ( "'read-bytes' expected its optional second argument to be a number" )
2022-07-07 21:31:01 +00:00
}
count = int ( c )
}
if count < 1 {
2022-07-09 02:48:02 +00:00
return exception ( "'read-bytes' expected its second argument to be a number greater than 0" )
2022-07-07 21:31:01 +00:00
}
b := make ( [ ] byte , count )
switch f := obj . Obj . ( type ) {
case * os . File :
n , err := f . Read ( b )
if err != nil && err != io . EOF {
return exception ( "'read-byte' could not read from the given IOHandle" )
} else if err != nil || n == 0 {
return false
}
if count > 1 {
return ByteSliceToExpressionSlice ( b )
}
return number ( b [ 0 ] )
case * net . Conn :
n , err := ( * f ) . Read ( b )
if err != nil && err != io . EOF {
return exception ( "'read-byte' could not read from the given IOHandle" )
} else if err != nil || n == 0 {
return false
}
if count > 1 {
return ByteSliceToExpressionSlice ( b )
}
return number ( b [ 0 ] )
case * tls . Conn :
n , err := f . Read ( b )
if err != nil && err != io . EOF {
return exception ( "'read-byte' could not read from the given IOHandle" )
} else if err != nil || n == 0 {
return false
}
if count > 1 {
return ByteSliceToExpressionSlice ( b )
}
return number ( b [ 0 ] )
default :
return exception ( "'read-byte' has not been implemented for this object type" )
}
2021-07-27 22:35:00 +00:00
}
2022-07-07 21:31:01 +00:00
2021-07-27 22:35:00 +00:00
} ,
2021-08-15 06:16:29 +00:00
"read-char" : func ( a ... expression ) expression {
if len ( a ) == 0 {
reader := bufio . NewReader ( os . Stdin )
char , _ , err := reader . ReadRune ( )
if err != nil {
2021-08-15 06:18:04 +00:00
return exception ( "'read-char' could not read from stdin" )
2021-08-15 06:16:29 +00:00
}
return fmt . Sprintf ( "%c" , char )
} else {
obj , ok := a [ 0 ] . ( * IOHandle )
if ! ok {
2021-08-15 06:18:04 +00:00
return exception ( "'read-char' expected an IO handle as its argument, but was not given an IO handle" )
2021-08-15 06:16:29 +00:00
}
if ! obj . Open {
2021-08-15 06:18:04 +00:00
return exception ( "'read-char' was given an IO handle that is already closed" )
2021-08-15 06:16:29 +00:00
}
// TODO add a strings.Builder to build output
// take a second arg for how many chars to read
// loop over readrune to read that many chars and
// build the buffer, then return the output. If an
// error is encountered while reading return the
// output if the len(buffer) > 0, otherwise an
// exception
2021-09-05 23:04:08 +00:00
byteBuf := make ( [ ] byte , 0 , 3 )
b := make ( [ ] byte , 1 )
2021-08-15 06:16:29 +00:00
switch f := obj . Obj . ( type ) {
case * os . File :
2021-09-05 23:04:08 +00:00
for {
n , err := f . Read ( b )
if err != nil && err != io . EOF {
return exception ( "'read-char' could not read from the given IOHandle" )
} else if err != nil || n == 0 {
2022-05-08 20:52:16 +00:00
return false
2021-09-05 23:04:08 +00:00
}
byteBuf = append ( byteBuf , b [ 0 ] )
if utf8 . FullRune ( byteBuf ) {
break
}
2021-08-15 06:16:29 +00:00
}
2021-09-05 23:04:08 +00:00
return string ( byteBuf )
2021-08-15 06:16:29 +00:00
case * net . Conn :
2021-09-05 23:04:08 +00:00
for {
n , err := ( * f ) . Read ( b )
if err != nil && err != io . EOF {
return exception ( "'read-char' could not read from the given IOHandle" )
} else if err != nil || n == 0 {
2022-05-08 20:52:16 +00:00
return false
2021-09-05 23:04:08 +00:00
}
byteBuf = append ( byteBuf , b [ 0 ] )
if utf8 . FullRune ( byteBuf ) {
break
}
2021-08-15 06:16:29 +00:00
}
2021-09-05 23:04:08 +00:00
return string ( byteBuf )
2021-08-15 06:16:29 +00:00
case * tls . Conn :
2021-09-05 23:04:08 +00:00
for {
n , err := f . Read ( b )
if err != nil && err != io . EOF {
return exception ( "'read-char' could not read from the given IOHandle" )
} else if err != nil || n == 0 {
2022-05-08 20:52:16 +00:00
return false
2021-09-05 23:04:08 +00:00
}
byteBuf = append ( byteBuf , b [ 0 ] )
if utf8 . FullRune ( byteBuf ) {
break
}
2021-08-15 06:16:29 +00:00
}
2021-09-05 23:04:08 +00:00
return string ( byteBuf )
2021-08-15 06:16:29 +00:00
default :
2021-08-15 06:18:04 +00:00
return exception ( "'read-char' has not been implemented for this object type" )
2021-08-15 06:16:29 +00:00
}
}
} ,
2021-07-26 05:31:22 +00:00
"read-line" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2022-05-08 20:52:16 +00:00
a = append ( a , & IOHandle { os . Stdin , true , "file (read-only)" } )
}
obj , ok := a [ 0 ] . ( * IOHandle )
if ! ok {
return exception ( "'read-line' expected an IO handle as its argument, but was not given an IO handle" )
}
if ! obj . Open {
return exception ( "'read-line' was given an IO handle that is already closed" )
}
split := byte ( '\n' )
if len ( a ) > 1 {
s := String ( a [ 1 ] , false )
if len ( s ) > 0 {
split = s [ 0 ]
2021-12-23 23:06:55 +00:00
}
2022-05-08 20:52:16 +00:00
}
switch f := obj . Obj . ( type ) {
case * os . File :
var o strings . Builder
for {
b := make ( [ ] byte , 1 )
n , err := f . Read ( b )
if n > 0 {
if b [ 0 ] == split {
2021-07-27 03:19:59 +00:00
break
}
2022-05-08 20:52:16 +00:00
o . Write ( b )
2021-07-27 03:19:59 +00:00
}
2022-05-08 20:52:16 +00:00
if err != nil && err != io . EOF && o . Len ( ) == 0 {
return exception ( "'read-line' could not read from the given IOHandle" )
} else if err != nil && err == io . EOF {
return false
} else if err != nil {
break
2021-07-27 22:35:00 +00:00
}
2022-05-08 20:52:16 +00:00
}
return o . String ( )
case * net . Conn :
var o strings . Builder
for {
b := make ( [ ] byte , 1 )
n , err := ( * f ) . Read ( b )
if n > 0 {
if b [ 0 ] == '\n' {
2021-07-28 05:45:52 +00:00
break
}
2022-05-08 20:52:16 +00:00
o . Write ( b )
}
if err != nil && err != io . EOF && o . Len ( ) == 0 {
return exception ( "'read-line' could not read from the given IOHandle" )
} else if err != nil && err == io . EOF {
return false
} else if err != nil {
break
2021-07-28 05:45:52 +00:00
}
2021-07-27 03:19:59 +00:00
}
2022-05-08 20:52:16 +00:00
return o . String ( )
default :
return exception ( "'read-line' has not been implemented for this object type" )
2021-07-26 05:31:22 +00:00
}
} ,
"newline" : func ( a ... expression ) expression {
2022-04-28 05:59:02 +00:00
SysoutPrint ( "\r\n" , Sysout )
2021-07-26 05:31:22 +00:00
return make ( [ ] expression , 0 )
} ,
"exit" : func ( a ... expression ) expression {
code := 0
if len ( a ) > 0 {
if v , ok := a [ 0 ] . ( number ) ; ok {
code = int ( v )
}
}
SafeExit ( code )
return 0
} ,
2022-05-10 21:09:08 +00:00
"slice" : func ( a ... expression ) expression {
2022-05-02 23:00:58 +00:00
if len ( a ) < 2 {
2022-05-10 21:09:08 +00:00
return exception ( "insufficient number of arguments given to 'slice', expected at least a list or string and at least one index number" )
2021-07-26 05:31:22 +00:00
}
var n1 , n2 number
var ok bool
2022-05-10 21:09:08 +00:00
switch val := a [ 0 ] . ( type ) {
case [ ] expression :
if n1 , ok = a [ 1 ] . ( number ) ; ! ok {
return exception ( "'slice' expects a number as its second argument" )
}
2021-07-26 05:31:22 +00:00
2022-05-10 21:09:08 +00:00
n2 = number ( len ( val ) )
if len ( a ) > 2 {
if n2 , ok = a [ 2 ] . ( number ) ; ! ok {
return exception ( "'slice' expects a number as its third argument" )
}
2022-05-02 23:00:58 +00:00
}
2021-07-26 05:31:22 +00:00
2022-05-10 21:09:08 +00:00
if int ( n2 ) > len ( val ) {
n2 = number ( len ( val ) )
}
2021-07-26 05:31:22 +00:00
2022-05-10 21:09:08 +00:00
if n1 < 0 {
n1 = number ( 0 )
}
2022-05-06 02:55:30 +00:00
2022-05-10 21:09:08 +00:00
if n1 >= n2 {
return make ( [ ] expression , 0 )
}
2022-05-02 23:00:58 +00:00
2022-05-10 21:09:08 +00:00
return val [ int ( n1 ) : int ( n2 ) ]
case string :
if n1 , ok = a [ 1 ] . ( number ) ; ! ok {
return exception ( "'slice' expects a number as its second argument" )
}
n2 = number ( len ( val ) )
if len ( a ) > 2 {
if n2 , ok = a [ 2 ] . ( number ) ; ! ok {
return exception ( "'slice' expects a number as its third argument" )
}
}
if int ( n2 ) > len ( val ) {
n2 = number ( len ( val ) )
}
if n1 < 0 {
n1 = number ( 0 )
}
if n1 >= n2 {
return ""
}
return val [ int ( n1 ) : int ( n2 ) ]
default :
return exception ( "'slice' expects a string or list as its first argument, a non-string non-list value was given" )
}
} ,
"substring" : func ( a ... expression ) expression {
return globalenv . vars [ "slice" ] . ( func ( a ... expression ) expression ) ( a ... )
2021-07-26 05:31:22 +00:00
} ,
2021-08-08 03:03:46 +00:00
"string-trim-space" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "insufficient number of arguments given to 'string-trim-space'" )
2021-08-08 03:03:46 +00:00
}
if s , ok := a [ 0 ] . ( string ) ; ok {
return strings . TrimSpace ( s )
}
2021-08-10 03:16:09 +00:00
return exception ( "'string-trim-space' expected a string but was given a non-string value" )
2021-08-08 03:03:46 +00:00
} ,
2021-07-26 05:31:22 +00:00
"string-append" : func ( a ... expression ) expression {
2022-05-09 21:54:28 +00:00
return globalenv . vars [ "append" ] . ( func ( ... expression ) expression ) ( a ... )
2021-07-26 05:31:22 +00:00
} ,
2022-04-27 22:36:30 +00:00
"rune->string" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'rune->string' expectes a number, no value was given" )
}
n , ok := a [ 0 ] . ( number )
if ! ok {
return exception ( "'rune->string' expectes a number, a non-number value was given" )
}
return string ( rune ( n ) )
} ,
"string->rune" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'string->rune' expectes a string, no value was given" )
}
s := String ( a [ 0 ] , false )
2022-05-19 22:09:17 +00:00
r := [ ] rune ( s )
if len ( r ) == 0 {
2022-04-27 22:36:30 +00:00
return number ( 0 )
}
2022-05-19 22:09:17 +00:00
return number ( r [ 0 ] )
2022-04-27 22:36:30 +00:00
} ,
2022-07-07 21:31:01 +00:00
"rune->bytes" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "insufficient number of arguments given to 'rune->bytes'" )
}
r , ok := a [ 0 ] . ( number )
if ! ok {
return exception ( "'rune->bytes' expectes a number, a non-number was given" )
}
return ByteSliceToExpressionSlice ( [ ] byte ( string ( rune ( r ) ) ) )
} ,
"string->bytes" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "insufficient number of arguments given to 'string->bytes'" )
}
return ByteSliceToExpressionSlice ( [ ] byte ( String ( a [ 0 ] , false ) ) )
} ,
"bytes->string" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "insufficient number of arguments given to 'bytes->string'" )
}
switch b := a [ 0 ] . ( type ) {
case number :
return string ( [ ] byte { byte ( b ) } )
case [ ] expression :
by := make ( [ ] byte , len ( b ) )
for i := range b {
n , ok := b [ i ] . ( number )
if ! ok {
return exception ( "'byte->string' encountered a non-number value in the given list" )
}
by [ i ] = byte ( n )
}
return string ( by )
default :
return exception ( "'byte->string' expected a number or a list of numbers, an invalid item type was given" )
}
} ,
2021-07-26 05:31:22 +00:00
"string->number" : func ( a ... expression ) expression {
2021-08-23 22:39:48 +00:00
// (string->number [string] [[base: number]])
2021-07-26 05:31:22 +00:00
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "insufficient number of arguments given to 'string->number'" )
2021-07-26 05:31:22 +00:00
}
2021-08-23 22:39:48 +00:00
s , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'string->number' expects a string as its first argument, a non-string value was given" )
}
base := 10
if len ( a ) > 1 {
b , ok2 := a [ 1 ] . ( number )
if ! ok2 {
return exception ( "'string->number' expects a number representing the base being used for number parsing as its second argument, a non-number value was given" )
}
base = int ( b )
}
if len ( a ) == 1 || base == 10 {
2021-07-26 05:31:22 +00:00
f , err := strconv . ParseFloat ( s , 64 )
if err == nil {
return number ( f )
}
}
2021-08-23 22:39:48 +00:00
i , err := strconv . ParseInt ( s , base , 64 )
if err != nil {
2021-09-01 20:58:32 +00:00
return false
2021-08-23 22:39:48 +00:00
}
return number ( i )
2021-07-26 05:31:22 +00:00
} ,
2022-07-11 00:11:58 +00:00
"string-replace" : func ( a ... expression ) expression {
if len ( a ) < 3 {
return exception ( "'string-replace' expected three values: a string, a thing to replace in that string, and a thing to replace it with. Insufficient values were given" )
}
count := - 1
if len ( a ) > 3 {
c , ok := a [ 3 ] . ( number )
if ! ok {
return exception ( "'string-replace' expected its fourth argument to be a number representing a count of replacements to be made, a non-number value was given" )
}
count = int ( c )
}
return strings . Replace ( String ( a [ 0 ] , false ) , String ( a [ 1 ] , false ) , String ( a [ 2 ] , false ) , count )
} ,
2021-08-15 06:16:29 +00:00
"string-fields" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "insufficient number of arguments given to 'string-fields'" )
}
stringIn , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'string-fields' expected a string, but was given a non-string value" )
}
sf := strings . Fields ( stringIn )
return StringSliceToExpressionSlice ( sf ) . ( [ ] expression )
} ,
2021-07-29 03:47:05 +00:00
"string->list" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "insufficient number of arguments given to 'string->list'" )
2021-07-29 03:47:05 +00:00
}
stringIn , ok := a [ 0 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'string->list' expected a string, but was given a non-string value" )
2021-07-29 03:47:05 +00:00
}
splitPoint := ""
if len ( a ) >= 2 {
switch sp := a [ 1 ] . ( type ) {
case string :
splitPoint = sp
case symbol :
splitPoint = string ( sp )
case number :
splitPoint = strconv . Itoa ( int ( sp ) )
case [ ] expression :
2021-08-12 21:20:02 +00:00
splitPoint = String ( sp , true )
2021-07-29 03:47:05 +00:00
default :
2021-08-10 03:16:09 +00:00
return exception ( "'string->list' could not convert the given split point to a string upon which to split" )
2021-07-29 03:47:05 +00:00
}
}
2021-09-02 20:40:19 +00:00
count := - 1
if len ( a ) > 2 {
num , ok := a [ 2 ] . ( number )
if ! ok {
return exception ( "'string->list' expected a number as its third argument, but a non-number value was given" )
}
if num < 1 {
count = - 1
} else {
count = int ( num )
}
}
sSlice := strings . SplitN ( stringIn , splitPoint , count )
2021-07-29 03:47:05 +00:00
return StringSliceToExpressionSlice ( sSlice ) . ( [ ] expression )
} ,
2021-08-11 04:56:06 +00:00
"regex-match?" : func ( a ... expression ) expression {
if len ( a ) < 2 {
2021-08-11 22:24:10 +00:00
return exception ( "'regex-match?' expects two arguments, a pattern string and an input string, insufficient arguments were given" )
2021-08-11 04:56:06 +00:00
}
2021-08-11 22:24:10 +00:00
pattern , ok := a [ 0 ] . ( string )
2021-08-11 04:56:06 +00:00
if ! ok {
2021-08-11 22:24:10 +00:00
return exception ( "'regex-match?' expects a string representing a regular expression pattern as its first argument, but was given a non-string value" )
2021-08-11 04:56:06 +00:00
}
2021-08-11 22:24:10 +00:00
str , ok := a [ 1 ] . ( string )
2021-08-11 04:56:06 +00:00
if ! ok {
2021-08-11 22:24:10 +00:00
return exception ( "'regex-match?' expects a string as its second argument, but was given a non-string value" )
2021-08-11 04:56:06 +00:00
}
match , err := regexp . MatchString ( pattern , str )
if err != nil {
return exception ( "'regex-match?' encountered a regular expression error while matching" )
}
return match
} ,
"regex-find" : func ( a ... expression ) expression {
if len ( a ) < 2 {
2021-08-11 22:24:10 +00:00
return exception ( "'regex-find' expects two arguments, a pattern string and an input string, insufficient arguments were given" )
2021-08-11 04:56:06 +00:00
}
2021-08-11 22:24:10 +00:00
pattern , ok := a [ 0 ] . ( string )
2021-08-11 04:56:06 +00:00
if ! ok {
2021-08-11 22:24:10 +00:00
return exception ( "'regex-find' expects a string representing a regular expression pattern as its first argument, but was given a non-string value" )
2021-08-11 04:56:06 +00:00
}
2021-08-11 22:24:10 +00:00
str , ok := a [ 1 ] . ( string )
2021-08-11 04:56:06 +00:00
if ! ok {
2021-08-11 22:24:10 +00:00
return exception ( "'regex-find' expects a string as its second argument, but was given a non-string value" )
2021-08-11 04:56:06 +00:00
}
re := regexp . MustCompile ( pattern )
found := re . FindAllString ( str , - 1 )
if found == nil {
return make ( [ ] expression , 0 )
}
return StringSliceToExpressionSlice ( found ) . ( [ ] expression )
} ,
2021-08-11 22:24:10 +00:00
"regex-replace" : func ( a ... expression ) expression {
if len ( a ) < 3 {
return exception ( "'regex-replace' expects three arguments: a pattern string, an input string, and a replacement string, insufficient arguments were given" )
}
pattern , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'regex-replace' expects a string representing a regular expression pattern as its first argument, but was given a non-string value" )
}
str , ok := a [ 1 ] . ( string )
if ! ok {
return exception ( "'regex-find' expects a string as its second argument, but was given a non-string value" )
}
replace , ok := a [ 2 ] . ( string )
if ! ok {
return exception ( "'regex-find' expects a replacement string as its third argument, but was given a non-string value" )
}
re := regexp . MustCompile ( pattern )
return re . ReplaceAllString ( str , replace )
} ,
2021-07-26 05:31:22 +00:00
"number->string" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "insufficient number of arguments given to 'string->number'" )
2021-07-26 05:31:22 +00:00
}
2021-08-22 03:27:17 +00:00
n , ok := a [ 0 ] . ( number )
if ! ok {
return exception ( "'number->string' expected a number as its first argument, a non-number value was given" )
2021-07-26 05:31:22 +00:00
}
2021-08-22 03:27:17 +00:00
if len ( a ) == 2 {
base , ok2 := a [ 1 ] . ( number )
if ! ok2 {
return exception ( "'number->string' expected a number as its second argument, a non-number value was given" )
}
2021-08-23 22:39:48 +00:00
if base != 10 {
return strconv . FormatInt ( int64 ( n ) , int ( base ) )
}
2021-08-22 03:27:17 +00:00
}
return strconv . FormatFloat ( float64 ( n ) , 'f' , - 1 , 64 )
} ,
2021-08-22 22:02:54 +00:00
"file-create-temp" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'file-create-temp' expects a filename pattern as a string, no value was given" )
}
fp , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'file-create-temp' expects a string representing a filename pattern, a non-string value was given" )
}
f , err := os . CreateTemp ( "" , fp )
if err != nil {
return exception ( "'file-create-temp' was unable to create the requested temporary file: " + err . Error ( ) )
}
2021-08-24 21:04:47 +00:00
obj := & IOHandle { f , true , "file (write-only)" }
2021-08-22 22:02:54 +00:00
openFiles = append ( openFiles , obj )
return obj
} ,
"file-name" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'file-path' expects an IOHandle, no value was given" )
}
ioh , ok := a [ 0 ] . ( * IOHandle )
if ! ok {
return exception ( "'file-path' expects an IOHandle, a non-IOHandle value was given" )
}
switch f := ioh . Obj . ( type ) {
2021-09-04 14:45:35 +00:00
case * os . File :
return f . Name ( )
default :
return exception ( "'file-path' encountered an IOHandle type that it cannot process" )
2021-08-22 22:02:54 +00:00
}
} ,
2021-08-22 03:27:17 +00:00
"file-stat" : func ( a ... expression ) expression {
2021-08-22 22:02:54 +00:00
if len ( a ) == 0 {
2021-08-22 03:27:17 +00:00
return exception ( "'file-stat' expects a filepath, no filepath was given" )
}
fp , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'file-stat' expects a filepath as a string, a non-string value was given" )
}
2022-07-06 21:50:56 +00:00
fp = ExpandedAbsFilepath ( fp )
info , err := os . Stat ( fp )
2021-08-22 03:27:17 +00:00
if err != nil {
2021-09-04 14:55:22 +00:00
if os . IsNotExist ( err ) {
return false
}
2021-08-22 03:27:17 +00:00
return exception ( "'file-stat' could not stat file: " + err . Error ( ) )
}
2022-07-06 20:52:18 +00:00
stat := info . Sys ( ) . ( * syscall . Stat_t )
u := strconv . FormatUint ( uint64 ( stat . Uid ) , 10 )
g := strconv . FormatUint ( uint64 ( stat . Gid ) , 10 )
2022-07-07 15:14:05 +00:00
var userString string
usr , err := user . LookupId ( u )
if err != nil {
userString = ""
} else {
userString = usr . Username
}
var groupString string
group , err := user . LookupGroupId ( g )
if err != nil {
groupString = ""
} else {
groupString = group . Name
}
2021-08-22 22:44:37 +00:00
2022-07-06 20:52:18 +00:00
out := make ( [ ] expression , 10 )
2021-08-22 03:27:17 +00:00
out [ 0 ] = [ ] expression { "name" , info . Name ( ) }
out [ 1 ] = [ ] expression { "size" , number ( info . Size ( ) ) }
out [ 2 ] = [ ] expression { "mode" , number ( info . Mode ( ) ) }
out [ 3 ] = [ ] expression { "mod-time" , number ( info . ModTime ( ) . Unix ( ) ) }
out [ 4 ] = [ ] expression { "is-dir?" , info . IsDir ( ) }
2021-08-22 22:44:37 +00:00
out [ 5 ] = [ ] expression { "is-symlink?" , false }
out [ 6 ] = [ ] expression { "path" , fp }
2022-07-07 15:14:05 +00:00
out [ 7 ] = [ ] expression { "owner" , userString }
out [ 8 ] = [ ] expression { "group" , groupString }
2022-07-06 20:52:18 +00:00
out [ 9 ] = [ ] expression { "modestring" , strconv . FormatUint ( uint64 ( info . Mode ( ) ) , 8 ) }
2021-08-22 22:44:37 +00:00
ln , err := os . Readlink ( fp )
if err == nil {
out [ 5 ] = [ ] expression { "is-symlink?" , true }
if ! filepath . IsAbs ( ln ) {
2021-08-22 22:50:06 +00:00
root := fp
if ! info . IsDir ( ) {
root = filepath . Dir ( fp )
}
ln = ExpandedAbsFilepath ( filepath . Join ( root , ln ) )
2021-08-22 22:44:37 +00:00
}
out [ 6 ] = [ ] expression { "path" , ln }
}
2021-08-22 03:27:17 +00:00
return out
2021-07-26 05:31:22 +00:00
} ,
"file-create" : func ( a ... expression ) expression {
if len ( a ) < 1 {
2021-08-10 03:16:09 +00:00
return exception ( "'file-create' expects a filepath, no filepath was given" )
2021-07-26 05:31:22 +00:00
}
2021-09-04 14:45:35 +00:00
fp , ok := a [ 0 ] . ( string )
2021-08-22 03:27:17 +00:00
if ! ok {
return exception ( "'file-create' expects a filepath as a string, a non-string value was given" )
}
f , err := os . Create ( fp )
if err != nil {
return exception ( "'file-create' encountered an error: " + err . Error ( ) )
2021-07-26 05:31:22 +00:00
}
2021-08-24 21:04:47 +00:00
obj := & IOHandle { f , true , "file (write-only)" }
2021-08-22 03:27:17 +00:00
openFiles = append ( openFiles , obj )
return obj
2021-07-26 05:31:22 +00:00
} ,
2021-07-27 03:19:59 +00:00
"file-open-read" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'file-open-read' expects a filepath, no filepath was given" )
2021-07-27 03:19:59 +00:00
}
if fp , ok := a [ 0 ] . ( string ) ; ok {
f , err := os . Open ( fp )
if err != nil {
return false
}
2021-08-24 21:04:47 +00:00
obj := & IOHandle { f , true , "file (read-only)" }
2021-07-27 16:33:06 +00:00
openFiles = append ( openFiles , obj )
return obj
2021-07-27 03:19:59 +00:00
}
2021-08-10 03:16:09 +00:00
return exception ( "'file-open-read' expects a filepath as a string, a non-string value was given" )
2021-07-27 03:19:59 +00:00
} ,
"file-open-write" : func ( a ... expression ) expression {
2021-08-23 22:39:48 +00:00
// (file-open-write [filepath: string] [[truncate: bool]])
2021-07-27 03:19:59 +00:00
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'file-open-write' expects a filepath, no filepath was given" )
2021-07-27 03:19:59 +00:00
}
if fp , ok := a [ 0 ] . ( string ) ; ok {
2021-07-31 20:25:20 +00:00
flags := os . O_CREATE | os . O_WRONLY
2021-07-27 03:19:59 +00:00
if len ( a ) >= 2 {
if b , ok := a [ 1 ] . ( bool ) ; ok && b {
flags = flags | os . O_TRUNC
}
}
2021-07-31 20:25:20 +00:00
f , err := os . OpenFile ( fp , flags , 0664 )
2021-07-27 03:19:59 +00:00
if err != nil {
2021-09-30 05:57:18 +00:00
return false
2021-07-27 03:19:59 +00:00
}
2021-08-24 21:04:47 +00:00
obj := & IOHandle { f , true , "file (write-only)" }
2021-07-27 16:33:06 +00:00
openFiles = append ( openFiles , obj )
return obj
2021-07-27 03:19:59 +00:00
}
2021-08-10 03:16:09 +00:00
return exception ( "'file-write' expects a filepath as a string, a non-string value was given" )
2021-07-27 03:19:59 +00:00
} ,
"file-append-to" : func ( a ... expression ) expression {
2021-08-23 22:39:48 +00:00
// (file-append-to [filepath: string] [[string...]])
2021-07-27 03:19:59 +00:00
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'file-append-to' requires a filepath and at least one string" )
2021-07-27 03:19:59 +00:00
}
if fp , ok := a [ 0 ] . ( string ) ; ok {
f , err := os . OpenFile ( fp , os . O_APPEND | os . O_CREATE | os . O_WRONLY , 0644 )
if err != nil {
2021-09-30 05:57:18 +00:00
return false
2021-07-27 03:19:59 +00:00
}
defer f . Close ( )
for i := range a [ 1 : ] {
2021-09-04 14:45:35 +00:00
f . WriteString ( String ( a [ i + 1 ] , false ) )
2021-07-27 03:19:59 +00:00
}
2021-08-13 16:34:42 +00:00
return make ( [ ] expression , 0 )
2021-07-27 03:19:59 +00:00
}
2021-08-10 03:16:09 +00:00
return exception ( "'file-append-to' expects a filepath, as a string, as its first argument" )
2021-07-27 03:19:59 +00:00
} ,
2021-07-31 20:25:20 +00:00
"open?" : func ( a ... expression ) expression {
2021-07-26 05:31:22 +00:00
if len ( a ) < 1 {
2021-08-10 03:16:09 +00:00
return exception ( "'open?' expects an IO handle, no argument was given" )
2021-07-26 05:31:22 +00:00
}
2021-07-27 16:33:06 +00:00
if h , ok := a [ 0 ] . ( * IOHandle ) ; ok {
return h . Open
}
2021-08-20 05:54:41 +00:00
return exception ( "'open?' expects an IO handle" )
2021-07-27 16:33:06 +00:00
} ,
"close" : func ( a ... expression ) expression {
if len ( a ) < 1 {
2021-08-10 03:16:09 +00:00
return exception ( "'close' expects an IO handle, no argument was given" )
2021-07-27 16:33:06 +00:00
}
if f , ok := a [ 0 ] . ( * IOHandle ) ; ok {
f . Open = false
switch o := f . Obj . ( type ) {
case * os . File :
o . Close ( )
2021-07-27 22:35:00 +00:00
case * net . Conn :
( * o ) . Close ( )
2021-07-28 05:45:52 +00:00
case * tls . Conn :
o . Close ( )
2021-08-10 23:09:00 +00:00
case * strings . Builder :
f . Open = false // duplicate here to not have a blank line
2021-07-27 16:33:06 +00:00
default :
2021-08-10 03:16:09 +00:00
return exception ( "'close' encountered an unsupported IO handle type" )
2021-07-27 16:33:06 +00:00
}
2021-07-26 05:31:22 +00:00
} else {
2021-08-10 03:16:09 +00:00
return exception ( "'close' expects an IO handle" )
2021-07-26 05:31:22 +00:00
}
return make ( [ ] expression , 0 )
} ,
2021-07-27 03:19:59 +00:00
"path-exists?" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'path-exists' expects a filepath as a string, no value was supplied" )
2021-07-27 03:19:59 +00:00
}
switch p := a [ 0 ] . ( type ) {
case string :
2021-07-29 03:47:05 +00:00
_ , err := os . Stat ( p )
2021-07-27 03:19:59 +00:00
if err != nil {
return false
}
return true
case symbol :
2021-07-29 03:47:05 +00:00
_ , err := os . Stat ( string ( p ) )
2021-07-27 03:19:59 +00:00
if err != nil {
return false
}
return true
default :
2021-08-09 22:53:27 +00:00
return exception ( "'path-exists' expected a string, but a non-string value was given" )
}
} ,
2021-08-14 06:15:10 +00:00
"path-abs" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'path-abs' expects a filepath as a string, no value was given" )
}
p , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'path-abs' expected a filepath as a string, a non-string value was given" )
}
return ExpandedAbsFilepath ( p )
} ,
2021-08-09 22:53:27 +00:00
"path-is-dir?" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-14 06:15:10 +00:00
return exception ( "'path-is-dir?' expects a filepath as a string, no value was given" )
2021-08-09 22:53:27 +00:00
}
switch p := a [ 0 ] . ( type ) {
case string :
2021-08-17 05:20:12 +00:00
f , err := os . Stat ( ExpandedAbsFilepath ( p ) )
2021-08-09 22:53:27 +00:00
if err != nil {
2021-08-17 05:20:12 +00:00
return exception ( strings . Replace ( err . Error ( ) , "stat " , "" , - 1 ) )
2021-08-09 22:53:27 +00:00
}
return f . IsDir ( )
case symbol :
2021-08-17 05:20:12 +00:00
f , err := os . Stat ( ExpandedAbsFilepath ( string ( p ) ) )
2021-08-09 22:53:27 +00:00
if err != nil {
2021-08-17 05:20:12 +00:00
return exception ( strings . Replace ( err . Error ( ) , "stat " , "" , - 1 ) )
2021-08-09 22:53:27 +00:00
}
return f . IsDir ( )
default :
return exception ( "'path-is-dir?' expected a string, but a non-string value was given" )
2021-07-27 03:19:59 +00:00
}
} ,
2021-08-14 15:08:35 +00:00
"path-join" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return "/"
}
strs := make ( [ ] string , len ( a ) )
for i := range a {
v , ok := a [ i ] . ( string )
if ! ok {
return exception ( fmt . Sprintf ( "Argument %d to 'path-join' is not a string" , i + 1 ) )
}
strs [ i ] = v
}
return filepath . Join ( strs ... )
} ,
"path-extension" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'path-extension' expects a string and, optionally, a second string; no value was given" )
}
p , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'path-extension' expects a path string as its first argument, a non-string value was given" )
}
if len ( a ) > 1 {
2022-04-14 06:09:49 +00:00
e , ok := a [ 1 ] . ( string )
2021-08-14 15:08:35 +00:00
if ! ok {
return exception ( "'path-extension' expects an extension as a string as its second argument, a non-string value was given" )
}
ex := filepath . Ext ( p )
if ex == "" {
return p + e
}
li := strings . LastIndex ( p , ex )
if li < 0 {
return p
}
2022-04-14 06:16:29 +00:00
return p [ : li ] + e
2021-08-14 15:08:35 +00:00
}
return filepath . Ext ( p )
} ,
2021-08-21 21:47:47 +00:00
"path-base" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'path-base' expects a string, no value was given" )
}
p , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'path-base' expects a path string as its first argument, a non-string value was given" )
}
return filepath . Base ( p )
} ,
"path-dir" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'path-dir' expects a string, no value was given" )
}
p , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'path-dir' expects a path string as its first argument, a non-string value was given" )
}
return filepath . Dir ( p )
} ,
2021-08-14 15:08:35 +00:00
"path-glob" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'path-glob' expects a filepath as a string, too few values given" )
}
p , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'path-glob' expects a filepath as a string, a non-string value was given" )
}
2022-05-11 02:55:06 +00:00
g , err := BetterGlob ( p )
2021-08-14 15:08:35 +00:00
if err != nil {
return exception ( "'path-glob' received an invalid filepath glob pattern as a string" )
}
return StringSliceToExpressionSlice ( g )
} ,
2021-08-22 22:59:20 +00:00
"hostname" : func ( a ... expression ) expression {
hostname , err := os . Hostname ( )
if err != nil {
return exception ( "'hostname' could not retrieve the hostname: " + err . Error ( ) )
}
return hostname
} ,
2022-09-19 16:10:40 +00:00
"net-post" : func ( a ... expression ) expression {
if len ( a ) < 2 {
2022-09-19 16:20:31 +00:00
return [ ] expression { false , "'net-post' expects a url string and a string or an open io-handle containing the post body/data, too few values were given" }
2022-09-19 16:10:40 +00:00
}
u , err := url . Parse ( String ( a [ 0 ] , false ) )
if err != nil {
return [ ] expression { false , "'net-post' was given a url string that could not be parsed as a url" }
}
switch u . Scheme {
case "http" , "https" :
mime := "text/plain"
if len ( a ) > 2 {
mime = String ( a [ 2 ] , false )
}
var resp * http . Response
switch data := a [ 1 ] . ( type ) {
case * IOHandle :
if ! data . Open {
return [ ] expression { false , "'net-post' was given a closed io-handle" }
}
switch handleObj := data . Obj . ( type ) {
case * os . File :
resp , err = http . Post ( u . String ( ) , mime , handleObj )
case * net . Conn :
resp , err = http . Post ( u . String ( ) , mime , * handleObj )
case net . Conn :
resp , err = http . Post ( u . String ( ) , mime , handleObj )
case * tls . Conn :
resp , err = http . Post ( u . String ( ) , mime , handleObj )
case * strings . Builder :
resp , err = http . Post ( u . String ( ) , mime , strings . NewReader ( handleObj . String ( ) ) )
default :
return [ ] expression { false , "'net-post' was given an unsupported io-handle type" }
}
default :
resp , err = http . Post ( u . String ( ) , mime , strings . NewReader ( String ( data , false ) ) )
}
if err != nil {
return [ ] expression { false , fmt . Sprintf ( "'net-post' error: %s" , err . Error ( ) ) }
}
defer resp . Body . Close ( )
body , err := io . ReadAll ( resp . Body )
if err != nil {
return [ ] expression { false , fmt . Sprintf ( "'net-get' error: %s" , err . Error ( ) ) }
}
return [ ] expression { true , string ( body ) }
case "gemini" :
u . RawQuery = url . QueryEscape ( String ( a [ 1 ] , false ) )
resp , mime , err := GeminiRequest ( u , 0 )
if err != nil {
return [ ] expression { false , fmt . Sprintf ( "'net-post' error: %s" , err . Error ( ) ) }
}
return [ ] expression { true , resp , mime }
case "gopher" :
if u . Port ( ) == "" {
u . Host = u . Host + ":70"
}
conn , err := net . Dial ( "tcp" , u . Host )
if err != nil {
return [ ] expression { false , fmt . Sprintf ( "'net-get' error: %s" , err . Error ( ) ) }
}
defer conn . Close ( )
p := u . Path
if len ( u . Path ) < 2 {
p = fmt . Sprintf ( "/\t%s\r\n" , a [ 1 ] )
} else {
p = fmt . Sprintf ( "%s\t%s\r\n" , p [ 2 : ] , a [ 1 ] )
}
_ , err = conn . Write ( [ ] byte ( p ) )
if err != nil {
return [ ] expression { false , fmt . Sprintf ( "'net-get' error: %s" , err . Error ( ) ) }
}
resp , err := io . ReadAll ( conn )
if err != nil {
return [ ] expression { false , fmt . Sprintf ( "'net-get' error: %s" , err . Error ( ) ) }
}
return [ ] expression { true , string ( resp ) }
default :
2022-09-19 16:20:31 +00:00
return [ ] expression { false , "'net-post' was given a url for an unsupported scheme: " + u . Scheme + ". Consider using 'net-conn' instead" }
2022-09-19 16:10:40 +00:00
}
} ,
2022-09-18 05:34:08 +00:00
"net-get" : func ( a ... expression ) expression {
if len ( a ) < 1 {
2022-09-19 16:10:40 +00:00
return [ ] expression { false , "'net-get' expects a url string, too few values were given" }
2022-09-18 05:34:08 +00:00
}
u , err := url . Parse ( String ( a [ 0 ] , false ) )
if err != nil {
2022-09-19 16:10:40 +00:00
return [ ] expression { false , "'net-get' was given a url string that could not be parsed as a url" }
2022-09-18 05:34:08 +00:00
}
switch u . Scheme {
case "http" , "https" :
resp , err := http . Get ( u . String ( ) )
if err != nil {
2022-09-19 16:10:40 +00:00
return [ ] expression { false , fmt . Sprintf ( "'net-get' error: %s" , err . Error ( ) ) }
2022-09-18 05:34:08 +00:00
}
defer resp . Body . Close ( )
body , err := io . ReadAll ( resp . Body )
if err != nil {
2022-09-19 16:10:40 +00:00
return [ ] expression { false , fmt . Sprintf ( "'net-get' error: %s" , err . Error ( ) ) }
2022-09-18 05:34:08 +00:00
}
2022-09-19 16:10:40 +00:00
return [ ] expression { true , string ( body ) }
2022-09-18 05:34:08 +00:00
case "gemini" :
2022-09-19 16:10:40 +00:00
resp , mime , err := GeminiRequest ( u , 0 )
2022-09-18 05:34:08 +00:00
if err != nil {
2022-09-19 16:10:40 +00:00
return [ ] expression { false , fmt . Sprintf ( "'net-get' error: %s" , err . Error ( ) ) }
2022-09-18 05:34:08 +00:00
}
2022-09-19 16:10:40 +00:00
return [ ] expression { true , resp , mime }
2022-09-18 05:34:08 +00:00
case "gopher" :
if u . Port ( ) == "" {
u . Host = u . Host + ":70"
}
conn , err := net . Dial ( "tcp" , u . Host )
if err != nil {
2022-09-19 16:10:40 +00:00
return [ ] expression { false , fmt . Sprintf ( "'net-get' error: %s" , err . Error ( ) ) }
2022-09-18 05:34:08 +00:00
}
defer conn . Close ( )
p := u . Path
if len ( u . Path ) < 2 {
p = "/" + "\n"
} else {
p = p [ 2 : ] + "\n"
}
_ , err = conn . Write ( [ ] byte ( p ) )
if err != nil {
2022-09-19 16:10:40 +00:00
return [ ] expression { false , fmt . Sprintf ( "'net-get' error: %s" , err . Error ( ) ) }
2022-09-18 05:34:08 +00:00
}
resp , err := io . ReadAll ( conn )
if err != nil {
2022-09-19 16:10:40 +00:00
return [ ] expression { false , fmt . Sprintf ( "'net-get' error: %s" , err . Error ( ) ) }
2022-09-18 05:34:08 +00:00
}
2022-09-19 16:10:40 +00:00
return [ ] expression { true , string ( resp ) }
2022-09-18 05:34:08 +00:00
default :
2022-09-19 16:10:40 +00:00
return [ ] expression { false , "'net-get' was given a URL for an unsupported scheme: " + u . Scheme + ". Consider using 'net-conn' instead" }
2022-09-18 05:34:08 +00:00
}
} ,
2022-11-05 15:25:29 +00:00
"net-listen" : func ( a ... expression ) expression {
if len ( a ) < 3 {
return exception ( ` 'net-listen' expects a string or symbol ("tcp", "tcp4", "tcp6", "unix", or "unixpacket"), an address string, and a procedure or macro that accepts an io-handle. Too few values were given ` )
}
network := strings . ToLower ( String ( a [ 0 ] , false ) )
switch network {
case "tcp" , "tcp4" , "tcp6" , "unix" , "unixpacket" :
break
default :
return exception ( ` 'net-listen' expected the first value given to be a string equal to "tcp", "tcp4", "tcp6", "unix", or "unixpacket". An invalid value was given ` )
}
address := String ( a [ 1 ] , false )
server , err := net . Listen ( network , address )
if err != nil {
return exception ( ` 'net-listen' could not listen at the given address: ` + address )
}
defer server . Close ( )
var callback func ( conn * IOHandle )
switch f := a [ 2 ] . ( type ) {
case func ( ... expression ) expression :
callback = func ( conn * IOHandle ) {
f ( IOHandle { & conn , true , "net-conn" } )
}
case proc :
callback = func ( conn * IOHandle ) {
apply ( f , [ ] expression { conn } , "net-listen callback" )
}
case macro :
callback = func ( conn * IOHandle ) {
apply ( f , [ ] expression { conn } , "net-listen callback" )
}
default :
return exception ( ` 'net-listen' expected a built-in, proc, or macro as its third value; an invalid value was given ` )
}
if len ( a ) > 3 {
switch f := a [ 3 ] . ( type ) {
case func ( ... expression ) expression :
f ( network , address )
case proc :
apply ( f , [ ] expression { network , address } , "net-listen init callback" )
case macro :
apply ( f , [ ] expression { network , address } , "net-listen init callback" )
}
}
for {
conn , err := server . Accept ( )
if err != nil {
return exception ( "'net-listen' encountered an error with a connection: " + err . Error ( ) )
}
go callback ( & IOHandle { & conn , true , "net-conn" } )
}
} ,
2021-07-27 22:35:00 +00:00
"net-conn" : func ( a ... expression ) expression {
2021-07-29 23:00:41 +00:00
// (net-conn host port use-tls timeout-seconds)
// (net-conn string string bool number)
2021-07-27 22:35:00 +00:00
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'net-conn' expects a host and a port as a string, too few values given" )
2021-07-27 22:35:00 +00:00
}
host , ok := a [ 0 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'net-conn' expects a host as a string, a non-string value was given" )
2021-07-27 22:35:00 +00:00
}
2021-07-28 05:45:52 +00:00
var port string
switch p := a [ 1 ] . ( type ) {
case string :
port = p
case number :
port = strconv . Itoa ( int ( p ) )
default :
2021-08-10 03:16:09 +00:00
return exception ( "'net-conn' expects a port as a string, a non-string value was given" )
2021-07-27 22:35:00 +00:00
}
2021-07-28 05:45:52 +00:00
usetls := false
if len ( a ) >= 3 {
t , ok := a [ 2 ] . ( bool )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'net-conn' expects a boolean value as the third argument (use-tls), a non-bool value was given" )
2021-07-28 05:45:52 +00:00
}
usetls = t
}
timeout := - 1
if len ( a ) >= 4 {
switch to := a [ 3 ] . ( type ) {
case number :
timeout = int ( to )
case string :
t , err := strconv . Atoi ( to )
if err != nil {
2021-08-10 03:16:09 +00:00
return exception ( "'net-conn' was given a timeout string that does not cast to an integer" )
2021-07-28 05:45:52 +00:00
}
timeout = t
default :
2021-08-10 03:16:09 +00:00
return exception ( "'net-conn' expects a string or number value representing a timeout, in seconds; a non-string non-number value was given " )
2021-07-28 05:45:52 +00:00
}
}
var conn net . Conn
var tlsconn * tls . Conn
var err error
addr := fmt . Sprintf ( "%s:%s" , host , port )
if usetls {
conf := & tls . Config { InsecureSkipVerify : true }
if timeout > 0 {
2021-07-31 20:25:20 +00:00
tlsconn , err = tls . DialWithDialer ( & net . Dialer { Timeout : time . Duration ( timeout ) * time . Second } , "tcp" , addr , conf )
2021-07-28 05:45:52 +00:00
if err != nil {
return false
}
} else {
tlsconn , err = tls . Dial ( "tcp" , addr , conf )
if err != nil {
return false
}
}
} else {
if timeout > 0 {
2021-07-31 20:25:20 +00:00
conn , err = net . DialTimeout ( "tcp" , addr , time . Duration ( timeout ) * time . Second )
2021-07-28 05:45:52 +00:00
if err != nil {
return false
}
} else {
conn , err = net . Dial ( "tcp" , addr )
if err != nil {
return false
}
}
}
// based on the tls value use tls or not, same with the timeout value for adding a timeout
var handle * IOHandle
if usetls {
2021-08-24 21:04:47 +00:00
handle = & IOHandle { tlsconn , true , "net-conn (tls)" }
2021-07-28 05:45:52 +00:00
} else {
2021-08-24 21:04:47 +00:00
handle = & IOHandle { & conn , true , "net-conn" }
2021-07-27 22:35:00 +00:00
}
openFiles = append ( openFiles , handle )
return handle
} ,
2021-07-29 03:47:05 +00:00
"url-host" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'url-host' expects a url as a string, no value was given" )
2021-07-29 03:47:05 +00:00
}
rawURL , ok := a [ 0 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'url-host' expects a url as a string, a non-string value was given" )
2021-07-29 03:47:05 +00:00
}
u , err := url . Parse ( rawURL )
if err != nil {
return false
}
if len ( a ) >= 2 {
p := ":" + u . Port ( )
if p == ":" {
p = ""
}
switch s := a [ 1 ] . ( type ) {
case string :
u . Host = s + p
case symbol :
u . Host = string ( s ) + p
default :
2021-10-07 22:57:09 +00:00
return exception ( "'url-host' expects either a string or a symbol as its second argument, a non-string non-symbol value was given" )
2021-07-29 03:47:05 +00:00
}
return u . String ( )
}
return u . Hostname ( )
} ,
"url-port" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'url-port' expects a url as a string, no value was given" )
2021-07-29 03:47:05 +00:00
}
rawURL , ok := a [ 0 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'url-port' expects a url as a string, a non-string value was given" )
2021-07-29 03:47:05 +00:00
}
u , err := url . Parse ( rawURL )
if err != nil {
return false
}
if len ( a ) >= 2 {
switch s := a [ 1 ] . ( type ) {
case string :
u . Host = u . Hostname ( ) + ":" + s
case symbol :
u . Host = u . Hostname ( ) + ":" + string ( s )
case number :
u . Host = u . Hostname ( ) + ":" + strconv . Itoa ( int ( s ) )
default :
2021-10-07 22:57:09 +00:00
return exception ( "'url-port' expects either a string or a symbol as its second argument, a non-string non-symbol value was given" )
2021-07-29 03:47:05 +00:00
}
return u . String ( )
}
return u . Port ( )
} ,
"url-query" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'url-query' expects a url as a string, no value was given" )
2021-07-29 03:47:05 +00:00
}
rawURL , ok := a [ 0 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'url-query' expects a url as a string, a non-string value was given" )
2021-07-29 03:47:05 +00:00
}
u , err := url . Parse ( rawURL )
if err != nil {
return false
}
if len ( a ) >= 2 {
switch s := a [ 1 ] . ( type ) {
case string :
2021-07-31 20:25:20 +00:00
u . RawQuery = s
2021-07-29 03:47:05 +00:00
case symbol :
u . RawQuery = string ( s )
default :
2021-08-10 03:16:09 +00:00
return exception ( "'url-query' expects either a string or a symbol as its second argument, a non-string non-symbol value was given" )
2021-07-29 03:47:05 +00:00
}
return u . String ( )
}
2021-10-07 22:57:09 +00:00
return u . RawQuery
2021-07-29 03:47:05 +00:00
} ,
"url-path" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'url-path' expects a url as a string, no value was given" )
2021-07-29 03:47:05 +00:00
}
rawURL , ok := a [ 0 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'url-path' expects a url as a string, a non-string value was given" )
2021-07-29 03:47:05 +00:00
}
u , err := url . Parse ( rawURL )
if err != nil {
return false
}
if len ( a ) >= 2 {
switch s := a [ 1 ] . ( type ) {
case string :
u . Path = s
case symbol :
u . Path = string ( s )
default :
2021-08-10 03:16:09 +00:00
return exception ( "'url-path' expects either a string or a symbol as its second argument, a non-string non-symbol value was given" )
2021-07-29 03:47:05 +00:00
}
return u . String ( )
}
return u . EscapedPath ( )
} ,
"url-scheme" : func ( a ... expression ) expression {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'url-scheme' expects a url as a string, no value was given" )
2021-07-29 03:47:05 +00:00
}
rawURL , ok := a [ 0 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'url-scheme' expects a url as a string, a non-string value was given" )
2021-07-29 03:47:05 +00:00
}
u , err := url . Parse ( rawURL )
if err != nil {
return false
}
if len ( a ) >= 2 {
switch s := a [ 1 ] . ( type ) {
case string :
u . Scheme = s
case symbol :
u . Scheme = string ( s )
default :
2021-08-10 03:16:09 +00:00
return exception ( "'url-scheme' expects either a string or a symbol as its second argument, a non-string non-symbol value was given" )
2021-07-29 03:47:05 +00:00
}
return u . String ( )
}
return u . Scheme
} ,
2021-07-31 20:25:20 +00:00
"license" : func ( a ... expression ) expression {
2022-04-28 05:59:02 +00:00
SysoutPrint ( licenseText , Sysout )
2021-07-31 20:25:20 +00:00
return make ( [ ] expression , 0 )
} ,
2021-09-04 14:45:35 +00:00
"chdir" : func ( a ... expression ) expression {
2021-08-09 22:53:27 +00:00
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'chdir' expects a path as a string, no value was given" )
2021-08-09 22:53:27 +00:00
}
p , ok := a [ 0 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'chdir' expects a string; a non-string value was given" )
2021-08-09 22:53:27 +00:00
}
err := os . Chdir ( ExpandedAbsFilepath ( p ) )
if err != nil {
2021-08-13 21:11:47 +00:00
return exception ( err . Error ( ) )
2021-08-09 22:53:27 +00:00
}
2022-08-31 21:41:56 +00:00
return make ( [ ] expression , 0 )
2021-08-09 22:53:27 +00:00
} ,
2021-09-04 14:45:35 +00:00
"chmod" : func ( a ... expression ) expression {
2021-08-09 22:53:27 +00:00
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'chmod' expects a path as a string and a file mode as a number, no value was given" )
2021-08-09 22:53:27 +00:00
}
p , ok := a [ 0 ] . ( string )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'chdir' expects a string; a non-string value was given" )
2021-08-09 22:53:27 +00:00
}
n , ok := a [ 1 ] . ( number )
err := os . Chmod ( ExpandedAbsFilepath ( p ) , os . FileMode ( n ) )
if err != nil {
2021-08-13 21:11:47 +00:00
return exception ( err . Error ( ) )
2021-08-09 22:53:27 +00:00
}
2022-08-31 21:41:56 +00:00
return make ( [ ] expression , 0 )
2021-08-09 22:53:27 +00:00
} ,
"env" : func ( a ... expression ) expression {
if len ( a ) == 0 {
e := make ( [ ] expression , 0 , 20 )
env := os . Environ ( )
for _ , v := range env {
sp := strings . Split ( v , "=" )
if len ( sp ) < 2 {
sp = append ( sp , "" )
}
e = append ( e , [ ] expression { sp [ 0 ] , sp [ 1 ] } )
}
return e
}
if len ( a ) == 1 {
switch i := a [ 0 ] . ( type ) {
case string :
return os . Getenv ( i )
default :
return exception ( "Invalid argument to single argument 'env' call, was not given a string" )
}
}
variable , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "Invalid argument to dual argument 'env' call, was not given a string as the first argument" )
}
2021-09-04 14:45:35 +00:00
err := os . Setenv ( variable , String ( a [ 1 ] , false ) )
2021-08-09 22:53:27 +00:00
if err != nil {
return exception ( err . Error ( ) )
}
2021-08-13 21:11:47 +00:00
return make ( [ ] expression , 0 )
2021-08-09 22:53:27 +00:00
} ,
2021-08-21 21:47:47 +00:00
"mkdir" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'mkdir' expects a path string and a number representing file permissions and optionally a bool; no value was given" )
}
path , ok1 := a [ 0 ] . ( string )
if ! ok1 {
return exception ( "'mkdir' expects a path string as its first argument, a non-string value was given" )
}
perms , ok2 := a [ 1 ] . ( number )
if ! ok2 {
return exception ( "'mkdir' expects a number representing a file permissions setting as its second argument, a non-number value was given" )
}
prmInt := os . FileMode ( perms )
var ok3 , mkdirAll bool
if len ( a ) > 2 {
mkdirAll , ok3 = a [ 2 ] . ( bool )
if ! ok3 {
return exception ( "'mkdir' expects a bool as its third argument, a non-bool value was given" )
}
}
var err error
if mkdirAll {
err = os . MkdirAll ( ExpandedAbsFilepath ( path ) , prmInt )
} else {
err = os . Mkdir ( ExpandedAbsFilepath ( path ) , prmInt )
}
if err != nil {
return exception ( "'mkdir' could not create directories for the given path with the given permissions: " + err . Error ( ) )
}
return make ( [ ] expression , 0 )
} ,
"rm" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'rm' expects a path string and, optionally, a bool; no value was given" )
}
path , ok1 := a [ 0 ] . ( string )
if ! ok1 {
return exception ( "'rm' expects a path string as its first argument, a non-string value was given" )
}
var ok2 , mkdirAll bool
if len ( a ) > 1 {
mkdirAll , ok2 = a [ 1 ] . ( bool )
if ! ok2 {
return exception ( "'rm' expects a bool as its second argument, a non-bool value was given" )
}
}
var err error
if mkdirAll {
err = os . RemoveAll ( ExpandedAbsFilepath ( path ) )
} else {
err = os . Remove ( ExpandedAbsFilepath ( path ) )
}
if err != nil {
return exception ( "'mkdir' could not create directories for the given path with the given permissions: " + err . Error ( ) )
}
return make ( [ ] expression , 0 )
} ,
"pwd" : func ( a ... expression ) expression {
wd , err := os . Getwd ( )
if err != nil {
return exception ( "'pwd' could not retrieve the current working directory: " + err . Error ( ) )
}
return wd
} ,
"mv" : func ( a ... expression ) expression {
if len ( a ) < 2 {
return exception ( "'mv' expected two path strings (from and to), but was given an insufficient number of arguments" )
}
from , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'mv' expected a path string as its first argument, a non-string value was given" )
}
to , ok2 := a [ 1 ] . ( string )
if ! ok2 {
return exception ( "'mv' expected a path string as its second argument, a non-string value was given" )
}
err := os . Rename ( from , to )
if err != nil {
return exception ( "'mv' could not perform the requested action: " + err . Error ( ) )
}
return make ( [ ] expression , 0 )
} ,
2021-08-13 06:02:54 +00:00
"subprocess" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'subprocess' expects a list containing the process to start and any arguments, as strings. It can also, optionally, take 1 or 2 file handles to write Stdout and Stderr to. No arguments were given." )
}
processList , ok := a [ 0 ] . ( [ ] expression )
if ! ok {
return exception ( "'subprocess' expects a list containing the process to start and any arguments as separate strings in the list. A non-list was given." )
}
process := make ( [ ] string , len ( processList ) )
for i := range processList {
if s , ok := processList [ i ] . ( string ) ; ok {
process [ i ] = s
} else {
return exception ( "a list passed to 'subprocess' contained the non-string value: " + String ( processList [ i ] , true ) )
}
}
2021-08-13 22:45:34 +00:00
2022-05-14 23:01:39 +00:00
var o , e , i * IOHandle
if len ( a ) > 3 {
i1 , i1ok := a [ 3 ] . ( * IOHandle )
if ! i1ok {
if b , ok := a [ 3 ] . ( bool ) ; ! ok || ( ok && b ) {
return exception ( "'subprocess' was given an input redirection that is not an IOHandle or the boolean '#f'" )
}
} else if ! i1 . Open {
return exception ( "'subprocess' was given an input redirection IOHandle that is already closed" )
}
i = i1
}
2021-08-13 22:45:34 +00:00
if len ( a ) > 2 {
e1 , e1ok := a [ 2 ] . ( * IOHandle )
if ! e1ok {
if b , ok := a [ 2 ] . ( bool ) ; ! ok || ( ok && b ) {
return exception ( "'subprocess' was given an error redirection that is not an IOHandle or the boolean '#f'" )
}
} else if ! e1 . Open {
return exception ( "'subprocess' was given an error redirection IOHandle that is already closed" )
}
e = e1
}
if len ( a ) > 1 {
o1 , o1ok := a [ 1 ] . ( * IOHandle )
if ! o1ok {
if b , ok := a [ 1 ] . ( bool ) ; ! ok || ( ok && b ) {
return exception ( "'subprocess' was given an output redirection that is not an IOHandle or the boolean '#f'" )
}
} else if ! o1 . Open {
return exception ( "'subprocess' was given an output redirection IOHandle that is already closed" )
}
o = o1
}
2021-08-13 06:02:54 +00:00
cmd := exec . Command ( process [ 0 ] , process [ 1 : ] ... )
cmd . Stdin = os . Stdin
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
2021-08-13 22:45:34 +00:00
if o != nil {
switch io := o . Obj . ( type ) {
case * os . File :
cmd . Stdout = io
case * net . Conn :
cmd . Stdout = ( * io )
case * tls . Conn :
cmd . Stdout = io
case * strings . Builder :
cmd . Stdout = io
default :
return exception ( "'subprocess' was given an output redirection IOHandle of an unknown type" )
}
}
if e != nil {
switch io := e . Obj . ( type ) {
case * os . File :
cmd . Stderr = io
case * net . Conn :
cmd . Stderr = ( * io )
case * tls . Conn :
cmd . Stderr = io
case * strings . Builder :
cmd . Stderr = io
default :
return exception ( "'subprocess' was given an error redirection IOHandle of an unknown type" )
}
}
2022-05-14 23:01:39 +00:00
if i != nil {
switch io := i . Obj . ( type ) {
case * os . File :
cmd . Stdin = io
case * net . Conn :
cmd . Stdin = ( * io )
case * tls . Conn :
cmd . Stdin = io
case * strings . Builder :
cmd . Stdin = strings . NewReader ( io . String ( ) )
default :
return exception ( "'subprocess' was given an input redirection IOHandle of an unknown type" )
}
}
2021-08-13 06:02:54 +00:00
err := cmd . Run ( )
if err != nil {
if exiterr , ok := err . ( * exec . ExitError ) ; ok {
if status , ok := exiterr . Sys ( ) . ( syscall . WaitStatus ) ; ok {
return number ( status . ExitStatus ( ) )
}
} else {
return exception ( "'subprocess' encountered an error running the given process and the process could not be run" )
}
2021-09-04 14:45:35 +00:00
}
2021-08-13 06:02:54 +00:00
return number ( 0 )
} ,
2021-08-15 06:16:29 +00:00
"term-size" : func ( a ... expression ) expression {
cols , rows := termios . GetWindowSize ( )
return [ ] expression { number ( cols ) , number ( rows ) }
} ,
"term-restore" : func ( a ... expression ) expression {
termios . Restore ( )
return make ( [ ] expression , 0 )
} ,
"term-char-mode" : func ( a ... expression ) expression {
termios . SetCharMode ( )
return make ( [ ] expression , 0 )
} ,
"term-raw-mode" : func ( a ... expression ) expression {
termios . SetRawMode ( )
return make ( [ ] expression , 0 )
} ,
"term-sane-mode" : func ( a ... expression ) expression {
termios . SetSaneMode ( )
return make ( [ ] expression , 0 )
} ,
"term-cooked-mode" : func ( a ... expression ) expression {
termios . SetCookedMode ( )
return make ( [ ] expression , 0 )
} ,
2021-08-15 22:49:38 +00:00
"timestamp" : func ( a ... expression ) expression {
2021-08-19 03:12:49 +00:00
return number ( time . Now ( ) . Unix ( ) )
} ,
"date" : func ( a ... expression ) expression {
// (date [[format: string]])
2021-08-15 22:49:38 +00:00
if len ( a ) == 0 {
2021-08-19 03:12:49 +00:00
return time . Now ( ) . Format ( "Mon Jan 2 15:04:05 -0700 MST 2006" )
2021-08-15 22:49:38 +00:00
}
2021-08-19 03:12:49 +00:00
layout , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'date' was given a non-string layout value" )
}
return time . Now ( ) . Format ( createTimeFormatString ( layout ) )
} ,
"timestamp->date" : func ( a ... expression ) expression {
// (timestamp->date [timestamp: string] [[layout: string]])
if len ( a ) >= 1 {
layout := "Mon Jan 2 15:04:05 -0700 MST 2006"
2021-08-15 22:49:38 +00:00
var dateint int64
2021-08-19 03:12:49 +00:00
switch d := a [ 0 ] . ( type ) {
2021-08-15 22:49:38 +00:00
case string :
di , err := strconv . ParseInt ( d , 10 , 64 )
if err != nil {
2021-08-19 03:12:49 +00:00
return exception ( "'timestamp->date' was given an invalid timestamp string, it could not be converted to a number" )
2021-08-15 22:49:38 +00:00
}
dateint = di
case number :
dateint = int64 ( d )
default :
2021-08-19 03:12:49 +00:00
return exception ( "'timestamp->date' was given a non-string non-number value as a timestamp" )
2021-08-15 22:49:38 +00:00
}
2021-08-19 03:12:49 +00:00
if len ( a ) >= 2 {
var ok1 bool
layout , ok1 = a [ 1 ] . ( string )
if ! ok1 {
return exception ( "'timestamp->date' was given a non-string layout value" )
}
2021-08-19 03:30:21 +00:00
layout = createTimeFormatString ( layout )
2021-08-19 03:12:49 +00:00
}
2021-08-15 22:49:38 +00:00
t := time . Unix ( dateint , 0 )
2021-08-19 03:30:21 +00:00
return t . Local ( ) . Format ( layout )
2021-08-15 22:49:38 +00:00
}
2021-08-19 03:12:49 +00:00
return exception ( "'timestamp->date' received an insufficient argument count, expected 1 or 2 arguments" )
2021-08-15 22:49:38 +00:00
} ,
2021-08-19 03:12:49 +00:00
"date-format" : func ( a ... expression ) expression {
// (date-format [input-format: string] [input-date: string] [output-format: string])
2021-08-15 22:49:38 +00:00
if len ( a ) == 3 {
// datestring->datestring
layout1 , ok1 := a [ 0 ] . ( string )
datestring , ok2 := a [ 1 ] . ( string )
layout2 , ok3 := a [ 2 ] . ( string )
if ! ok1 || ! ok2 || ! ok3 {
2021-08-19 03:12:49 +00:00
return exception ( "'date-format' was given a non-string argument, it expects strings" )
2021-08-15 22:49:38 +00:00
}
2021-08-19 03:12:49 +00:00
t , err := time . ParseInLocation ( createTimeFormatString ( layout1 ) , datestring , time . Now ( ) . Location ( ) )
2021-08-15 22:49:38 +00:00
if err != nil {
2021-08-19 03:12:49 +00:00
return exception ( "'date-format' could not parse the input date to a time value based on the given layout string" )
2021-08-15 22:49:38 +00:00
}
2021-08-19 03:12:49 +00:00
return t . Format ( createTimeFormatString ( layout2 ) )
}
return exception ( "'date-format' received an unexpected number of arguments" )
} ,
"date->timestamp" : func ( a ... expression ) expression {
// (date->timestamp [date: string] [format: string])
if len ( a ) == 2 {
layout1 , ok1 := a [ 1 ] . ( string )
datestring , ok2 := a [ 0 ] . ( string )
if ! ok1 || ! ok2 {
return exception ( "'date->timestamp' was given a non-string argument, it expects strings" )
}
t , err := time . ParseInLocation ( createTimeFormatString ( layout1 ) , datestring , time . Now ( ) . Location ( ) )
if err != nil {
return exception ( "'date->timestamp' could not parse the input date to a time value based on the given layout string" )
}
return number ( t . Unix ( ) )
2021-08-15 22:49:38 +00:00
}
return exception ( "'date' received an unexpected number of arguments" )
} ,
2021-08-19 03:30:21 +00:00
"date-default-format" : func ( a ... expression ) expression {
return "%w %f %d %g:%I:%S %O %Z %Y"
} ,
2021-08-19 03:12:49 +00:00
"sleep" : func ( a ... expression ) expression {
// (sleep [ms: number])
if len ( a ) > 0 {
ms , ok := a [ 0 ] . ( number )
if ! ok {
return exception ( "'sleep' was given a non-number value" )
}
time . Sleep ( time . Duration ( ms ) * time . Millisecond )
2021-08-15 22:49:38 +00:00
}
2021-08-19 03:12:49 +00:00
return make ( [ ] expression , 0 )
2021-08-15 22:49:38 +00:00
} ,
2022-06-08 21:26:30 +00:00
"rand-seed" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'rand-seed' expected a number, no value was given" )
}
seed , ok := a [ 0 ] . ( number )
if ! ok {
return exception ( "'rand-seed' expected a number, a non-number value was given" )
}
rand . Seed ( int64 ( seed ) )
return true
} ,
2021-08-23 05:38:54 +00:00
"rand" : func ( a ... expression ) expression {
if len ( a ) == 0 {
// equal to: (rand 1 0)
return number ( rand . Float64 ( ) )
}
var max , min number
var ok bool
if len ( a ) >= 1 {
// equal to: (rand [max] 0)
max , ok = a [ 0 ] . ( number )
if ! ok {
return exception ( "'rand' expected a number as its first argument, a non-number value was given" )
}
}
if len ( a ) == 1 {
return number ( rand . Float64 ( ) * float64 ( max ) )
}
// equal to: (rand [max] [min])
min , ok = a [ 1 ] . ( number )
if ! ok {
return exception ( "'rand' expected a number as its second argument, a non-number value was given" )
}
2021-09-04 14:45:35 +00:00
return number ( rand . Float64 ( ) * ( float64 ( max ) - float64 ( min ) ) + float64 ( min ) )
2021-08-23 22:39:48 +00:00
} ,
"string->md5" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'string->md5' expects a string, no value was given" )
}
s , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'string->md5' expected a string as its first argument, a non-string argument was given" )
}
return fmt . Sprintf ( "%x" , md5 . Sum ( [ ] byte ( s ) ) )
} ,
"string->sha256" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return exception ( "'string->sha256' expects a string, no value was given" )
}
s , ok := a [ 0 ] . ( string )
if ! ok {
return exception ( "'string->sha256' expected a string as its first argument, a non-string argument was given" )
}
return fmt . Sprintf ( "%x" , sha256 . Sum256 ( [ ] byte ( s ) ) )
2021-08-23 05:38:54 +00:00
} ,
2022-08-26 21:13:00 +00:00
"ns-create" : func ( a ... expression ) expression {
2022-08-26 20:19:36 +00:00
if len ( a ) == 0 {
2022-08-26 21:13:00 +00:00
return exception ( "'ns-create' expects a string, no argument was given" )
2022-08-26 20:19:36 +00:00
}
s := String ( a [ 0 ] , false )
2022-08-26 21:13:00 +00:00
if strings . ContainsAny ( s , " ()\"#':" ) {
return exception ( "'ns-create' cannot create an env with a name containing any of the following characters: \033[1m:'( )\"#\033[0m" )
2022-08-26 20:19:36 +00:00
}
if _ , ok := namespaces [ s ] ; ok {
2022-08-26 21:13:00 +00:00
return exception ( "'ns-create' cannot create an env with a name that is already in use" )
2022-08-26 20:19:36 +00:00
}
namespaces [ s ] = env { vars ( map [ symbol ] expression { } ) , & globalenv }
// Add magic string (slope custom env)
namespaces [ s ] . vars [ magicCustomEnv ] = true
return true
} ,
2022-08-26 21:13:00 +00:00
"ns-delete" : func ( a ... expression ) expression {
2022-08-26 20:19:36 +00:00
if len ( a ) == 0 {
2022-08-26 21:13:00 +00:00
return exception ( "'ns-delete' expects a string representing an env name and, optionally, a string representing a symbol in the given env. No argument was given" )
2022-08-26 20:19:36 +00:00
}
s := String ( a [ 0 ] , false )
if _ , ok := namespaces [ s ] ; ok {
if _ , ok := namespaces [ s ] . vars [ magicCustomEnv ] ; ok {
if len ( a ) > 1 {
delete ( namespaces [ s ] . vars , symbol ( String ( a [ 1 ] , false ) ) )
} else {
delete ( namespaces , s )
}
return true
} else {
2022-08-26 21:13:00 +00:00
return exception ( "'ns-delete' cannot delete loaded environments, only custom environments made with 'ns-create'" )
2022-08-26 20:19:36 +00:00
}
}
2022-08-26 21:13:00 +00:00
return exception ( "'ns-delete' could not find the env in question" )
2022-08-26 20:19:36 +00:00
} ,
2022-08-26 21:13:00 +00:00
"ns-list" : func ( a ... expression ) expression {
2022-08-26 20:19:36 +00:00
if len ( a ) > 0 {
s := String ( a [ 0 ] , false )
if ns , ok := namespaces [ s ] ; ok {
if _ , magicPresent := ns . vars [ magicCustomEnv ] ; magicPresent {
symbols := make ( [ ] expression , 0 , 5 )
for k , _ := range ns . vars {
if k == magicCustomEnv {
continue
}
symbols = append ( symbols , string ( k ) )
}
return symbols
} else {
2022-08-26 21:13:00 +00:00
return exception ( "'ns-list' cannot list loaded environments, only custom environments made with 'ns-create'" )
2022-08-26 20:19:36 +00:00
}
} else {
2022-08-26 21:13:00 +00:00
return exception ( "'ns-list' could not find the requested env" )
2022-08-26 20:19:36 +00:00
}
} else {
envs := make ( [ ] expression , 0 , 5 )
for k , v := range namespaces {
if _ , ok := v . vars [ magicCustomEnv ] ; ok {
envs = append ( envs , k )
}
}
return envs
}
} ,
2022-08-26 21:13:00 +00:00
"ns-define" : func ( a ... expression ) expression {
2022-08-26 20:19:36 +00:00
if len ( a ) < 3 {
2022-08-26 21:13:00 +00:00
return exception ( "'ns-define' expects three values: the env name, the symbol name, and a value. Insufficient arguments were given" )
2022-08-26 20:19:36 +00:00
}
envS := String ( a [ 0 ] , false )
symS := String ( a [ 1 ] , false ) // FIXME this should ensure string or symbol and not coerce
if _ , ok := namespaces [ envS ] ; ok {
if _ , ok := namespaces [ envS ] . vars [ magicCustomEnv ] ; ok {
namespaces [ envS ] . vars [ symbol ( symS ) ] = a [ 2 ]
return a [ 2 ]
} else {
2022-08-26 21:13:00 +00:00
return exception ( "'ns-define' can only define new variables for custom environments made with 'ns-create'" )
2022-08-26 20:19:36 +00:00
}
}
2022-08-26 21:13:00 +00:00
return exception ( "'ns-define' could not find the env in question" )
2022-08-26 20:19:36 +00:00
} ,
2022-08-26 21:13:00 +00:00
"ns->string" : func ( a ... expression ) expression {
2022-08-26 20:19:36 +00:00
if len ( a ) < 1 {
2022-08-26 21:13:00 +00:00
return exception ( "'ns->string' requires the name of an env, no arguments were given" )
2022-08-26 20:19:36 +00:00
}
s := String ( a [ 0 ] , false )
var buf strings . Builder
ns , ok := namespaces [ s ]
if ! ok {
2022-08-26 21:13:00 +00:00
return exception ( "'ns->string' could not find the requested env" )
2022-08-26 20:19:36 +00:00
}
if _ , magicPresent := ns . vars [ magicCustomEnv ] ; magicPresent {
buf . WriteString ( ";;; " )
buf . WriteString ( s )
2022-08-26 21:13:00 +00:00
buf . WriteString ( "\n;; Exported via ns->string:\n\n" )
2022-08-26 20:19:36 +00:00
buf . WriteString ( strings . Join ( ExpressionSliceToStringSlice ( a [ 1 : ] ) , "\n\n" ) )
if len ( a [ 1 : ] ) > 0 {
buf . WriteString ( "\n\n" )
}
for k , v := range ns . vars {
if k == magicCustomEnv {
continue
}
buf . WriteString ( "(define " )
buf . WriteString ( string ( k ) )
buf . WriteRune ( ' ' )
buf . WriteString ( strings . ReplaceAll ( String ( v , true ) , fmt . Sprintf ( "%s::" , s ) , "" ) )
buf . WriteString ( ")\n\n" )
}
return buf . String ( )
}
2022-08-26 21:13:00 +00:00
return exception ( "'ns->string' cannot export loaded environments, only custom environments made with 'ns-create'" )
2022-08-26 20:19:36 +00:00
} ,
2022-10-10 15:58:25 +00:00
"coeval" : func ( a ... expression ) expression {
// (coeval [proc [args]]...)
// Takes a variable number of args. All args should
// be a list containing a proc and a list of args to
// that proc
//
// Use apply instead of eval (which was used in the special form)
// Pass 'coeval - arg (#)' as the name for apply
if len ( a ) == 0 {
return exception ( "'coeval' expects at least one argument (a list containing a procedure and a list of arguments to the procedure)" )
}
for i := range a {
if list , ok := a [ i ] . ( [ ] expression ) ; ok {
if len ( list ) == 0 {
return exception ( "'coeval' expects lists with a proc and a list of arguments, an empty list was given" )
}
_ , procOk := list [ 0 ] . ( proc )
_ , funcOk := list [ 0 ] . ( func ( ... expression ) expression )
if ! procOk && ! funcOk {
return exception ( "'The first item in a list given to 'coeval' should be a procedure, a non-procedure value was given" )
}
} else {
return exception ( "'coeval' expects its arguments to be lists, a non-list value was given" )
}
}
// Validate args here before moving on
wg := new ( sync . WaitGroup )
wg . Add ( len ( a ) )
for i := range a {
input := a [ i ] . ( [ ] expression )
var list [ ] expression
if len ( input ) == 1 {
list = make ( [ ] expression , 0 )
} else {
switch item := input [ 1 ] . ( type ) {
case [ ] expression :
list = item
default :
list = [ ] expression { item }
}
}
go func ( w * sync . WaitGroup , p expression , args [ ] expression , name expression ) {
defer w . Done ( )
apply ( p , args , name )
} ( wg , input [ 0 ] , list , fmt . Sprintf ( "Coeval procedure argument %d" , i ) )
}
wg . Wait ( )
return make ( [ ] expression , 0 )
} ,
2021-07-26 05:31:22 +00:00
}