2023-06-02 05:42:09 +00:00
/ *
Copyright ( C ) 2023 Brian Evans ( aka sloum ) . All rights reserved .
This source code is available under the terms of the ffsl , or ,
Floodgap Free Software License . A copy of the license has been
provided as the file ' LICENSE ' in the same folder as this source
code file . If for some reason it is not present , you can find the
terms of version 1 of the FFSL at the following URL :
https : //www.floodgap.com/software/ffsl/license.html
* /
2023-05-31 23:18:16 +00:00
package main
import (
2023-06-06 06:04:58 +00:00
"bufio"
2023-05-31 23:18:16 +00:00
"fmt"
2023-06-12 23:14:28 +00:00
"io"
"math"
2023-06-08 05:59:53 +00:00
"net"
"net/http"
"net/url"
2023-06-01 22:16:17 +00:00
"os"
2023-06-03 16:33:09 +00:00
"regexp"
2023-05-31 23:18:16 +00:00
"strconv"
"strings"
)
2023-06-03 16:33:09 +00:00
var (
listRefSugar * regexp . Regexp = regexp . MustCompile ( ` ^([^\s\[\]]+)\[([^\s\[\]]+)\]$ ` )
)
2023-06-02 05:42:09 +00:00
2023-06-14 20:51:43 +00:00
/ *
TODO Add the following ?
- path - abs , path - base , path - join , path - dir , path - glob
- cd , mkdir
- Update ` file-remove ` to also work on directories ? Or add separate ` rm ` that can optionally be recursive ?
2023-06-14 23:17:13 +00:00
- map
- filter ! and each ! are already there and because of how the stack works reduce can be achieved via each !
2023-06-14 20:51:43 +00:00
* /
2023-05-31 23:18:16 +00:00
func callKeyword ( kw token , r * tokenReader , en * env ) error {
switch kw . val . ( string ) {
case "+" :
2023-06-12 05:51:39 +00:00
return libAdd ( kw . line , kw . file )
2023-05-31 23:18:16 +00:00
case "-" :
2023-06-12 05:51:39 +00:00
return libSubtract ( kw . line , kw . file )
2023-05-31 23:18:16 +00:00
case "*" :
2023-06-12 05:51:39 +00:00
return libMultiply ( kw . line , kw . file )
2023-05-31 23:18:16 +00:00
case "/" :
2023-06-12 05:51:39 +00:00
return libDivide ( kw . line , kw . file )
2023-06-04 22:04:58 +00:00
case "var!" :
2023-06-12 05:51:39 +00:00
return libVar ( kw . line , r , en , kw . file )
2023-06-04 22:04:58 +00:00
case "scoped-var!" :
2023-06-12 05:51:39 +00:00
return libVarPlus ( kw . line , r , en , kw . file )
2023-06-04 22:04:58 +00:00
case "set!" :
2023-06-12 05:51:39 +00:00
return libSet ( kw . line , r , en , kw . file )
2023-06-04 22:04:58 +00:00
case "scoped-set!" :
2023-06-12 05:51:39 +00:00
return libSetPlus ( kw . line , r , en , kw . file )
2023-05-31 23:18:16 +00:00
case "dup" :
2023-06-12 05:51:39 +00:00
return libDuplicate ( kw . line , kw . file )
2023-05-31 23:18:16 +00:00
case "swap" :
2023-06-12 05:51:39 +00:00
return libSwap ( kw . line , kw . file )
2023-05-31 23:18:16 +00:00
case "over" :
2023-06-12 05:51:39 +00:00
return libOver ( kw . line , kw . file )
2023-06-13 23:19:18 +00:00
case "rot" :
return libRot ( kw . line , kw . file )
2023-05-31 23:18:16 +00:00
case "drop" :
2023-06-12 05:51:39 +00:00
return libDrop ( kw . line , kw . file )
2023-05-31 23:18:16 +00:00
case "cast" :
2023-06-12 05:51:39 +00:00
return libConvertType ( kw . line , kw . file )
2023-06-02 05:42:09 +00:00
case "and" :
2023-06-12 05:51:39 +00:00
return libLogicalAnd ( kw . line , kw . file )
2023-06-02 05:42:09 +00:00
case "or" :
2023-06-12 05:51:39 +00:00
return libLogicalOr ( kw . line , kw . file )
2023-06-01 22:16:17 +00:00
case "=" :
2023-06-12 05:51:39 +00:00
return libEqual ( kw . line , kw . file )
2023-06-01 22:16:17 +00:00
case ">" :
2023-06-12 05:51:39 +00:00
return libGreaterThan ( kw . line , kw . file )
2023-06-01 22:16:17 +00:00
case "<" :
2023-06-12 05:51:39 +00:00
return libLessThan ( kw . line , kw . file )
2023-06-01 22:16:17 +00:00
case "stackdump" :
return libStackDump ( )
case "clearstack" :
return libClearStack ( )
2023-06-07 06:17:21 +00:00
case "stackdepth" :
2023-06-12 05:51:39 +00:00
return libStackDepth ( kw . line , kw . file )
2023-06-04 22:04:58 +00:00
case "length" :
2023-06-12 05:51:39 +00:00
return libLength ( kw . line , kw . file )
2023-06-04 22:04:58 +00:00
case "append!" , "=>!" :
2023-06-12 05:51:39 +00:00
return libAppendSpecial ( kw . line , r , en , kw . file )
2023-06-04 22:04:58 +00:00
case "append" , "=>" :
2023-06-12 05:51:39 +00:00
return libAppend ( kw . line , kw . file )
2023-06-04 22:04:58 +00:00
case "list-get" , "<-" :
2023-06-12 05:51:39 +00:00
return libGetListItem ( kw . line , r , en , kw . file )
2023-06-04 22:04:58 +00:00
case "list-set!" , "->!" :
2023-06-12 05:51:39 +00:00
return libSetListItemSpecial ( kw . line , r , en , kw . file )
2023-06-04 22:04:58 +00:00
case "list-set" , "->" :
2023-06-12 05:51:39 +00:00
return libSetListItem ( kw . line , kw . file )
2023-06-05 15:33:11 +00:00
case "file-append" :
2023-06-12 05:51:39 +00:00
return libFileAppend ( kw . line , kw . file )
2023-06-05 15:33:11 +00:00
case "file-exists?" :
2023-06-12 05:51:39 +00:00
return libFileExists ( kw . line , kw . file )
2023-06-05 15:33:11 +00:00
case "file-read" :
2023-06-12 05:51:39 +00:00
return libFileRead ( kw . line , kw . file )
2023-06-05 15:33:11 +00:00
case "file-write" :
2023-06-12 05:51:39 +00:00
return libFileAppend ( kw . line , kw . file )
2023-06-05 22:45:53 +00:00
case "docstring!" :
2023-06-12 05:51:39 +00:00
return libDocstring ( kw . line , r , en , kw . file )
2023-06-06 06:04:58 +00:00
case "input" :
2023-06-12 05:51:39 +00:00
return libInput ( kw . line , kw . file )
2023-06-06 06:04:58 +00:00
case "re-match?" :
2023-06-12 05:51:39 +00:00
return libReMatch ( kw . line , kw . file )
2023-06-06 06:04:58 +00:00
case "re-find" :
2023-06-12 05:51:39 +00:00
return libReFind ( kw . line , kw . file )
2023-06-06 06:04:58 +00:00
case "re-replace" :
2023-06-12 05:51:39 +00:00
return libReReplace ( kw . line , kw . file )
2023-06-07 06:17:21 +00:00
case "slice" :
2023-06-12 05:51:39 +00:00
return libSlice ( kw . line , kw . file )
2023-06-08 05:59:53 +00:00
case "type" :
2023-06-12 05:51:39 +00:00
return libType ( kw . line , kw . file )
2023-06-08 05:59:53 +00:00
case "net-get" :
2023-06-12 05:51:39 +00:00
return libNetGet ( kw . line , kw . file )
2023-06-09 17:05:10 +00:00
case "throw" :
2023-06-12 05:51:39 +00:00
return libThrow ( kw . line , kw . file )
2023-06-13 20:22:22 +00:00
case "import" :
return libImport ( kw . line , kw . file , en )
2023-06-14 03:32:23 +00:00
case "each!" :
return libEach ( kw . line , r , en , kw . file )
2023-06-14 21:03:05 +00:00
case "filter!" :
return libFilter ( kw . line , r , en , kw . file )
2023-05-31 23:18:16 +00:00
default :
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Unknown keyword: `%s`" , kw . line , kw . val . ( string ) )
2023-05-31 23:18:16 +00:00
}
}
2023-06-04 22:04:58 +00:00
/ *
2023-06-06 06:04:58 +00:00
TODO
- [ X ] split ( implemented as ` / ` overload : ` STRING STRING / ` - > LIST )
- [ X ] join ( implemented as ` LIST STRING * ` - > STRING )
- [ X ] ` + ` supports ` STRING INT + ` to add a rune to a string as a char , would bytes be better ?
- [ X ] input ( scans a line of text )
2023-06-08 05:59:53 +00:00
- [ X ] net - get ( should support http / s , gopher , gemini )
2023-06-06 06:04:58 +00:00
- [ X ] regex - match ?
2023-06-07 06:17:21 +00:00
- [ X ] regex - find
2023-06-06 06:04:58 +00:00
- [ ] regex - replace
- [ ] string - index - of
2023-06-08 05:59:53 +00:00
- [ X ] slice , takes two INT from stack detailing the range , the range is fully inclusive and overflows of avialable list are prevented
2023-06-06 06:04:58 +00:00
- [ X ] Use ` + ` for list join if two lists are passed ( use append to add to a list )
2023-06-08 05:59:53 +00:00
- [ X ] type , for runtime type checking
2023-06-04 22:04:58 +00:00
* /
2023-05-31 23:18:16 +00:00
2023-06-12 05:51:39 +00:00
func libAdd ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
v2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
2023-05-31 23:18:16 +00:00
if v1 . kind == STRING && v2 . kind == STRING {
2023-06-06 06:04:58 +00:00
// String join
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { STRING , v1 . val . ( string ) + v2 . val . ( string ) , line , fp } )
2023-06-06 06:04:58 +00:00
} else if v1 . kind == LIST && v2 . kind == LIST {
// List join
l1 := v1 . val . ( list )
l2 := v2 . val . ( list )
nl := make ( [ ] token , len ( l1 . body ) + len ( l2 . body ) )
for i , lt := range l1 . body {
nl [ i ] = lt
}
for i , lt := range l2 . body {
nl [ i + len ( l1 . body ) ] = lt
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { LIST , list { nl } , line , fp } )
2023-06-06 06:04:58 +00:00
} else if v1 . kind == STRING && v2 . kind == INT {
i := v2 . val . ( int )
if i < 0 {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Adding a rune to a string is only valid with positive integers, got %d" , i )
2023-06-06 06:04:58 +00:00
}
s := v1 . val . ( string )
s += string ( rune ( i ) )
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { STRING , s , line , fp } )
2023-05-31 23:18:16 +00:00
} else if v1 . kind == INT && v2 . kind == INT {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { INT , v1 . val . ( int ) + v2 . val . ( int ) , line , fp } )
2023-05-31 23:18:16 +00:00
} else if v1 . kind == FLOAT && v2 . kind == FLOAT {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { FLOAT , v1 . val . ( float64 ) + v2 . val . ( float64 ) , line , fp } )
} else if v1 . kind == FLOAT && v2 . kind == INT {
err = globalStack . Push ( token { FLOAT , v1 . val . ( float64 ) + float64 ( v2 . val . ( int ) ) , line , fp } )
} else if v1 . kind == INT && v2 . kind == FLOAT {
err = globalStack . Push ( token { FLOAT , v2 . val . ( float64 ) + float64 ( v1 . val . ( int ) ) , line , fp } )
2023-05-31 23:18:16 +00:00
} else {
fmt . Println ( "STACK-DUMP: " , globalStack )
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Cannot `+` %s and %s" , kindToString ( v1 . kind ) , kindToString ( v2 . kind ) )
2023-06-05 22:45:53 +00:00
}
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-05-31 23:18:16 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libSubtract ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
v2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
2023-05-31 23:18:16 +00:00
if v1 . kind == STRING && v2 . kind == STRING {
s := strings . Replace ( v1 . val . ( string ) , v2 . val . ( string ) , "" , - 1 )
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { STRING , s , line , fp } )
2023-05-31 23:18:16 +00:00
} else if v1 . kind == INT && v2 . kind == INT {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { INT , v1 . val . ( int ) - v2 . val . ( int ) , line , fp } )
2023-05-31 23:18:16 +00:00
} else if v1 . kind == FLOAT && v2 . kind == FLOAT {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { FLOAT , v1 . val . ( float64 ) - v2 . val . ( float64 ) , line , fp } )
} else if v1 . kind == FLOAT && v2 . kind == INT {
err = globalStack . Push ( token { FLOAT , v1 . val . ( float64 ) - float64 ( v2 . val . ( int ) ) , line , fp } )
} else if v1 . kind == INT && v2 . kind == FLOAT {
err = globalStack . Push ( token { FLOAT , float64 ( v1 . val . ( int ) ) - v2 . val . ( float64 ) , line , fp } )
2023-05-31 23:18:16 +00:00
} else {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Cannot `-` %s and %s" , kindToString ( v1 . kind ) , kindToString ( v2 . kind ) )
2023-05-31 23:18:16 +00:00
}
2023-06-05 22:45:53 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 22:45:53 +00:00
}
2023-05-31 23:18:16 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libMultiply ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
v2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
2023-05-31 23:18:16 +00:00
if v1 . kind == STRING && v2 . kind == INT {
s := strings . Repeat ( v1 . val . ( string ) , v2 . val . ( int ) )
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { STRING , s , line , fp } )
2023-06-06 06:04:58 +00:00
} else if v1 . kind == LIST && v2 . kind == STRING {
l := v1 . val . ( list )
s := l . Join ( v2 . val . ( string ) )
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { STRING , s , line , fp } )
2023-05-31 23:18:16 +00:00
} else if v1 . kind == INT && v2 . kind == INT {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { INT , v1 . val . ( int ) * v2 . val . ( int ) , line , fp } )
2023-05-31 23:18:16 +00:00
} else if v1 . kind == FLOAT && v2 . kind == FLOAT {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { FLOAT , v1 . val . ( float64 ) * v2 . val . ( float64 ) , line , fp } )
} else if v1 . kind == FLOAT && v2 . kind == INT {
err = globalStack . Push ( token { FLOAT , v1 . val . ( float64 ) * float64 ( v2 . val . ( int ) ) , line , fp } )
} else if v1 . kind == INT && v2 . kind == FLOAT {
err = globalStack . Push ( token { FLOAT , v2 . val . ( float64 ) * float64 ( v1 . val . ( int ) ) , line , fp } )
2023-05-31 23:18:16 +00:00
} else {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Cannot `*` %s and %s" , kindToString ( v1 . kind ) , kindToString ( v2 . kind ) )
2023-05-31 23:18:16 +00:00
}
2023-06-05 22:45:53 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 22:45:53 +00:00
}
2023-05-31 23:18:16 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libDivide ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
v2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
2023-06-05 22:45:53 +00:00
if v1 . kind == STRING && v2 . kind == STRING {
l := strings . SplitN ( v1 . val . ( string ) , v2 . val . ( string ) , - 1 )
ts := make ( [ ] token , len ( l ) )
for i := range l {
2023-06-12 05:51:39 +00:00
ts [ i ] = token { STRING , l [ i ] , line , fp }
2023-06-05 22:45:53 +00:00
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { LIST , list { ts } , line , fp } )
2023-06-05 22:45:53 +00:00
} else if v1 . kind == INT && v2 . kind == INT {
2023-06-12 23:14:28 +00:00
if v2 . val . ( int ) == 0 {
return fmt . Errorf ( "Division by zero" )
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { INT , v1 . val . ( int ) / v2 . val . ( int ) , line , fp } )
2023-05-31 23:18:16 +00:00
} else if v1 . kind == FLOAT && v2 . kind == FLOAT {
2023-06-12 23:14:28 +00:00
if v2 . val . ( float64 ) == 0.0 {
return fmt . Errorf ( "Division by zero" )
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { FLOAT , v1 . val . ( float64 ) / v2 . val . ( float64 ) , line , fp } )
} else if v1 . kind == FLOAT && v2 . kind == INT {
2023-06-12 23:14:28 +00:00
if v2 . val . ( int ) == 0 {
return fmt . Errorf ( "Division by zero" )
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { FLOAT , v1 . val . ( float64 ) / float64 ( v2 . val . ( int ) ) , line , fp } )
} else if v1 . kind == INT && v2 . kind == FLOAT {
2023-06-12 23:14:28 +00:00
if v2 . val . ( float64 ) == 0.0 {
return fmt . Errorf ( "Division by zero" )
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { FLOAT , float64 ( v1 . val . ( int ) ) / v2 . val . ( float64 ) , line , fp } )
2023-05-31 23:18:16 +00:00
} else {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Cannot `/` %s and %s" , kindToString ( v1 . kind ) , kindToString ( v2 . kind ) )
2023-05-31 23:18:16 +00:00
}
2023-06-05 22:45:53 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 22:45:53 +00:00
}
2023-05-31 23:18:16 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libAppend ( line int , fp string ) error {
2023-06-03 16:33:09 +00:00
valueToken , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-03 16:33:09 +00:00
}
2023-06-04 22:04:58 +00:00
targetToken , err := globalStack . Pop ( )
2023-06-03 16:33:09 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-03 16:33:09 +00:00
}
2023-06-04 22:04:58 +00:00
switch targetToken . kind {
case LIST :
l := targetToken . val . ( list )
err = l . Append ( valueToken )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { LIST , l , line , fp } )
2023-06-04 22:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
case STRING :
s := targetToken . val . ( string )
s = s + toString ( valueToken , false )
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { STRING , s , line , fp } )
2023-06-04 22:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
default :
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Cannot `append` %s to %s" , kindToString ( valueToken . kind ) , kindToString ( targetToken . kind ) )
2023-06-03 16:33:09 +00:00
}
2023-06-04 22:04:58 +00:00
return nil
}
2023-06-03 16:33:09 +00:00
2023-06-12 05:51:39 +00:00
func libAppendSpecial ( line int , r * tokenReader , en * env , fp string ) error {
2023-06-04 22:04:58 +00:00
valueToken , err := globalStack . Pop ( )
2023-06-03 16:33:09 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-03 16:33:09 +00:00
}
2023-06-04 22:04:58 +00:00
t , err := r . Read ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Unexpectedly reached EOF" )
2023-06-03 16:33:09 +00:00
}
2023-06-04 22:04:58 +00:00
var fromVariable bool
var targetToken token = t
var targetTokenenv * env
if t . kind == SYMBOL {
targetTokenenv , err = en . Find ( t . val . ( string ) )
2023-06-03 16:33:09 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-03 16:33:09 +00:00
}
2023-06-04 22:04:58 +00:00
targetToken = targetTokenenv . vars [ t . val . ( string ) ]
fromVariable = true
}
switch targetToken . kind {
case LIST :
l := targetToken . val . ( list )
err = l . Append ( valueToken )
2023-06-03 16:33:09 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-03 16:33:09 +00:00
}
2023-06-04 22:04:58 +00:00
if fromVariable {
2023-06-12 05:51:39 +00:00
targetTokenenv . vars [ t . val . ( string ) ] = token { LIST , l , line , fp }
2023-06-04 22:04:58 +00:00
} else {
2023-06-12 05:51:39 +00:00
err := globalStack . Push ( token { LIST , l , targetToken . line , targetToken . file } )
2023-06-04 22:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
2023-06-03 16:33:09 +00:00
}
2023-06-04 22:04:58 +00:00
case STRING :
s := targetToken . val . ( string ) + toString ( valueToken , false )
if fromVariable {
2023-06-12 05:51:39 +00:00
targetTokenenv . vars [ t . val . ( string ) ] = token { STRING , s , targetToken . line , targetToken . file }
2023-06-04 22:04:58 +00:00
} else {
targetToken . val = s
2023-06-12 05:51:39 +00:00
err := globalStack . Push ( token { STRING , s , targetToken . line , targetToken . file } )
2023-06-04 22:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
2023-06-03 16:33:09 +00:00
}
2023-06-04 22:04:58 +00:00
default :
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`=>` cannot append to %s" , kindToString ( targetToken . kind ) )
2023-06-04 22:04:58 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libSetListItem ( line int , fp string ) error {
2023-06-04 22:04:58 +00:00
valueToken , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
indexToken , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
if indexToken . kind != INT {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`list-set`/`->` expected an INT index after a value but found %s" , kindToString ( indexToken . kind ) )
2023-06-04 22:04:58 +00:00
}
listToken , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
if listToken . kind != LIST {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`list-set`/`->` expected an LIST after an INT index and a value but found %s" , kindToString ( listToken . kind ) )
2023-06-04 22:04:58 +00:00
}
l := listToken . val . ( list )
err = l . Set ( indexToken . val . ( int ) , valueToken )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { LIST , l , line , fp } )
2023-06-04 22:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libSetListItemSpecial ( line int , r * tokenReader , en * env , fp string ) error {
2023-06-04 22:04:58 +00:00
valueToken , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
indexToken , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
if indexToken . kind != INT {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`list-set!`/`->!` expected an INT on TOS but got %s" , kindToString ( indexToken . kind ) )
2023-06-04 22:04:58 +00:00
}
t , err := r . Read ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
if t . kind != SYMBOL {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`list-set!`/`->!` expected to read in a SYMBOL but found %s" , kindToString ( t . kind ) )
2023-06-04 22:04:58 +00:00
}
targetTokenEnv , err := en . Find ( t . val . ( string ) )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
targetToken := targetTokenEnv . vars [ t . val . ( string ) ]
if targetToken . kind != LIST {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`list-set!`/`->!` cannot set a value in type %s" , kindToString ( targetToken . kind ) )
2023-06-04 22:04:58 +00:00
}
l := targetToken . val . ( list )
err = l . Set ( indexToken . val . ( int ) , valueToken )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libGetListItem ( line int , r * tokenReader , en * env , fp string ) error {
2023-06-04 22:04:58 +00:00
indexToken , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
if indexToken . kind != INT {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`<-`/`list-get` expected an INT underneath a LIST on the stack, LIST found but found %s instead of INT" , kindToString ( indexToken . kind ) )
2023-06-04 22:04:58 +00:00
}
listToken , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
if listToken . kind != LIST {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`<-`/`list-get` expected a LIST on TOS, but found %s" , kindToString ( listToken . kind ) )
2023-06-04 22:04:58 +00:00
}
l := listToken . val . ( list )
val , err := l . Get ( indexToken . val . ( int ) )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
err = globalStack . Push ( val )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-03 16:33:09 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libVar ( line int , r * tokenReader , en * env , fp string ) error {
2023-06-01 22:16:17 +00:00
typeToken , err := globalStack . Pop ( )
2023-05-31 23:18:16 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-05-31 23:18:16 +00:00
}
2023-06-01 22:16:17 +00:00
if typeToken . kind != TYPE {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`var!` expects TYPE on the top of the stack" )
2023-05-31 23:18:16 +00:00
}
2023-06-01 22:16:17 +00:00
newToken := token { }
newToken . kind = typeToken . val . ( int )
newToken . line = line
2023-06-12 05:51:39 +00:00
newToken . file = fp
2023-06-01 22:16:17 +00:00
t , err := r . Read ( )
2023-05-31 23:18:16 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Unexpectedly reached EOF" )
2023-05-31 23:18:16 +00:00
}
2023-06-01 22:16:17 +00:00
if t . kind != SYMBOL {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "A non-symbol value was given as an identifier to `var!`" )
2023-06-01 22:16:17 +00:00
}
newToken . val = typeInit ( newToken . kind )
2023-05-31 23:18:16 +00:00
en . Add ( t . val . ( string ) , newToken )
2023-06-02 17:12:23 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libVarPlus ( line int , r * tokenReader , en * env , fp string ) error {
2023-06-02 17:12:23 +00:00
scope , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-02 17:12:23 +00:00
}
if scope . kind != INT {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`scoped-var!` expected an INT declaring how many scopes to jump up, found %s" , kindToString ( scope . kind ) )
2023-06-02 17:12:23 +00:00
}
for i := scope . val . ( int ) ; i > 0 ; i -- {
if en . parent != nil {
en = en . parent
} else {
break
}
}
typeToken , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-02 17:12:23 +00:00
}
if typeToken . kind != TYPE {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`scoped-var!` expects a TYPE literal on the the stack after an INT, found %s" , kindToString ( typeToken . kind ) )
2023-06-02 17:12:23 +00:00
}
newToken := token { }
newToken . kind = typeToken . val . ( int )
newToken . line = line
2023-06-12 05:51:39 +00:00
newToken . file = fp
2023-06-02 17:12:23 +00:00
t , err := r . Read ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Unexpectedly reached EOF" )
2023-06-02 17:12:23 +00:00
}
if t . kind != SYMBOL {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "A non-symbol value was given as an identifier to `scoped-var!`" )
2023-06-02 17:12:23 +00:00
}
newToken . val = typeInit ( newToken . kind )
en . Add ( t . val . ( string ) , newToken )
2023-05-31 23:18:16 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libSet ( line int , r * tokenReader , en * env , f string ) error {
2023-05-31 23:18:16 +00:00
t , err := r . Read ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Unexpectedly reached EOF" )
2023-05-31 23:18:16 +00:00
}
if t . kind != SYMBOL {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "A non-symbol value was given as an identifier to `set!`" )
2023-05-31 23:18:16 +00:00
}
e , err := en . Find ( t . val . ( string ) )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
current := e . vars [ t . val . ( string ) ]
if v1 . kind != current . kind {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Cannot `set!` %s to %s" , kindToString ( v1 . kind ) , kindToString ( current . kind ) )
2023-06-02 17:12:23 +00:00
}
e . vars [ t . val . ( string ) ] = v1
return nil
}
2023-06-04 22:04:58 +00:00
// ANY INT scoped-set! SYMBOL
2023-06-02 17:12:23 +00:00
// INT represents the number of scopes to jump up
2023-06-12 05:51:39 +00:00
func libSetPlus ( line int , r * tokenReader , en * env , fp string ) error {
2023-06-02 17:12:23 +00:00
t , err := r . Read ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Unexpectedly reached EOF" )
2023-06-02 17:12:23 +00:00
}
if t . kind != SYMBOL {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "A non-symbol value was given as an identifier to `scoped-set!`" )
2023-06-02 17:12:23 +00:00
}
scope , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-02 17:12:23 +00:00
}
if scope . kind != INT {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`scoped-set!` expected an INT declaring how many scopes to jump up" )
2023-06-02 17:12:23 +00:00
}
for i := scope . val . ( int ) ; i > 0 ; i -- {
if en . parent != nil {
en = en . parent
} else {
break
}
}
e , err := en . Find ( t . val . ( string ) )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-02 17:12:23 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-02 17:12:23 +00:00
}
current := e . vars [ t . val . ( string ) ]
if v1 . kind != current . kind {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Cannot assign %s to %s var" , kindToString ( v1 . kind ) , kindToString ( current . kind ) )
2023-05-31 23:18:16 +00:00
}
e . vars [ t . val . ( string ) ] = v1
return nil
}
2023-06-12 05:51:39 +00:00
func libDuplicate ( line int , fp string ) error {
2023-06-02 05:42:09 +00:00
err := globalStack . Push ( globalStack . Peek ( ) )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-02 05:42:09 +00:00
}
2023-05-31 23:18:16 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libSwap ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
v2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
2023-06-04 22:04:58 +00:00
err = globalStack . Push ( v2 )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
err = globalStack . Push ( v1 )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
2023-05-31 23:18:16 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libOver ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
v2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
2023-06-04 22:04:58 +00:00
err = globalStack . Push ( v1 )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
err = globalStack . Push ( v2 )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
err = globalStack . Push ( v1 )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
2023-05-31 23:18:16 +00:00
return nil
}
2023-06-13 23:19:18 +00:00
func libRot ( line int , fp string ) error {
v3 , err := globalStack . Pop ( )
if err != nil {
return err
}
v2 , err := globalStack . Pop ( )
if err != nil {
return err
}
v1 , err := globalStack . Pop ( )
if err != nil {
return err
}
err = globalStack . Push ( v2 )
if err != nil {
return err
}
err = globalStack . Push ( v3 )
if err != nil {
return err
}
err = globalStack . Push ( v1 )
if err != nil {
return err
}
return nil
}
2023-06-12 05:51:39 +00:00
func libDrop ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
_ , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libLogicalAnd ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
v2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
if toBoolRaw ( v1 ) && toBoolRaw ( v2 ) {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , true , line , fp } )
2023-06-04 22:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
2023-06-01 22:16:17 +00:00
} else {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , false , line , fp } )
2023-06-04 22:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
2023-06-01 22:16:17 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libLogicalOr ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
v2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
if toBoolRaw ( v1 ) || toBoolRaw ( v2 ) {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , true , line , fp } )
2023-06-04 22:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
2023-06-01 22:16:17 +00:00
} else {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , false , line , fp } )
2023-06-04 22:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
2023-06-01 22:16:17 +00:00
}
return nil
}
2023-06-08 05:59:53 +00:00
// `=` works at a value level and does not refer to
// the equality of memory adresses. As such, two lists
// with the same contents are equal even if they do
// not occupy the same memory/refer to the same object
2023-06-12 05:51:39 +00:00
func libEqual ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
v2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
2023-06-08 05:59:53 +00:00
if v1 . kind != v2 . kind {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , false , line , fp } )
2023-06-08 05:59:53 +00:00
} else {
var equal bool
switch v1 . kind {
case BOOL :
equal = v1 . val . ( bool ) == v2 . val . ( bool )
case INT , TYPE :
equal = v1 . val . ( int ) == v2 . val . ( int )
case STRING :
equal = v1 . val . ( string ) == v2 . val . ( string )
case FLOAT :
equal = v1 . val . ( float64 ) == v2 . val . ( float64 )
case LIST :
l1 := v1 . val . ( list )
l2 := v2 . val . ( list )
equal = ( l1 . String ( ) == l2 . String ( ) )
2023-06-04 22:04:58 +00:00
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , equal , line , fp } )
2023-06-01 22:16:17 +00:00
}
2023-06-05 22:45:53 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 22:45:53 +00:00
}
2023-06-01 22:16:17 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libGreaterThan ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
v2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
if v1 . kind == v2 . kind {
switch v1 . kind {
case STRING :
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , v1 . val . ( string ) > v2 . val . ( string ) , line , fp } )
2023-06-01 22:16:17 +00:00
case INT :
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , v1 . val . ( int ) > v2 . val . ( int ) , line , fp } )
2023-06-01 22:16:17 +00:00
case FLOAT :
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , v1 . val . ( float64 ) > v2 . val . ( float64 ) , line , fp } )
2023-06-01 22:16:17 +00:00
default :
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "%s is not a comparable type" , kindToString ( v1 . kind ) )
2023-06-01 22:16:17 +00:00
}
} else if ( v1 . kind == INT && v2 . kind == FLOAT ) {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , float64 ( v1 . val . ( int ) ) > v2 . val . ( float64 ) , line , fp } )
2023-06-01 22:16:17 +00:00
} else if ( v1 . kind == FLOAT && v2 . kind == INT ) {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , v1 . val . ( float64 ) > float64 ( v2 . val . ( int ) ) , line , fp } )
2023-06-01 22:16:17 +00:00
} else {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "%s and %s cannot be compared" , kindToString ( v1 . kind ) , kindToString ( v2 . kind ) )
2023-06-01 22:16:17 +00:00
}
2023-06-02 05:42:09 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-02 05:42:09 +00:00
}
2023-06-01 22:16:17 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libLessThan ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
v2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
v1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
if v1 . kind == v2 . kind {
switch v1 . kind {
case STRING :
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , v1 . val . ( string ) < v2 . val . ( string ) , line , fp } )
2023-06-01 22:16:17 +00:00
case INT :
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , v1 . val . ( int ) < v2 . val . ( int ) , line , fp } )
2023-06-01 22:16:17 +00:00
case FLOAT :
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , v1 . val . ( float64 ) < v2 . val . ( float64 ) , line , fp } )
2023-06-14 23:17:13 +00:00
case BOOL :
err = globalStack . Push ( token { BOOL , false , line , fp } )
2023-06-01 22:16:17 +00:00
default :
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "%s is not a comparable type" , kindToString ( v1 . kind ) )
2023-06-01 22:16:17 +00:00
}
} else if ( v1 . kind == INT && v2 . kind == FLOAT ) {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , float64 ( v1 . val . ( int ) ) < v2 . val . ( float64 ) , line , fp } )
2023-06-01 22:16:17 +00:00
} else if ( v1 . kind == FLOAT && v2 . kind == INT ) {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , v1 . val . ( float64 ) < float64 ( v2 . val . ( int ) ) , line , fp } )
2023-06-01 22:16:17 +00:00
} else {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "%s and %s cannot be compared" , kindToString ( v1 . kind ) , kindToString ( v2 . kind ) )
2023-06-01 22:16:17 +00:00
}
2023-06-02 05:42:09 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-02 05:42:09 +00:00
}
2023-06-01 22:16:17 +00:00
return nil
}
func libStackDump ( ) error {
fmt . Fprintf ( os . Stderr , "\n%s\n" , globalStack . String ( ) )
return nil
}
func libClearStack ( ) error {
globalStack . sp = 0
2023-05-31 23:18:16 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libLength ( line int , fp string ) error {
2023-06-04 22:04:58 +00:00
t , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
switch t . kind {
case LIST :
l := t . val . ( list )
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { INT , len ( l . body ) , line , fp } )
2023-06-04 22:04:58 +00:00
case STRING :
s := t . val . ( string )
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { INT , len ( s ) , line , fp } )
2023-06-04 22:04:58 +00:00
default :
2023-06-12 05:51:39 +00:00
err = fmt . Errorf ( "%s does not have a length" , kindToString ( t . kind ) )
2023-06-04 22:04:58 +00:00
}
return err
}
2023-06-12 05:51:39 +00:00
func libConvertType ( line int , fp string ) error {
2023-06-01 22:16:17 +00:00
typeToken , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
2023-05-31 23:18:16 +00:00
if typeToken . kind != TYPE {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`cast` expected a TYPE on top of stack" )
2023-05-31 23:18:16 +00:00
}
2023-06-01 22:16:17 +00:00
fromToken , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-01 22:16:17 +00:00
}
2023-05-31 23:18:16 +00:00
toKind , fromKind := typeToken . val . ( int ) , fromToken . kind
if fromKind == toKind {
2023-06-04 22:04:58 +00:00
err = globalStack . Push ( fromToken )
2023-05-31 23:18:16 +00:00
} else if fromKind == INT && toKind == FLOAT {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { FLOAT , float64 ( fromToken . val . ( int ) ) , line , fp } )
2023-05-31 23:18:16 +00:00
} else if fromKind == FLOAT && toKind == INT {
2023-06-12 23:14:28 +00:00
err = globalStack . Push ( token { INT , int ( math . Floor ( fromToken . val . ( float64 ) ) ) , line , fp } )
2023-05-31 23:18:16 +00:00
} else if toKind == STRING {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { STRING , toString ( fromToken , false ) , line , fp } )
2023-05-31 23:18:16 +00:00
} else if fromKind == STRING && toKind == INT {
i , err := tokenStringToInt ( fromToken . val . ( string ) )
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "%s (STRING: %q)" , err . Error ( ) , fromToken . val . ( string ) )
2023-05-31 23:18:16 +00:00
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { INT , i , line , fp } )
2023-05-31 23:18:16 +00:00
} else if fromKind == STRING && toKind == FLOAT {
f , err := strconv . ParseFloat ( fromToken . val . ( string ) , 64 )
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Could not convert STRING to FLOAT (STRING: %q)" , fromToken . val . ( string ) )
2023-05-31 23:18:16 +00:00
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { FLOAT , f , line , fp } )
2023-05-31 23:18:16 +00:00
} else if toKind == BOOL {
2023-06-04 22:04:58 +00:00
err = globalStack . Push ( toBool ( fromToken ) )
2023-05-31 23:18:16 +00:00
} else if fromKind == TYPE && toKind == INT {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { INT , fromToken . val . ( int ) , line , fp } )
2023-05-31 23:18:16 +00:00
2023-06-12 05:51:39 +00:00
} else if toKind == TYPE {
// TODO This is wrong
// converting anything to type should take on the type of the fromKind
err = globalStack . Push ( token { TYPE , fromToken . kind , line , fp } )
2023-05-31 23:18:16 +00:00
} else {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Could not convert %s to %s" , kindToString ( fromKind ) , kindToString ( toKind ) )
2023-05-31 23:18:16 +00:00
}
2023-06-04 22:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-04 22:04:58 +00:00
}
2023-05-31 23:18:16 +00:00
return nil
}
2023-06-05 15:33:11 +00:00
2023-06-12 05:51:39 +00:00
func libFileExists ( line int , fp string ) error {
2023-06-05 15:33:11 +00:00
p , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 15:33:11 +00:00
}
if p . kind != STRING {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`file-exists?` expected a STRING path on TOS but got %s" , kindToString ( p . kind ) )
2023-06-05 15:33:11 +00:00
}
path := ExpandedAbsFilepath ( p . val . ( string ) )
_ , err = os . Stat ( path )
if err == nil {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , true , line , fp } )
2023-06-05 15:33:11 +00:00
} else {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , false , line , fp } )
2023-06-05 15:33:11 +00:00
}
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 15:33:11 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libFileRemove ( line int , fp string ) error {
2023-06-05 15:33:11 +00:00
p , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 15:33:11 +00:00
}
if p . kind != STRING {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`file-remove` expected a STRING path on TOS but got %s" , kindToString ( p . kind ) )
2023-06-05 15:33:11 +00:00
}
path := ExpandedAbsFilepath ( p . val . ( string ) )
err = os . RemoveAll ( path )
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`file-remove` could not remove the file(s): %s" , path )
2023-06-05 15:33:11 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libFileAppend ( line int , fp string ) error {
2023-06-05 15:33:11 +00:00
p , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 15:33:11 +00:00
}
if p . kind != STRING {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`file-append` expected a STRING path on TOS but got %s" , kindToString ( p . kind ) )
2023-06-05 15:33:11 +00:00
}
data , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 15:33:11 +00:00
}
path := ExpandedAbsFilepath ( p . val . ( string ) )
s := toString ( data , false )
f , err := os . OpenFile ( path , os . O_APPEND | os . O_WRONLY | os . O_CREATE , 0644 )
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`file-append` could not open or create the path: %s" , path )
2023-06-05 15:33:11 +00:00
}
defer f . Close ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`file-append` opened but could not write to the path: %s" , path )
2023-06-05 15:33:11 +00:00
}
_ , err = f . WriteString ( s )
return nil
}
2023-06-12 05:51:39 +00:00
func libFileRead ( line int , fp string ) error {
2023-06-05 15:33:11 +00:00
p , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 15:33:11 +00:00
}
if p . kind != STRING {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`file-read` expected a STRING path on TOS but got %s" , kindToString ( p . kind ) )
2023-06-05 15:33:11 +00:00
}
path := ExpandedAbsFilepath ( p . val . ( string ) )
b , err := os . ReadFile ( path )
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`file-read` could not read from the file at path: %s" , path )
2023-06-05 15:33:11 +00:00
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { STRING , string ( b ) , line , fp } )
2023-06-05 15:33:11 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 15:33:11 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libDocstring ( line int , r * tokenReader , en * env , fp string ) error {
2023-06-05 22:45:53 +00:00
t , err := r . Read ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Unexpectedly reached EOF" )
2023-06-05 22:45:53 +00:00
}
2023-06-08 05:59:53 +00:00
// There is a situation here where the parser does not expect
// to see a structure (proc, while, if) here and may mangle
// parsing, since there will be no close to the declaration.
// Make the parser smart enough to handle this case.
if t . kind == KEYWORD || t . kind == PROC || t . kind == IF || t . kind == WHILE {
var s string
switch t . kind {
case IF :
s = "if"
case WHILE :
s = "while"
case PROC :
if t . val . ( proc ) . hasArg {
s = "proc!"
} else {
s = "proc"
}
default :
s = t . val . ( string )
2023-06-05 22:45:53 +00:00
}
2023-06-08 05:59:53 +00:00
v , ok := kwDocstrings [ s ]
if ok {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { STRING , fmt . Sprintf ( "`%s`\n%s" , s , v ) , line , fp } )
2023-06-08 05:59:53 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-08 05:59:53 +00:00
}
return nil
}
} else if t . kind != SYMBOL && t . kind != KEYWORD {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`docstring!` expected to read in a SYMBOL or KEYWORD but found %s" , kindToString ( t . kind ) )
2023-06-05 22:45:53 +00:00
}
targetTokenEnv , err := en . Find ( t . val . ( string ) )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 22:45:53 +00:00
}
targetToken := targetTokenEnv . vars [ t . val . ( string ) ]
if targetToken . kind != PROC {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`docstring!` cannot read a docstring from %s" , kindToString ( targetToken . kind ) )
2023-06-05 22:45:53 +00:00
}
p := targetToken . val . ( proc )
2023-06-12 16:27:42 +00:00
s := fmt . Sprintf ( "`%s`\n%s" , t . val . ( string ) , p . doc )
err = globalStack . Push ( token { STRING , s , line , fp } )
2023-06-05 22:45:53 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-05 22:45:53 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libInput ( line int , fp string ) error {
2023-06-06 06:04:58 +00:00
scanner := bufio . NewScanner ( os . Stdin )
scanner . Scan ( )
text := scanner . Text ( )
2023-06-12 05:51:39 +00:00
err := globalStack . Push ( token { STRING , text , line , fp } )
2023-06-06 06:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-06 06:04:58 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libReMatch ( line int , fp string ) error {
2023-06-06 06:04:58 +00:00
t2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-06 06:04:58 +00:00
}
t1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-06 06:04:58 +00:00
}
if ! ( t1 . kind == STRING && t2 . kind == STRING ) {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`re-match?` expected two strings from the stack but found %s and %s" , kindToString ( t1 . kind ) , kindToString ( t2 . kind ) )
2023-06-06 06:04:58 +00:00
}
re := regexp . MustCompile ( t1 . val . ( string ) )
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { BOOL , re . MatchString ( t2 . val . ( string ) ) , line , fp } )
2023-06-06 06:04:58 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-06 06:04:58 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libReFind ( line int , fp string ) error {
2023-06-07 06:17:21 +00:00
t2 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-07 06:17:21 +00:00
}
t1 , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-07 06:17:21 +00:00
}
if ! ( t1 . kind == STRING && t2 . kind == STRING ) {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`re-find` expected two strings from the stack but found %s and %s" , kindToString ( t1 . kind ) , kindToString ( t2 . kind ) )
2023-06-07 06:17:21 +00:00
}
re := regexp . MustCompile ( t1 . val . ( string ) )
matches := re . FindStringSubmatch ( t2 . val . ( string ) )
tokens := make ( [ ] token , len ( matches ) )
for i , s := range matches {
2023-06-12 05:51:39 +00:00
tokens [ i ] = token { STRING , s , line , fp }
2023-06-07 06:17:21 +00:00
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { LIST , list { tokens } , line , fp } )
2023-06-07 06:17:21 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-07 06:17:21 +00:00
}
2023-06-06 06:04:58 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libReReplace ( line int , fp string ) error {
2023-06-13 23:19:18 +00:00
replacement , err := globalStack . Pop ( )
if err != nil {
return err
}
input , err := globalStack . Pop ( )
if err != nil {
return err
}
pattern , err := globalStack . Pop ( )
if err != nil {
return err
}
re := regexp . MustCompile ( toString ( pattern , false ) )
s := re . ReplaceAllString ( toString ( input , false ) , toString ( replacement , false ) )
err = globalStack . Push ( token { STRING , s , line , fp } )
if err != nil {
return err
}
2023-06-06 06:04:58 +00:00
return nil
}
2023-06-12 05:51:39 +00:00
func libSlice ( line int , fp string ) error {
2023-06-07 06:17:21 +00:00
to , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-07 06:17:21 +00:00
}
from , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-07 06:17:21 +00:00
}
if to . kind != INT || from . kind != INT {
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "`slice` expected two integers from the stack but found %s and %s" , kindToString ( from . kind ) , kindToString ( to . kind ) )
2023-06-07 06:17:21 +00:00
}
data , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-07 06:17:21 +00:00
}
t := to . val . ( int )
f := from . val . ( int ) - 1
switch data . kind {
case STRING :
s := data . val . ( string )
if t > len ( s ) {
t = len ( s )
}
if f < 0 {
f = 0
}
if f > t {
f = t
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { STRING , s [ f : t ] , line , fp } )
2023-06-07 06:17:21 +00:00
case LIST :
li := data . val . ( list )
l := li . body
if t > len ( l ) {
t = len ( l )
}
if f < 0 {
f = 0
}
if f > t {
f = t
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { LIST , list { l [ f : t ] } , line , fp } )
2023-06-07 06:17:21 +00:00
default :
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Cannot `slice` %s\n" , line , kindToString ( data . kind ) )
2023-06-07 06:17:21 +00:00
}
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-07 06:17:21 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libStackDepth ( line int , fp string ) error {
err := globalStack . Push ( token { INT , globalStack . Depth ( ) , line , fp } )
2023-06-07 06:17:21 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-07 06:17:21 +00:00
}
return nil
}
2023-06-08 05:59:53 +00:00
2023-06-12 05:51:39 +00:00
func libType ( line int , fp string ) error {
2023-06-08 05:59:53 +00:00
val , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-08 05:59:53 +00:00
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { TYPE , val . kind , line , fp } )
2023-06-08 05:59:53 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-08 05:59:53 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libNetGet ( line int , fp string ) error {
2023-06-08 05:59:53 +00:00
val , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-08 05:59:53 +00:00
}
respList := list { [ ] token {
2023-06-12 05:51:39 +00:00
token { BOOL , false , line , fp } ,
token { INT , - 1 , line , fp } ,
token { STRING , "The given string could not be parsed as a url" , line , fp } ,
2023-06-08 05:59:53 +00:00
} }
u , err := url . Parse ( toString ( val , false ) )
if err != nil {
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { LIST , respList , line , fp } )
2023-06-08 05:59:53 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-08 05:59:53 +00:00
}
return nil
}
switch u . Scheme {
case "http" , "https" :
resp , err := http . Get ( u . String ( ) )
if err != nil {
2023-06-12 05:51:39 +00:00
respList . body [ 2 ] = token { STRING , err . Error ( ) , line , fp }
2023-06-08 05:59:53 +00:00
break
}
defer resp . Body . Close ( )
success := true
status := resp . StatusCode
if status < 200 || status > 299 {
success = false
}
body , err := io . ReadAll ( resp . Body )
if err != nil {
2023-06-12 05:51:39 +00:00
respList . body [ 1 ] = token { INT , status , line , fp }
respList . body [ 2 ] = token { STRING , "Could not read response body, or reading was interuppted" , line , fp }
2023-06-08 05:59:53 +00:00
break
}
2023-06-12 05:51:39 +00:00
respList . body [ 0 ] = token { BOOL , success , line , fp }
respList . body [ 1 ] = token { INT , status , line , fp }
respList . body [ 2 ] = token { STRING , string ( body ) , line , fp }
2023-06-08 05:59:53 +00:00
case "gemini" :
status , resp := GeminiRequest ( u , 0 )
if status == 1 || status == 2 {
2023-06-12 05:51:39 +00:00
respList . body [ 0 ] = token { BOOL , true , line , fp }
2023-06-08 05:59:53 +00:00
} else {
2023-06-12 05:51:39 +00:00
respList . body [ 0 ] = token { BOOL , false , line , fp }
2023-06-08 05:59:53 +00:00
}
2023-06-12 05:51:39 +00:00
respList . body [ 1 ] = token { INT , status , line , fp }
respList . body [ 2 ] = token { STRING , resp , line , fp }
2023-06-08 05:59:53 +00:00
case "gopher" :
if u . Port ( ) == "" {
u . Host = u . Host + ":70"
}
conn , err := net . Dial ( "tcp" , u . Host )
if err != nil {
2023-06-12 05:51:39 +00:00
respList . body [ 2 ] = token { STRING , err . Error ( ) , line , fp }
2023-06-08 05:59:53 +00:00
break
}
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 {
2023-06-12 05:51:39 +00:00
respList . body [ 2 ] = token { STRING , err . Error ( ) , line , fp }
2023-06-08 05:59:53 +00:00
break
}
resp , err := io . ReadAll ( conn )
if err != nil {
2023-06-12 05:51:39 +00:00
respList . body [ 2 ] = token { STRING , err . Error ( ) , line , fp }
2023-06-08 05:59:53 +00:00
break
}
2023-06-12 05:51:39 +00:00
respList . body [ 0 ] = token { BOOL , true , line , fp }
respList . body [ 2 ] = token { STRING , string ( resp ) , line , fp }
2023-06-08 05:59:53 +00:00
default :
2023-06-12 05:51:39 +00:00
return fmt . Errorf ( "Unsupported URL scheme: %s\n" , line , u . Scheme )
2023-06-08 05:59:53 +00:00
}
2023-06-12 05:51:39 +00:00
err = globalStack . Push ( token { LIST , respList , line , fp } )
2023-06-08 05:59:53 +00:00
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-08 05:59:53 +00:00
}
return nil
}
2023-06-12 05:51:39 +00:00
func libThrow ( line int , fp string ) error {
2023-06-09 17:05:10 +00:00
e , err := globalStack . Pop ( )
if err != nil {
2023-06-12 05:51:39 +00:00
return err
2023-06-09 17:05:10 +00:00
}
return fmt . Errorf ( "%s" , toString ( e , false ) )
}
2023-06-13 20:22:22 +00:00
func libImport ( line int , fp string , en * env ) error {
t , err := globalStack . Pop ( )
if err != nil {
return err
}
if t . kind != STRING {
return fmt . Errorf ( "`import` expects a STRING on TOS, not %s" , kindToString ( t . kind ) )
}
if en . Imported ( t . val . ( string ) ) {
return nil
}
switch t . val . ( string ) {
case "std" :
err = lexParseInterpret ( stdImport , en , "std" )
if err != nil {
return err
}
en . imports [ t . val . ( string ) ] = true
case "math" :
err = lexParseInterpret ( mathImport , en , "math" )
if err != nil {
return err
}
en . imports [ t . val . ( string ) ] = true
case "stack" :
err = lexParseInterpret ( stackImport , en , "stack" )
if err != nil {
return err
}
en . imports [ t . val . ( string ) ] = true
default :
p := ExpandedAbsFilepath ( t . val . ( string ) )
s , err := readFile ( p )
if err != nil {
return err
}
err = lexParseInterpret ( s , en , p )
if err != nil {
return err
}
en . imports [ t . val . ( string ) ] = true
}
return nil
}
2023-06-14 03:32:23 +00:00
func libEach ( line int , r * tokenReader , en * env , fp string ) error {
li , err := globalStack . Pop ( )
if err != nil {
return err
}
if li . kind != LIST {
return fmt . Errorf ( "'each!' expected a LIST on TOS but found %s" , kindToString ( li . kind ) )
}
l := li . val . ( list ) . body
t , err := r . Read ( )
if err != nil {
return fmt . Errorf ( "Unexpectedly reached EOF" )
}
if t . kind == SYMBOL {
e , err := en . Find ( t . val . ( string ) )
if err != nil {
return err
}
val := e . vars [ t . val . ( string ) ]
if val . kind != PROC {
return fmt . Errorf ( "Cannot apply %s to list item in `each!`, expected PROC or KEYWORD" , kindToString ( val . kind ) )
}
} else if t . kind != KEYWORD {
return fmt . Errorf ( "A non-SYMBOL/non-KEYWORD value was given as an identifier to `each!`" )
}
for i := range l {
err = interpret ( [ ] token { l [ i ] , t } , en )
if err != nil {
return err
}
}
return nil
}
2023-06-14 21:03:05 +00:00
func libFilter ( line int , r * tokenReader , en * env , fp string ) error {
li , err := globalStack . Pop ( )
if err != nil {
return err
}
if li . kind != LIST {
return fmt . Errorf ( "'filter!' expected a LIST on TOS but found %s" , kindToString ( li . kind ) )
}
l := li . val . ( list ) . body
t , err := r . Read ( )
if err != nil {
return fmt . Errorf ( "Unexpectedly reached EOF" )
}
if t . kind == SYMBOL {
e , err := en . Find ( t . val . ( string ) )
if err != nil {
return err
}
val := e . vars [ t . val . ( string ) ]
if val . kind != PROC {
return fmt . Errorf ( "Cannot apply %s to list item in `filter!`, expected PROC or KEYWORD" , kindToString ( val . kind ) )
}
} else if t . kind != KEYWORD {
return fmt . Errorf ( "A non-SYMBOL/non-KEYWORD value was given as an identifier to `filter!`" )
}
newList := make ( [ ] token , 0 , len ( l ) )
for i := range l {
err = interpret ( [ ] token { l [ i ] , t } , en )
if err != nil {
return err
}
b , err := globalStack . Pop ( )
if err != nil {
return err
}
if toBoolRaw ( b ) {
newList = append ( newList , l [ i ] )
}
}
err = globalStack . Push ( token { LIST , list { newList } , line , fp } )
if err != nil {
return err
}
return nil
}