Adds import support

This commit is contained in:
sloum 2023-06-13 13:22:22 -07:00
parent ddd215ee55
commit 93a4ebb4b8
9 changed files with 188 additions and 112 deletions

13
env.go
View File

@ -18,6 +18,7 @@ import (
type env struct {
vars map[string]token
imports map[string]bool
parent *env
}
@ -32,6 +33,16 @@ func (e *env) Find(s string) (*env, error) {
}
}
func (e *env) Imported(s string) bool {
if _, ok := e.imports[s]; ok {
return true
} else if e.parent == nil {
return false
} else {
return e.parent.Imported(s)
}
}
func (e *env) UpLevel(count int) *env {
var en *env = e
for ; count > 0; count-- {
@ -47,5 +58,5 @@ func (e *env) Add(name string, t token) {
}
func NewEnv(parent *env) *env {
return &env{make(map[string]token), parent}
return &env{make(map[string]token), make(map[string]bool), parent}
}

View File

@ -46,4 +46,5 @@ var (
// This is the main stack for the language
globalStack stack = NewStack()
globalRetStack stack = NewStack()
imports = make(map[string]bool)
)

View File

@ -116,7 +116,7 @@ func isKeyword(s string) bool {
"list-set!", "split", "join", "file-write", "file-remove",
"file-exists?", "file-read", "docstring!", "input", "re-match?",
"re-find", "re-replace", "slice", "stackdepth", "net-get", "try",
"catch", "throw":
"catch", "throw", "import":
return true
default:
return false

View File

@ -113,6 +113,8 @@ func callKeyword(kw token, r *tokenReader, en *env) error {
return libNetGet(kw.line, kw.file)
case "throw":
return libThrow(kw.line, kw.file)
case "import":
return libImport(kw.line, kw.file, en)
default:
return fmt.Errorf("Unknown keyword: `%s`", kw.line, kw.val.(string))
}
@ -1271,3 +1273,58 @@ func libThrow(line int, fp string) error {
return fmt.Errorf("%s", toString(e, false))
}
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))
}
// Use 'imports' var (found in globals.go) to mark once imported
// XXX scratch that. Add a map to the env struct and mark them there
// so that they can get imported wherever in scope, but never doubly
// so? A way to search parent scopes for the value will be important
// as well
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
case "io":
err = lexParseInterpret(ioImport, en, "io")
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
}

28
lib/io.fe Normal file
View File

@ -0,0 +1,28 @@
STRING var! stdout
"/dev/fd/1" set! stdout
STRING var! stderr
"/dev/fd/2" set! stderr
STRING var! stdin
"/dev/fd/0" set! stdin
STRING var! devnull
"/dev/null" set! devnull
proc print
| Stack: ANY
Read :
Push :
Notes: Prints the value on TOS |
STRING cast stdout file-write
.
proc println
| Stack: ANY
Read :
Push :
Notes: Prints the value on TOS + a newline |
STRING cast "\n" append stdout file-write
.

52
lib/math.fe Normal file
View File

@ -0,0 +1,52 @@
"std" import
"stack" import
proc abs
| Stack: FLOAT/INT
Read :
Push : FLOAT/INT
Notes: Takes TOS and leaves it's absolute value on TOS |
dup 0 <
if
-1 *
.
.
proc %
| Stack: INT
Read :
Push : INT
Notes: Calculates the remainder (n1 n2 -- n3) |
dup2
INT var! div
FLOAT cast / INT cast set! div
div * -
.
proc ipow
| Stack: INT INT
Read :
Push : INT
Notes: Raise n1 by n2 (n1 n2 -- n3) |
INT var! exp INT cast set! exp
INT var! base INT cast set! base
exp 0 < if
"`pow` with negative exponent not allowed" throw .
INT var! result 1 set! result
proc loop? exp 0 > .
loop? while
result base * set! result
--! exp
loop?
.
result
.
proc square
| Stack: INT
Read :
Push : INT
Notes: Squares the given INT and leaves the result on TOS |
dup *
.

17
lib/stack.fe Normal file
View File

@ -0,0 +1,17 @@
proc dup2
| Stack: ANY1 ANY2
Read :
Push : ANY1 ANY2
Notes: Duplicates the top two stack values such that the stack starts and ends as: v1 v2 -- v1 v2 v1 v2 |
over over
.
proc nip
| Stack: ANY1 ANY2
Read :
Push :
Notes: Drops the item under TOS (a1 a2 -- a2) |
swap drop
.

View File

@ -1,17 +1,3 @@
STRING var! stdout
"/dev/fd/1" set! stdout
STRING var! stderr
"/dev/fd/2" set! stderr
STRING var! stdin
"/dev/fd/0" set! stdin
STRING var! devnull
"/dev/null" set! devnull
proc! ++!
| Stack:
Read : SYMBOL(INT)
@ -68,56 +54,6 @@ Notes: Decrements an INT on TOS and places the result back on TOS |
.
.
proc print
| Stack: ANY
Read :
Push :
Notes: Prints the value on TOS |
STRING cast stdout file-write
.
proc println
| Stack: ANY
Read :
Push :
Notes: Prints the value on TOS + a newline |
STRING cast "\n" append stdout file-write
.
proc abs
| Stack: FLOAT/INT
Read :
Push : FLOAT/INT
Notes: Takes TOS and leaves it's absolute value on TOS |
dup TYPE cast var! v
set! v
v 0 <
if
v -1 *
else
v
.
.
proc dup2
| Stack: ANY1 ANY2
Read :
Push : ANY1 ANY2
Notes: Duplicates the top two stack values such that the stack starts and ends as: v1 v2 -- v1 v2 v1 v2 |
over over
.
proc %
| Stack: INT
Read :
Push : INT
Notes: Calculates the remainder (n1 n2 -- n3) |
dup2
INT var! div
FLOAT cast / INT cast set! div
div * -
.
proc not
| Stack: ANY
Read :
@ -126,37 +62,4 @@ Notes: Treats TOS as a BOOL (regardless of actual type) and inverts the BOOL |
if false else true .
.
proc ipow
| Stack: INT INT
Read :
Push : INT
Notes: Raise n1 by n2 (n1 n2 -- n3) |
INT var! exp INT cast set! exp
INT var! base INT cast set! base
exp 0 < if
"`pow` with negative exponent not allowed" throw .
INT var! result 1 set! result
proc loop? exp 0 > .
loop? while
result base * set! result
--! exp
loop?
.
result
.
proc square
| Stack: INT
Read :
Push : INT
Notes: Squares the given INT and leaves the result on TOS |
dup *
.
proc nip
| Stack: ANY1 ANY2
Read :
Push :
Notes: Drops the item under TOS (a1 a2 -- a2) |
swap drop
.

33
main.go
View File

@ -45,8 +45,16 @@ var completions = []string{
}
//go:embed lib/std.fe
var std string
var stdImport string
//go:embed lib/math.fe
var mathImport string
//go:embed lib/io.fe
var ioImport string
//go:embed lib/stack.fe
var stackImport string
/*
@ -94,11 +102,6 @@ func VersionString() string {
return fmt.Sprintf("%.2f\n", version)
}
func seedBaseEnv(en *env) {
lexParseInterpret(std, en, "std.fe")
}
func repl() {
fmt.Printf("%s\nfelise version %s(c) 2023 sloum, all rights reserved\nLicensed under the terms of the Floodgap Free Software License\n(`bye` or `exit` to quit)\n\n", logo,VersionString())
initialTerm, _ = ln.TerminalMode()
@ -125,7 +128,6 @@ func repl() {
var text strings.Builder
var cont bool
baseEnv := NewEnv(nil)
seedBaseEnv(baseEnv)
for {
if linerTerm != nil {
@ -149,22 +151,28 @@ func repl() {
text.WriteString(in)
}
if !cont {
lexParseInterpret(text.String(), baseEnv, "")
processInterpretError(lexParseInterpret(text.String(), baseEnv, ""), "")
text.Reset()
}
}
}
func lexParseInterpret(s string, en *env, file string) {
func lexParseInterpret(s string, en *env, file string) error {
lexedTokens, err := lex(s, file)
if err == nil {
r := NewTokenReader(lexedTokens)
var parsedTokens []token
parsedTokens, err := parse(r)
parsedTokens, err = parse(r)
if err == nil {
err = interpret(parsedTokens, en)
}
}
return err
}
// TODO just separated lex and process err
// Make sure all is working
func processInterpretError(err error, file string) {
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR | %s\n", err.Error())
if file == "" {
@ -173,7 +181,7 @@ func lexParseInterpret(s string, en *env, file string) {
os.Exit(1)
}
} else if file == "" {
fmt.Println("\n\033[0mOk.")
fmt.Println("\n\033[0mOk.")
}
}
@ -194,7 +202,6 @@ func main() {
os.Exit(1)
}
en := NewEnv(nil)
seedBaseEnv(en)
if len(flagArgs) > 1 {
t := make([]token, len(flagArgs)-1)
for i, s := range flagArgs[1:] {
@ -205,7 +212,7 @@ func main() {
t := make([]token, 0)
en.Add("sys-args", token{LIST, list{t}, -1, "built-in"})
}
lexParseInterpret(s, en, flagArgs[0])
processInterpretError(lexParseInterpret(s, en, flagArgs[0]), flagArgs[0])
} else {
repl()
}