Adds eval and fixes some issues with exceptions in the interpreter

master
sloum 1 year ago
parent 416ea454ce
commit 1191e14da6

@ -77,6 +77,8 @@ There are a number of special forms in the language that will allow, for example
- Used within modules to load additional files
- `usage`
- Used to display the procedure signature and any additional information about the procedure, mostly used interactively
- `eval`
- Executes representations of slope code
While not a special form from a compiler implementation standpoint `list` has some syntactic sugar that is worthy of note. These four lines will all produce the same list:
@ -111,6 +113,7 @@ Quote and list both have some syntactic sugar to create a shorthand for their us
<li><code>(load-mod [filepath: string...])</code>: <code>symbol</code></li>
<li><code>(load-mod-file [filepath: string...])</code>: <code>symbol</code></li>
<li><code>(usage [[symbol|string]])</code>: <code>()</code> Will display usage information for the given procedure or runtime value. If no argument is given <code>usage</code> will list known procedures</li>
<li><code>(eval [expression|value] [[evaluate-string-as-code: bool]])</code>: <code>value</code></li>
</ul>
</details>

@ -192,12 +192,12 @@ func eval(exp expression, en *env) (value expression) {
value = make([]expression, 0)
case "load":
if en.outer != nil {
value = expression("'load' is only callable from the global/top-level")
value = exception("'load' is only callable from the global/top-level")
break
}
for _, fp := range e[1:] {
if _, ok := fp.(string); !ok {
value = expression("'load' expects filepaths as a string, a non-string value was encountered")
value = exception("'load' expects filepaths as a string, a non-string value was encountered")
break
}
}
@ -243,13 +243,13 @@ func eval(exp expression, en *env) (value expression) {
value = symbol("ok")
case "filter":
if len(e) < 3 {
value = expression("'filter' expects two arguments: a procedure and an argument list, too few arguments were given")
value = exception("'filter' expects two arguments: a procedure and an argument list, too few arguments were given")
break
}
proc := eval(e[1], en)
list, ok := eval(e[2], en).([]expression)
if !ok {
value = expression("'filter' expects a list as its second argument, a non-list value was given")
value = exception("'filter' expects a list as its second argument, a non-list value was given")
break
}
val := make([]expression, 0)
@ -263,7 +263,7 @@ func eval(exp expression, en *env) (value expression) {
value = val
case "apply":
if len(e) < 3 {
value = expression("'apply' expects two arguments: a procedure and an argument list, too few arguments were given")
value = exception("'apply' expects two arguments: a procedure and an argument list, too few arguments were given")
break
}
args := eval(e[2], en)
@ -273,6 +273,47 @@ func eval(exp expression, en *env) (value expression) {
default:
value = apply(eval(e[1], en), []expression{item})
}
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(l[0], en)
}
} else {
value = item
}
default:
value = item
}
default:
operands := e[1:]
values := make([]expression, len(operands))
@ -300,7 +341,7 @@ func apply(procedure expression, args []expression) (value expression) {
switch params := p.params.(type) {
case []expression:
if len(params)-variadic(params) > len(args) {
return expression(fmt.Sprintf("Lambda expected %d arguments but received %d", len(params), len(args)))
return exception(fmt.Sprintf("Lambda expected %d arguments but received %d", len(params), len(args)))
}
for i, param := range params {
if param.(symbol) == symbol("args-list") {
@ -318,7 +359,7 @@ func apply(procedure expression, args []expression) (value expression) {
}
value = eval(p.body, en)
default:
panic("Unknown procedure type encountered during APPLY")
panic("Unknown procedure type encountered during APPLY: " + String(procedure, true))
}
return
}

@ -1585,7 +1585,7 @@ var stdLibrary = vars{
}
f, err := os.OpenFile(fp, flags, 0664)
if err != nil {
return exception("'file-write' could not open the given filepath")
return false
}
obj := &IOHandle{f, true, "file (write-only)"}
openFiles = append(openFiles, obj)
@ -1601,7 +1601,7 @@ var stdLibrary = vars{
if fp, ok := a[0].(string); ok {
f, err := os.OpenFile(fp, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return exception("'file-append-to' could not open the given filepath")
return false
}
defer f.Close()
for i := range a[1:] {

@ -52,6 +52,7 @@ var usageStrings = map[string]string{
"E": "E => number",
"env": "(env [[env-key: string]] [[env-value: string]]) => list|string|()",
"equal?": "(equal? [value] [value]) => bool",
"eval": "(eval [expression|value] [[treat-string-as-code: bool]]) => value\n\ntreat-string-as-code defaults to #f. As such, a string will be evaluated to itself, a string. If true is passed, then the string will be parsed as code and the first, hopefully only, top level expression in the string will be evaluated. Aside from evaluating string it is common to use eval to evaluate quoted code: `(eval '(1 2 3))` would result in the list (1 2 3)",
"exception-mode-panic": "(exception-mode-panic) => ()",
"exception-mode-panic?": "(exception-mode-panic?) => bool",
"exception-mode-pass": "(exception-mode-pass) => ()",

@ -83,7 +83,7 @@ func SetCookedMode() {
var t = getTermios()
t.Iflag |= syscall.BRKINT | syscall.IGNPAR | syscall.ISTRIP | syscall.ICRNL | syscall.IXON
t.Oflag |= syscall.OPOST
t.Lflag |= syscall.ISIG | syscall.ICANON
t.Lflag |= syscall.ISIG | syscall.ICANON | syscall.ECHO
setTermios(t)
}

Loading…
Cancel
Save