Adds import support
This commit is contained in:
parent
ddd215ee55
commit
93a4ebb4b8
13
env.go
13
env.go
|
@ -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}
|
||||
}
|
||||
|
|
|
@ -46,4 +46,5 @@ var (
|
|||
// This is the main stack for the language
|
||||
globalStack stack = NewStack()
|
||||
globalRetStack stack = NewStack()
|
||||
imports = make(map[string]bool)
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
57
keywords.go
57
keywords.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
.
|
||||
|
|
@ -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 *
|
||||
.
|
||||
|
|
@ -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
|
||||
.
|
||||
|
97
lib/std.fe
97
lib/std.fe
|
@ -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
33
main.go
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue