Moves special forms into their own file and fixes apply special form to work with nested lists properly
This commit is contained in:
parent
bc375214af
commit
0f677d5b1a
356
interpreter.go
356
interpreter.go
|
@ -2,14 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.rawtext.club/sloum/slope/termios"
|
||||
ln "github.com/peterh/liner"
|
||||
)
|
||||
|
||||
type proc struct {
|
||||
|
@ -87,354 +80,21 @@ func eval(exp expression, en *env) (value expression) {
|
|||
case "begin0":
|
||||
value = specialBegin0(e, en)
|
||||
case "usage":
|
||||
var procSigRE = regexp.MustCompile(`(?s)(\()([^() ]+\b)([^)]*)(\))(?:(\s*=>)([^\n]*))?(.*)`)
|
||||
var replacer = "\033[40;33;1m$1\033[95m$2\033[92m$3\033[33m$4\033[94m$5\033[36m$6\033[0m$7"
|
||||
|
||||
if len(e) < 2 {
|
||||
// XXX - Branch for viewing list of all std procs
|
||||
var out strings.Builder
|
||||
header := "(usage [[procedure: symbol]])\n\n\033[1;4mKnown Symbols\033[0m\n\n"
|
||||
out.WriteString(procSigRE.ReplaceAllString(header, replacer))
|
||||
keys := make([]string, 0, len(usageStrings))
|
||||
for key, _ := range usageStrings {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
var width int = 60
|
||||
if globalenv.vars[symbol("slope-interactive?")] != false {
|
||||
width, _ = termios.GetWindowSize()
|
||||
}
|
||||
printedWidth := 0
|
||||
sort.Strings(keys)
|
||||
for i := range keys {
|
||||
if printedWidth+26 >= width {
|
||||
out.WriteRune('\n')
|
||||
printedWidth = 0
|
||||
}
|
||||
out.WriteString(fmt.Sprintf("%-26s", keys[i]))
|
||||
printedWidth += 26
|
||||
}
|
||||
|
||||
if len(namespaces) > 0 {
|
||||
out.WriteString("\n\n\033[1;4mKnown Modules\033[0m\n\n")
|
||||
taken := make(map[string]bool)
|
||||
for k, v := range altnamespaces {
|
||||
out.WriteString(fmt.Sprintf("%-12s -> %s\n", v, k))
|
||||
taken[v] = true
|
||||
}
|
||||
for k := range namespaces {
|
||||
if _, ok := taken[string(k)]; !ok {
|
||||
out.WriteString(string(k))
|
||||
out.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SysoutPrint(out.String(), Sysout)
|
||||
value = make([]expression, 0)
|
||||
break
|
||||
} else if len(e) == 2 {
|
||||
proc, ok := e[1].(string)
|
||||
if !ok {
|
||||
p, ok2 := e[1].(symbol)
|
||||
if !ok2 {
|
||||
value = exception("'usage' expected a string or symbol as its first argument, a non-string non-symbol value was given")
|
||||
break
|
||||
}
|
||||
proc = string(p)
|
||||
}
|
||||
if strings.HasSuffix(proc, "::") {
|
||||
// XXX - Print list of module procedures
|
||||
ns := proc[:len(proc)-2]
|
||||
module := ns
|
||||
altName, ok := altnamespaces[ns]
|
||||
if ok {
|
||||
module = altName
|
||||
}
|
||||
useMap, err := GetUsageMap(module)
|
||||
if err != nil {
|
||||
value = exception("'usage' encountered an error: " + err.Error())
|
||||
break
|
||||
}
|
||||
SysoutPrint(fmt.Sprintf("\033[1;4m%s's Known Symbols\033[0m\n\n", ns), Sysout)
|
||||
for k := range useMap {
|
||||
SysoutPrint(fmt.Sprintf("%v\n", k), Sysout)
|
||||
}
|
||||
} else if strings.Contains(proc, "::") {
|
||||
// XXX - Show info for a module symbol
|
||||
pair := strings.SplitN(proc, "::", 2)
|
||||
if len(pair) != 2 {
|
||||
value = exception("'usage' was given an invalid module/symbol format")
|
||||
break
|
||||
}
|
||||
ns := pair[0]
|
||||
module := ns
|
||||
altName, ok := altnamespaces[ns]
|
||||
if ok {
|
||||
module = altName
|
||||
}
|
||||
useMap, err := GetUsageMap(module)
|
||||
if err != nil {
|
||||
value = exception("'usage' encountered an error: " + err.Error())
|
||||
break
|
||||
}
|
||||
subFunc, ok := useMap[pair[1]]
|
||||
if !ok {
|
||||
value = exception("'usage' could not find the requested symbol within the " + ns + "module's usage data")
|
||||
break
|
||||
}
|
||||
SysoutPrint(fmt.Sprintf("%v\n", procSigRE.ReplaceAllString(subFunc, replacer)), Sysout)
|
||||
|
||||
} else {
|
||||
// XXX - Show info for a builtin
|
||||
v, ok := usageStrings[proc]
|
||||
if !ok {
|
||||
SysoutPrint(fmt.Sprintf("%q does not have a usage definition\n", proc), Sysout)
|
||||
} else {
|
||||
SysoutPrint(fmt.Sprintf("%v\n\n", procSigRE.ReplaceAllString(v, replacer)), Sysout)
|
||||
}
|
||||
}
|
||||
}
|
||||
value = make([]expression, 0)
|
||||
value = specialUsage(e)
|
||||
case "load":
|
||||
if en.outer != nil {
|
||||
value = exception("'load' is only callable from the global/top-level")
|
||||
break
|
||||
}
|
||||
files := make([]expression, 0, len(e)-1)
|
||||
for _, fp := range e[1:] {
|
||||
var p string
|
||||
if _, ok := fp.([]expression); ok {
|
||||
p = String(eval(fp, en), false)
|
||||
} else {
|
||||
p = String(fp, false)
|
||||
}
|
||||
files = append(files, p)
|
||||
}
|
||||
loadFiles(files)
|
||||
value = symbol("ok")
|
||||
value = specialLoad(e, en)
|
||||
case "load-mod":
|
||||
if len(e) < 2 {
|
||||
value = exception("'load-mod' expected a module name, no value was given")
|
||||
break
|
||||
}
|
||||
fullLoadEnv := env{make(map[symbol]expression), &globalenv}
|
||||
modName := e[1]
|
||||
|
||||
var p string
|
||||
if _, ok := modName.([]expression); ok {
|
||||
p = String(eval(modName, en), false)
|
||||
} else {
|
||||
p = String(modName, false)
|
||||
}
|
||||
modEnv, err := RunModule(p, false)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("'load-mod' failed loading module %s: %s", p, err.Error()))
|
||||
}
|
||||
for k, v := range modEnv.vars {
|
||||
fullLoadEnv.vars[k] = v
|
||||
}
|
||||
|
||||
altName := ""
|
||||
if len(e) > 2 {
|
||||
if _, ok := modName.([]expression); ok {
|
||||
altName = String(eval(e[2], en), false)
|
||||
} else {
|
||||
altName = String(e[2], false)
|
||||
}
|
||||
}
|
||||
|
||||
namespaces[p] = fullLoadEnv
|
||||
if altName != "" {
|
||||
altnamespaces[altName] = p
|
||||
}
|
||||
value = symbol("ok")
|
||||
value = specialLoadMod(e, en)
|
||||
case "load-mod-file":
|
||||
fullLoadEnv := env{make(map[symbol]expression), &globalenv}
|
||||
for _, fp := range e[1:] {
|
||||
var p string
|
||||
if _, ok := fp.([]expression); ok {
|
||||
p = String(eval(fp, en), false)
|
||||
} else {
|
||||
p = String(fp, false)
|
||||
}
|
||||
modEnv, err := RunModule(p, true)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("'load-mod-file' failed loading module %s: %s", p, err.Error()))
|
||||
}
|
||||
for k, v := range modEnv.vars {
|
||||
fullLoadEnv.vars[k] = v
|
||||
}
|
||||
}
|
||||
for k, v := range fullLoadEnv.vars {
|
||||
globalenv.vars[k] = v
|
||||
}
|
||||
value = symbol("ok")
|
||||
value = specialLoadModFile(e, en)
|
||||
case "exists?":
|
||||
if len(e) == 0 {
|
||||
value = exception("'exists?' expects at least one symbol or string, no values were given")
|
||||
}
|
||||
value = true
|
||||
var err error
|
||||
for i := range e[1:] {
|
||||
doPanic := panicOnException
|
||||
if doPanic {
|
||||
panicOnException = false
|
||||
}
|
||||
// TODO make this work with interpreter forms
|
||||
_, ok := e[i].([]expression)
|
||||
if ok {
|
||||
_, err = en.Find(symbol(String(eval(e[i+1], en), false)))
|
||||
} else {
|
||||
environ := en
|
||||
symbolAsString := String(e[i+1], false)
|
||||
if i := strings.Index(symbolAsString, "::"); i >= 0 {
|
||||
modname := symbolAsString[:i]
|
||||
alias, ok := altnamespaces[modname]
|
||||
if !ok {
|
||||
e, ok2 := namespaces[modname]
|
||||
if !ok2 {
|
||||
return false
|
||||
}
|
||||
environ = &e
|
||||
} else {
|
||||
e := namespaces[alias]
|
||||
environ = &e
|
||||
}
|
||||
symbolAsString = symbolAsString[i+2:]
|
||||
}
|
||||
_, err = environ.Find(symbol(symbolAsString))
|
||||
}
|
||||
if doPanic {
|
||||
panicOnException = true
|
||||
}
|
||||
if err != nil {
|
||||
value = false
|
||||
break
|
||||
}
|
||||
}
|
||||
value = specialExists(e, en)
|
||||
case "inspect":
|
||||
// Make sure we are attached to a tty
|
||||
if fileInfo, _ := os.Stdout.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 {
|
||||
value = false
|
||||
break
|
||||
}
|
||||
if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 {
|
||||
value = false
|
||||
break
|
||||
}
|
||||
initialState, _ := ln.TerminalMode()
|
||||
liner := ln.NewLiner()
|
||||
linerState, _ := ln.TerminalMode()
|
||||
defer liner.Close()
|
||||
histFile := ExpandedAbsFilepath(filepath.Join(getModBaseDir(), "..", historyFilename))
|
||||
if f, e := os.Open(histFile); e == nil {
|
||||
liner.ReadHistory(f)
|
||||
f.Close()
|
||||
}
|
||||
// Set up completion
|
||||
liner.SetTabCompletionStyle(ln.TabCircular)
|
||||
liner.SetCompleter(func(l string) (c []string) {
|
||||
if len(l) == 0 {
|
||||
return
|
||||
}
|
||||
lastIndex := strings.LastIndexAny(l, "( \n")
|
||||
c = append(c, completeFromMap(usageStrings, l, lastIndex)...)
|
||||
c = append(c, completeFromMap(getAllModFuncNames(), l, lastIndex)...)
|
||||
sort.Strings(c)
|
||||
return
|
||||
})
|
||||
inspectID := "inspect"
|
||||
if len(e) > 1 {
|
||||
inspectID = String(eval(e[1], en), false)
|
||||
}
|
||||
var text strings.Builder
|
||||
var cont bool
|
||||
var raw bool
|
||||
var match bool
|
||||
for {
|
||||
globalenv.vars[symbol("slope-interactive?")] = true
|
||||
if linerState != nil {
|
||||
linerState.ApplyMode()
|
||||
}
|
||||
in := prompt(liner, cont, inspectID)
|
||||
if initialState != nil {
|
||||
initialState.ApplyMode()
|
||||
}
|
||||
if in == "uninspect" || in == "(uninspect)" {
|
||||
break
|
||||
}
|
||||
if len(strings.TrimSpace(in)) == 0 && !raw {
|
||||
continue
|
||||
}
|
||||
text.WriteString(in)
|
||||
text.WriteRune('\n')
|
||||
|
||||
var brokenString bool
|
||||
match, raw, brokenString = stringParensMatch(text.String())
|
||||
if !match && !brokenString {
|
||||
cont = true
|
||||
} else {
|
||||
cont = false
|
||||
outputResult(text.String(), en)
|
||||
text.Reset()
|
||||
}
|
||||
}
|
||||
value = true
|
||||
value = specialInspect(e, en)
|
||||
case "apply":
|
||||
if len(e) < 3 {
|
||||
value = exception("'apply' expects two arguments: a procedure and an argument list, too few arguments were given")
|
||||
break
|
||||
}
|
||||
args := eval(e[2], en)
|
||||
// TODO make this work so that we dont have to eval things here
|
||||
switch item := args.(type) {
|
||||
case []expression:
|
||||
value = eval(append([]expression{e[1]}, item...), en)
|
||||
default:
|
||||
value = eval([]expression{e[1], item}, en)
|
||||
}
|
||||
value = specialApply(e, en)
|
||||
case "eval":
|
||||
if len(e) < 1 {
|
||||
value = exception("'eval' expects a string and an optional boolean to indicate that a string should be parsed and evaluated, but was not given any arguments")
|
||||
}
|
||||
sParse := false
|
||||
if len(e) >= 3 {
|
||||
v, ok := e[2].(bool)
|
||||
if ok {
|
||||
sParse = v
|
||||
}
|
||||
}
|
||||
switch item := eval(e[1], en).(type) {
|
||||
case []expression:
|
||||
if _, ok := item[0].(symbol); !ok {
|
||||
value = item
|
||||
} else {
|
||||
value = eval(item, en)
|
||||
v, ok := value.(string)
|
||||
if ok && sParse {
|
||||
p := Parse(v)
|
||||
value = eval(p.([]expression)[0], en)
|
||||
}
|
||||
}
|
||||
case string:
|
||||
if sParse {
|
||||
p := Parse(item)
|
||||
if l, ok := p.([]expression)[0].([]expression); ok {
|
||||
if _, ok := l[0].(symbol); ok {
|
||||
value = eval(p.([]expression)[0], en)
|
||||
} else {
|
||||
value = l
|
||||
}
|
||||
} else {
|
||||
value = eval(p.([]expression)[0], en)
|
||||
}
|
||||
} else {
|
||||
value = item
|
||||
}
|
||||
default:
|
||||
value = item
|
||||
}
|
||||
value = specialEval(e, en)
|
||||
default:
|
||||
operands := e[1:]
|
||||
values := make([]expression, len(operands))
|
||||
|
|
2
main.go
2
main.go
|
@ -19,7 +19,7 @@ import (
|
|||
"golang.design/x/clipboard"
|
||||
)
|
||||
|
||||
const version = "1.2.13"
|
||||
const version = "1.2.14"
|
||||
|
||||
const globalLibPath = "/usr/local/lib/slope/modules/"
|
||||
|
||||
|
|
365
specialForms.go
365
specialForms.go
|
@ -1,8 +1,16 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.rawtext.club/sloum/slope/termios"
|
||||
ln "github.com/peterh/liner"
|
||||
)
|
||||
|
||||
func specialAnd(e []expression, en *env) expression {
|
||||
|
@ -28,6 +36,20 @@ func specialAnd(e []expression, en *env) expression {
|
|||
return value
|
||||
}
|
||||
|
||||
func specialApply(e []expression, en *env) expression {
|
||||
if len(e) < 3 {
|
||||
return exception("'apply' expects two arguments: a procedure and an argument list, too few arguments were given")
|
||||
}
|
||||
args := eval(e[2], en)
|
||||
// TODO make this work so that we dont have to eval things here
|
||||
switch item := args.(type) {
|
||||
case []expression:
|
||||
return apply(eval(e[1], en), item, true)
|
||||
default:
|
||||
return apply(eval(e[1], en), []expression{item}, true)
|
||||
}
|
||||
}
|
||||
|
||||
func specialBegin0(e []expression, en *env) expression {
|
||||
var v expression
|
||||
for ii, i := range e[1:] {
|
||||
|
@ -131,6 +153,97 @@ func specialDefine(e []expression, en *env) expression {
|
|||
return val
|
||||
}
|
||||
|
||||
func specialEval(e []expression, en *env) expression {
|
||||
if len(e) < 1 {
|
||||
return exception("'eval' expects a string and an optional boolean to indicate that a string should be parsed and evaluated, but was not given any arguments")
|
||||
}
|
||||
sParse := false
|
||||
if len(e) >= 3 {
|
||||
v, ok := e[2].(bool)
|
||||
if ok {
|
||||
sParse = v
|
||||
}
|
||||
}
|
||||
switch item := eval(e[1], en).(type) {
|
||||
case []expression:
|
||||
if _, ok := item[0].(symbol); !ok {
|
||||
return item
|
||||
} else {
|
||||
value := eval(item, en)
|
||||
v, ok := value.(string)
|
||||
if ok && sParse {
|
||||
p := Parse(v)
|
||||
return eval(p.([]expression)[0], en)
|
||||
}
|
||||
return value
|
||||
}
|
||||
case string:
|
||||
if sParse {
|
||||
p := Parse(item)
|
||||
if l, ok := p.([]expression)[0].([]expression); ok {
|
||||
if _, ok := l[0].(symbol); ok {
|
||||
return eval(p.([]expression)[0], en)
|
||||
} else {
|
||||
return l
|
||||
}
|
||||
} else {
|
||||
return eval(p.([]expression)[0], en)
|
||||
}
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
default:
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
func specialExists(e []expression, en *env) expression {
|
||||
if len(e) == 0 {
|
||||
return exception("'exists?' expects at least one symbol or string, no values were given")
|
||||
}
|
||||
var err error
|
||||
for i := range e[1:] {
|
||||
doPanic := panicOnException
|
||||
if doPanic {
|
||||
panicOnException = false
|
||||
}
|
||||
// TODO make this work with interpreter forms
|
||||
_, ok := e[i].([]expression)
|
||||
if ok {
|
||||
_, err = en.Find(symbol(String(eval(e[i+1], en), false)))
|
||||
} else {
|
||||
environ := en
|
||||
symbolAsString := String(e[i+1], false)
|
||||
if i := strings.Index(symbolAsString, "::"); i >= 0 {
|
||||
modname := symbolAsString[:i]
|
||||
alias, ok := altnamespaces[modname]
|
||||
if !ok {
|
||||
e, ok2 := namespaces[modname]
|
||||
if !ok2 {
|
||||
if doPanic {
|
||||
panicOnException = true
|
||||
}
|
||||
return false
|
||||
}
|
||||
environ = &e
|
||||
} else {
|
||||
e := namespaces[alias]
|
||||
environ = &e
|
||||
}
|
||||
symbolAsString = symbolAsString[i+2:]
|
||||
}
|
||||
_, err = environ.Find(symbol(symbolAsString))
|
||||
}
|
||||
if doPanic {
|
||||
panicOnException = true
|
||||
}
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func specialFor(e []expression, en *env) expression {
|
||||
if len(e) < 3 {
|
||||
return exception("'for' requires at least two arguments: a list of initializers and a test")
|
||||
|
@ -199,6 +312,75 @@ func specialIf(e []expression, en *env) expression {
|
|||
return make([]expression, 0)
|
||||
}
|
||||
|
||||
func specialInspect(e []expression, en *env) expression {
|
||||
// Make sure we are attached to a tty
|
||||
if fileInfo, _ := os.Stdout.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 {
|
||||
return false
|
||||
}
|
||||
if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
initialState, _ := ln.TerminalMode()
|
||||
liner := ln.NewLiner()
|
||||
linerState, _ := ln.TerminalMode()
|
||||
defer liner.Close()
|
||||
histFile := ExpandedAbsFilepath(filepath.Join(getModBaseDir(), "..", historyFilename))
|
||||
if f, e := os.Open(histFile); e == nil {
|
||||
liner.ReadHistory(f)
|
||||
f.Close()
|
||||
}
|
||||
// Set up completion
|
||||
liner.SetTabCompletionStyle(ln.TabCircular)
|
||||
liner.SetCompleter(func(l string) (c []string) {
|
||||
if len(l) == 0 {
|
||||
return
|
||||
}
|
||||
lastIndex := strings.LastIndexAny(l, "( \n")
|
||||
c = append(c, completeFromMap(usageStrings, l, lastIndex)...)
|
||||
c = append(c, completeFromMap(getAllModFuncNames(), l, lastIndex)...)
|
||||
sort.Strings(c)
|
||||
return
|
||||
})
|
||||
inspectID := "inspect"
|
||||
if len(e) > 1 {
|
||||
inspectID = String(eval(e[1], en), false)
|
||||
}
|
||||
var text strings.Builder
|
||||
var cont bool
|
||||
var raw bool
|
||||
var match bool
|
||||
for {
|
||||
globalenv.vars[symbol("slope-interactive?")] = true
|
||||
if linerState != nil {
|
||||
linerState.ApplyMode()
|
||||
}
|
||||
in := prompt(liner, cont, inspectID)
|
||||
if initialState != nil {
|
||||
initialState.ApplyMode()
|
||||
}
|
||||
if in == "uninspect" || in == "(uninspect)" {
|
||||
break
|
||||
}
|
||||
if len(strings.TrimSpace(in)) == 0 && !raw {
|
||||
continue
|
||||
}
|
||||
text.WriteString(in)
|
||||
text.WriteRune('\n')
|
||||
|
||||
var brokenString bool
|
||||
match, raw, brokenString = stringParensMatch(text.String())
|
||||
if !match && !brokenString {
|
||||
cont = true
|
||||
} else {
|
||||
cont = false
|
||||
outputResult(text.String(), en)
|
||||
text.Reset()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func specialLambda(e []expression, en *env) expression {
|
||||
if len(e) < 3 {
|
||||
return exception("'lambda' expects at least three arguments")
|
||||
|
@ -232,6 +414,84 @@ func specialLambda(e []expression, en *env) expression {
|
|||
return proc{e[1], stringUnescapeEval(b), en, predicates}
|
||||
}
|
||||
|
||||
func specialLoad(e []expression, en *env) expression {
|
||||
if en.outer != nil {
|
||||
return exception("'load' is only callable from the global/top-level")
|
||||
}
|
||||
files := make([]expression, 0, len(e)-1)
|
||||
for _, fp := range e[1:] {
|
||||
var p string
|
||||
if _, ok := fp.([]expression); ok {
|
||||
p = String(eval(fp, en), false)
|
||||
} else {
|
||||
p = String(fp, false)
|
||||
}
|
||||
files = append(files, p)
|
||||
}
|
||||
loadFiles(files)
|
||||
return true
|
||||
}
|
||||
|
||||
func specialLoadMod(e []expression, en *env) expression {
|
||||
if len(e) < 2 {
|
||||
return exception("'load-mod' expected a module name, no value was given")
|
||||
}
|
||||
fullLoadEnv := env{make(map[symbol]expression), &globalenv}
|
||||
modName := e[1]
|
||||
|
||||
var p string
|
||||
if _, ok := modName.([]expression); ok {
|
||||
p = String(eval(modName, en), false)
|
||||
} else {
|
||||
p = String(modName, false)
|
||||
}
|
||||
modEnv, err := RunModule(p, false)
|
||||
if err != nil {
|
||||
return exception(fmt.Sprintf("'load-mod' failed loading module %s: %s", p, err.Error()))
|
||||
}
|
||||
for k, v := range modEnv.vars {
|
||||
fullLoadEnv.vars[k] = v
|
||||
}
|
||||
|
||||
altName := ""
|
||||
if len(e) > 2 {
|
||||
if _, ok := modName.([]expression); ok {
|
||||
altName = String(eval(e[2], en), false)
|
||||
} else {
|
||||
altName = String(e[2], false)
|
||||
}
|
||||
}
|
||||
|
||||
namespaces[p] = fullLoadEnv
|
||||
if altName != "" {
|
||||
altnamespaces[altName] = p
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func specialLoadModFile(e []expression, en *env) expression {
|
||||
fullLoadEnv := env{make(map[symbol]expression), &globalenv}
|
||||
for _, fp := range e[1:] {
|
||||
var p string
|
||||
if _, ok := fp.([]expression); ok {
|
||||
p = String(eval(fp, en), false)
|
||||
} else {
|
||||
p = String(fp, false)
|
||||
}
|
||||
modEnv, err := RunModule(p, true)
|
||||
if err != nil {
|
||||
return exception(fmt.Sprintf("'load-mod-file' failed loading module %s: %s", p, err.Error()))
|
||||
}
|
||||
for k, v := range modEnv.vars {
|
||||
fullLoadEnv.vars[k] = v
|
||||
}
|
||||
}
|
||||
for k, v := range fullLoadEnv.vars {
|
||||
globalenv.vars[k] = v
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func specialMacro(e []expression, en *env) expression {
|
||||
if len(e) < 3 {
|
||||
return exception("'macro' expects at least three arguments")
|
||||
|
@ -295,3 +555,108 @@ func specialSet(e []expression, en *env) expression {
|
|||
return val
|
||||
}
|
||||
|
||||
func specialUsage(e []expression) expression {
|
||||
var procSigRE = regexp.MustCompile(`(?s)(\()([^() ]+\b)([^)]*)(\))(?:(\s*=>)([^\n]*))?(.*)`)
|
||||
var replacer = "\033[40;33;1m$1\033[95m$2\033[92m$3\033[33m$4\033[94m$5\033[36m$6\033[0m$7"
|
||||
|
||||
if len(e) < 2 {
|
||||
// XXX - Branch for viewing list of all std procs
|
||||
var out strings.Builder
|
||||
header := "(usage [[procedure: symbol]])\n\n\033[1;4mKnown Symbols\033[0m\n\n"
|
||||
out.WriteString(procSigRE.ReplaceAllString(header, replacer))
|
||||
keys := make([]string, 0, len(usageStrings))
|
||||
for key, _ := range usageStrings {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
var width int = 60
|
||||
if globalenv.vars[symbol("slope-interactive?")] != false {
|
||||
width, _ = termios.GetWindowSize()
|
||||
}
|
||||
printedWidth := 0
|
||||
sort.Strings(keys)
|
||||
for i := range keys {
|
||||
if printedWidth+26 >= width {
|
||||
out.WriteRune('\n')
|
||||
printedWidth = 0
|
||||
}
|
||||
out.WriteString(fmt.Sprintf("%-26s", keys[i]))
|
||||
printedWidth += 26
|
||||
}
|
||||
|
||||
if len(namespaces) > 0 {
|
||||
out.WriteString("\n\n\033[1;4mKnown Modules\033[0m\n\n")
|
||||
taken := make(map[string]bool)
|
||||
for k, v := range altnamespaces {
|
||||
out.WriteString(fmt.Sprintf("%-12s -> %s\n", v, k))
|
||||
taken[v] = true
|
||||
}
|
||||
for k := range namespaces {
|
||||
if _, ok := taken[string(k)]; !ok {
|
||||
out.WriteString(string(k))
|
||||
out.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SysoutPrint(out.String(), Sysout)
|
||||
return make([]expression, 0)
|
||||
} else if len(e) == 2 {
|
||||
proc, ok := e[1].(string)
|
||||
if !ok {
|
||||
p, ok2 := e[1].(symbol)
|
||||
if !ok2 {
|
||||
return exception("'usage' expected a string or symbol as its first argument, a non-string non-symbol value was given")
|
||||
}
|
||||
proc = string(p)
|
||||
}
|
||||
if strings.HasSuffix(proc, "::") {
|
||||
// XXX - Print list of module procedures
|
||||
ns := proc[:len(proc)-2]
|
||||
module := ns
|
||||
altName, ok := altnamespaces[ns]
|
||||
if ok {
|
||||
module = altName
|
||||
}
|
||||
useMap, err := GetUsageMap(module)
|
||||
if err != nil {
|
||||
return exception("'usage' encountered an error: " + err.Error())
|
||||
}
|
||||
SysoutPrint(fmt.Sprintf("\033[1;4m%s's Known Symbols\033[0m\n\n", ns), Sysout)
|
||||
for k := range useMap {
|
||||
SysoutPrint(fmt.Sprintf("%v\n", k), Sysout)
|
||||
}
|
||||
} else if strings.Contains(proc, "::") {
|
||||
// XXX - Show info for a module symbol
|
||||
pair := strings.SplitN(proc, "::", 2)
|
||||
if len(pair) != 2 {
|
||||
return exception("'usage' was given an invalid module/symbol format")
|
||||
}
|
||||
ns := pair[0]
|
||||
module := ns
|
||||
altName, ok := altnamespaces[ns]
|
||||
if ok {
|
||||
module = altName
|
||||
}
|
||||
useMap, err := GetUsageMap(module)
|
||||
if err != nil {
|
||||
return exception("'usage' encountered an error: " + err.Error())
|
||||
}
|
||||
subFunc, ok := useMap[pair[1]]
|
||||
if !ok {
|
||||
return exception("'usage' could not find the requested symbol within the " + ns + "module's usage data")
|
||||
}
|
||||
SysoutPrint(fmt.Sprintf("%v\n", procSigRE.ReplaceAllString(subFunc, replacer)), Sysout)
|
||||
|
||||
} else {
|
||||
// XXX - Show info for a builtin
|
||||
v, ok := usageStrings[proc]
|
||||
if !ok {
|
||||
SysoutPrint(fmt.Sprintf("%q does not have a usage definition\n", proc), Sysout)
|
||||
} else {
|
||||
SysoutPrint(fmt.Sprintf("%v\n\n", procSigRE.ReplaceAllString(v, replacer)), Sysout)
|
||||
}
|
||||
}
|
||||
}
|
||||
return make([]expression, 0)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue