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"
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"
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"
2021-08-13 06:02:54 +00:00
"syscall"
2021-07-28 05:45:52 +00:00
"time"
2021-08-15 06:16:29 +00:00
"git.rawtext.club/sloum/slope/termios"
2021-07-26 05:31:22 +00:00
)
var stdLibrary = vars {
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
} ,
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"
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 ) ) )
} ,
"+" : 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
} ,
"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 {
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'equal?' expected, but did not receive, two values" )
2021-07-26 05:31:22 +00:00
}
return reflect . DeepEqual ( a [ 0 ] , a [ 1 ] )
} ,
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
} ,
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 {
l [ 1 ] = a [ 2 ]
list [ x ] = l
return list
}
2021-08-10 03:16:09 +00:00
return l [ 1 ]
}
}
2021-08-24 05:49:20 +00:00
if len ( a ) >= 3 {
list = append ( list , [ ] expression { a [ 1 ] , a [ 2 ] } )
return list
2021-08-10 03:16:09 +00:00
}
2021-08-24 05:49:20 +00:00
return exception ( "'assoc' could not find the key value " + 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
}
} ,
"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
}
} ,
"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
prev := ' '
parsePercent := false
slash := 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 )
}
} else if c == '%' && ! slash {
parsePercent = true
} else if c == '%' {
outputBuf . WriteRune ( c )
} else if c == '\\' {
slash = true
} else {
if slash {
outputBuf . WriteRune ( prev )
slash = false
}
outputBuf . WriteRune ( c )
prev = c
}
2021-07-29 22:36:11 +00:00
}
2021-08-16 05:45:05 +00:00
return 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 {
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'string-index-of' expects a string and a number; 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-ref' expects a string as its first argument; a non-string value was given" )
2021-08-08 03:03:46 +00:00
}
index , ok := a [ 1 ] . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'string-ref' expects a number as its first argument; a non-number value was given" )
2021-08-08 03:03:46 +00:00
}
i := int ( index )
if i >= len ( haystack ) || i < 0 {
2021-08-10 03:16:09 +00:00
return exception ( "'string-ref' was given a reference index that is out of bounds for the given string" )
2021-08-08 03:03:46 +00:00
}
return string ( haystack [ int ( index ) ] )
} ,
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 )
} ,
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 )
} ,
2021-07-26 05:31:22 +00:00
"append" : func ( a ... expression ) expression {
if len ( a ) == 0 {
return make ( [ ] expression , 0 )
}
mainList , ok := a [ 0 ] . ( [ ] expression )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'append' expects argument 1 to be a list, a non-list was given" )
2021-07-26 05:31:22 +00:00
}
for _ , v := range a [ 1 : ] {
switch i := v . ( type ) {
case [ ] expression :
mainList = append ( mainList , i ... )
default :
mainList = append ( mainList , i )
}
}
return mainList
} ,
2021-07-31 20:25:20 +00:00
"list" : eval ( Parse ( "(lambda z z)" ) . ( [ ] expression ) [ 0 ] , & globalenv ) ,
2021-07-31 06:31:08 +00:00
"list-ref" : func ( a ... expression ) expression {
if len ( a ) < 2 {
2021-08-10 03:16:09 +00:00
return exception ( "'list-ref' expects 2 arguments, a list and a number, insufficient arguments were given" )
2021-07-31 06:31:08 +00:00
}
list , ok := a [ 0 ] . ( [ ] expression )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'list-ref' expects argument one to be a list, a non-list argument was given" )
2021-07-31 06:31:08 +00:00
}
index , ok := a [ 1 ] . ( number )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'list-ref' expects argument two to be a whole number, a non-number argument was given" )
2021-07-31 06:31:08 +00:00
}
if int ( index ) >= len ( list ) {
2021-08-10 03:16:09 +00:00
return exception ( "the list reference value is larger than the number of items in the list given to 'list-ref'" )
2021-07-31 06:31:08 +00:00
}
2021-08-01 22:38:40 +00:00
if len ( a ) >= 3 {
list [ int ( index ) ] = a [ 2 ]
return list
}
2021-07-31 06:31:08 +00:00
return list [ int ( index ) ]
} ,
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
}
} ,
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 {
out = append ( out , apply ( i , mergedLists [ merge ] . ( [ ] expression ) ) )
}
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
}
} ,
"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 {
apply ( i , mergedLists [ merge ] . ( [ ] expression ) )
}
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
}
fmt . Print ( out . String ( ) )
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' )
}
}
fmt . Print ( out . String ( ) )
return make ( [ ] expression , 0 )
} ,
2021-07-26 05:31:22 +00:00
"write" : func ( a ... expression ) expression {
if len ( a ) == 1 {
2021-09-04 14:45:35 +00:00
fmt . Print ( String ( a [ 0 ] , false ) )
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
} ,
2021-07-27 03:19:59 +00:00
"write-raw" : func ( a ... expression ) expression {
if len ( a ) == 1 {
2021-08-12 21:20:02 +00:00
fmt . Print ( String ( a [ 0 ] , true ) )
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 {
reader := bufio . NewReader ( os . Stdin )
text , err := reader . ReadString ( '\n' )
if err != nil {
return false
}
return text [ : len ( text ) - 1 ]
} 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
}
}
} ,
"read-all-lines" : func ( a ... expression ) expression {
if len ( a ) == 0 {
reader := bufio . NewReader ( os . Stdin )
text , err := reader . ReadString ( '\n' )
if err != nil {
2021-08-13 16:34:42 +00:00
return exception ( "'read-all-lines' could not read from the given IOHandle (Stdin)" )
2021-07-28 05:45:52 +00:00
}
return strings . SplitN ( text [ : len ( text ) - 1 ] , "\n" , - 1 )
} else {
obj , ok := a [ 0 ] . ( * IOHandle )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'read-all-lines' expected an IO handle as its argument, but was not given an IO handle" )
2021-07-28 05:45:52 +00:00
}
if ! obj . Open {
2021-08-10 03:16:09 +00:00
return exception ( "'read-all-lines' was given an IO handle that is already closed" )
2021-07-28 05:45:52 +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-lines' could not read from the given IOHandle" )
2021-07-28 05:45:52 +00:00
}
2021-07-29 03:47:05 +00:00
o := strings . SplitN ( string ( b ) , "\n" , - 1 )
return StringSliceToExpressionSlice ( o ) . ( [ ] expression )
2021-07-28 05:45:52 +00:00
case * net . Conn :
b , err := ioutil . ReadAll ( * f )
if err != nil {
2021-08-13 16:34:42 +00:00
return exception ( "'read-all-lines' could not read from the given IOHandle" )
2021-07-28 05:45:52 +00:00
}
2021-07-29 03:47:05 +00:00
o := strings . SplitN ( string ( b ) , "\n" , - 1 )
return StringSliceToExpressionSlice ( o ) . ( [ ] expression )
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-lines' could not read from the given IOHandle" )
2021-07-28 05:45:52 +00:00
}
2021-07-29 03:47:05 +00:00
o := strings . SplitN ( string ( b ) , "\n" , - 1 )
return StringSliceToExpressionSlice ( o ) . ( [ ] expression )
2021-08-10 23:09:00 +00:00
case * strings . Builder :
s := f . String ( )
o := strings . SplitN ( s , "\n" , - 1 )
return StringSliceToExpressionSlice ( o ) . ( [ ] expression )
2021-07-28 05:45:52 +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-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
switch f := obj . Obj . ( type ) {
case * os . File :
reader := bufio . NewReader ( f )
char , _ , err := reader . ReadRune ( )
if err != nil {
2021-08-15 06:18:04 +00:00
return exception ( "'read-char' could not read from the given IOHandle" )
2021-08-15 06:16:29 +00:00
}
return fmt . Sprintf ( "%c" , char )
case * net . Conn :
reader := bufio . NewReader ( * f )
char , _ , err := reader . ReadRune ( )
if err != nil {
2021-08-15 06:18:04 +00:00
return exception ( "'read-char' could not read from the given IOHandle" )
2021-08-15 06:16:29 +00:00
}
return fmt . Sprintf ( "%c" , char )
case * tls . Conn :
reader := bufio . NewReader ( f )
char , _ , err := reader . ReadRune ( )
if err != nil {
2021-08-15 06:18:04 +00:00
return exception ( "'read-char' could not read from the given IOHandle" )
2021-08-15 06:16:29 +00:00
}
return fmt . Sprintf ( "%c" , char )
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 {
reader := bufio . NewReader ( os . Stdin )
text , err := reader . ReadString ( '\n' )
if err != nil {
return false
}
return text [ : len ( text ) - 1 ]
2021-07-27 03:19:59 +00:00
} else {
2021-07-27 16:33:06 +00:00
obj , ok := a [ 0 ] . ( * IOHandle )
if ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'read-line' 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 ( "'read-line' was given an IO handle that is already closed" )
2021-07-27 16:33:06 +00:00
}
switch f := obj . Obj . ( type ) {
2021-07-27 03:19:59 +00:00
case * os . File :
var o strings . Builder
for {
b := make ( [ ] byte , 1 )
n , err := f . Read ( b )
if n > 0 {
if b [ 0 ] == '\n' {
break
}
o . Write ( b )
}
if err != nil && err != io . EOF && o . Len ( ) == 0 {
2021-08-13 16:34:42 +00:00
return exception ( "'read-line' could not read from the given IOHandle" )
2021-07-27 03:19:59 +00:00
} else if err != nil && err == io . EOF {
return symbol ( "EOF" )
} else if err != nil {
break
}
}
return o . String ( )
2021-07-27 22:35:00 +00:00
case * net . Conn :
var o strings . Builder
for {
b := make ( [ ] byte , 1 )
n , err := ( * f ) . Read ( b )
if n > 0 {
if b [ 0 ] == '\n' {
break
}
o . Write ( b )
}
if err != nil && err != io . EOF && o . Len ( ) == 0 {
2021-08-13 16:34:42 +00:00
return exception ( "'read-line' could not read from the given IOHandle" )
2021-07-27 22:35:00 +00:00
} else if err != nil && err == io . EOF {
return symbol ( "EOF" )
} else if err != nil {
break
}
}
return o . String ( )
2021-07-28 05:45:52 +00:00
case * tls . Conn :
var o strings . Builder
for {
b := make ( [ ] byte , 1 )
n , err := ( * f ) . Read ( b )
if n > 0 {
if b [ 0 ] == '\n' {
break
}
o . Write ( b )
}
if err != nil && err != io . EOF && o . Len ( ) == 0 {
2021-08-13 16:34:42 +00:00
return exception ( "'read-line' could not read from the given IOHandle" )
2021-07-28 05:45:52 +00:00
} else if err != nil && err == io . EOF {
return symbol ( "EOF" )
} else if err != nil {
break
}
}
return o . String ( )
2021-07-27 03:19:59 +00:00
default :
2021-08-10 03:16:09 +00:00
return exception ( "'read-line' has not been implemented for this object type" )
2021-07-27 03:19:59 +00:00
}
2021-07-26 05:31:22 +00:00
}
} ,
"newline" : func ( a ... expression ) expression {
fmt . Print ( "\n" )
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
} ,
"substring" : func ( a ... expression ) expression {
if len ( a ) < 3 {
2021-08-10 03:16:09 +00:00
return exception ( "insufficient number of arguments given to 'substring'" )
2021-07-26 05:31:22 +00:00
}
var s string
var n1 , n2 number
var ok bool
if s , ok = a [ 0 ] . ( string ) ; ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'substring' expects a string as its first argument" )
2021-07-26 05:31:22 +00:00
}
if n1 , ok = a [ 1 ] . ( number ) ; ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'substring' expects a number as its second argument" )
2021-07-26 05:31:22 +00:00
}
if n2 , ok = a [ 2 ] . ( number ) ; ! ok {
2021-08-10 03:16:09 +00:00
return exception ( "'substring' expects a number as its second argument" )
2021-07-26 05:31:22 +00:00
}
2021-07-31 20:25:20 +00:00
if n1 < 0 || n1 >= n2 {
2021-08-10 03:16:09 +00:00
return exception ( "start position of 'substring' is out of range" )
2021-07-26 05:31:22 +00:00
}
if int ( n2 ) > len ( s ) {
2021-08-10 03:16:09 +00:00
return exception ( "end position of 'substring' is out of range" )
2021-07-26 05:31:22 +00:00
}
return s [ int ( n1 ) : int ( n2 ) ]
} ,
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 {
if len ( a ) == 0 {
2021-08-10 03:16:09 +00:00
return exception ( "insufficient number of arguments given to 'string-append'" )
2021-07-26 05:31:22 +00:00
}
var out strings . Builder
for i := range a {
2021-08-25 20:37:42 +00:00
out . WriteString ( String ( a [ i ] , false ) )
2021-07-26 05:31:22 +00:00
}
return out . String ( )
} ,
"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
} ,
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" )
}
info , err := os . Stat ( ExpandedAbsFilepath ( fp ) )
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 ( ) )
}
2021-08-22 22:44:37 +00:00
out := make ( [ ] expression , 7 )
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 }
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-08-13 16:34:42 +00:00
return exception ( "'file-write' could not open the given filepath" )
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-08-13 16:34:42 +00:00
return exception ( "'file-append-to' could not open the given filepath" )
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 {
e , ok := a [ 0 ] . ( string )
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
}
return p [ : li + 1 ] + e
}
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" )
}
g , err := filepath . Glob ( p )
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
} ,
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-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 ( )
}
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-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 ( )
}
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 ( )
}
return u . Query ( )
} ,
"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 {
fmt . Println ( licenseText )
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
}
2021-08-13 21:11:47 +00:00
return make ( [ ] exception , 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
}
2021-08-13 21:11:47 +00:00
return make ( [ ] exception , 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
var o , e * IOHandle
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" )
}
}
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 {
fmt . Println ( a )
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" )
}
fmt . Println ( layout1 )
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
} ,
2021-08-23 05:38:54 +00:00
"rand" : func ( a ... expression ) expression {
rand . Seed ( time . Now ( ) . UnixNano ( ) )
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
} ,
2021-07-26 05:31:22 +00:00
}
/ *
TODO :
Number based :
[ ] Adjust > , >= , < , <= to accept any number args and require all to be sequenced correctly for the given operator
[ ] modulo
[ ] remainder
[ ] quotient
[ ] expt
[ ] sin
[ ] cos
[ ] tan
[ ] atan
[ ] sqrt
IO Based :
2021-07-27 05:47:24 +00:00
[ ] file - seek
2021-07-26 05:31:22 +00:00
2021-07-27 03:19:59 +00:00
Net Based :
2021-07-28 05:45:52 +00:00
[ ] http - get
[ ] http - post
2021-07-26 05:31:22 +00:00
System Based :
2021-08-01 03:04:26 +00:00
[ ] eval
2021-07-26 05:31:22 +00:00
* /