seperated builtins
Some checks failed
Build / build (push) Successful in 10m0s
Test / build (push) Failing after 16m20s

This commit is contained in:
Chuck Smith
2024-03-20 17:01:45 -04:00
parent f735215c28
commit 43362475f9
13 changed files with 345 additions and 204 deletions

116
examples/bf.monkey Normal file
View File

@@ -0,0 +1,116 @@
#!./monkey-lang
let fill = fn(x, i) {
let xs = []
while (i > 0) {
xs = push(xs, x)
i = i - 1
}
return xs
}
let buildJumpMap = fn(program) {
let stack = []
let map = {}
let n = 0
while (n < len(program)) {
if (program[n] == "[") {
stack = push(stack, n)
}
if (program[n] == "]") {
let start = pop(stack)
map[start] = n
map[n] = start
}
n = n + 1
}
return map
}
let ascii_table = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
let ord = fn(s) {
return ascii_table[s]
}
let chr = fn(x) {
if (x < 0) {
return "??"
}
if (x > 127) {
return "??"
}
return ascii_table[x]
}
let read = fn() {
let buf = input()
if (len(buf) > 0) {
return ord(buf[0])
}
return 0
}
let write = fn(x) {
print(chr(x))
}
let VM = fn(program) {
let jumps = buildJumpMap(program)
let ip = 0
let dp = 0
let memory = fill(0, 32)
while (ip < len(program)) {
let op = program[ip]
if (op == ">") {
dp = dp + 1
} else if (op == "<") {
dp = dp - 1
} else if (op == "+") {
if (memory[dp] < 255) {
memory[dp] = memory[dp] + 1
} else {
memory[dp] = 0
}
} else if (op == "-") {
if (memory[dp] > 0) {
memory[dp] = memory[dp] - 1
} else {
memory[dp] = 255
}
} else if (op == ".") {
write(memory[dp])
} else if (op == ",") {
memory[dp] = read()
} else if (op == "[") {
if (memory[dp] == 0) {
ip = jumps[ip]
}
} else if (op == "]") {
if (memory[dp] != 0) {
ip = jumps[ip]
}
}
ip = ip + 1
}
print("memory:")
print(memory)
print("ip:")
print(ip)
print("dp:")
print(dp)
}
// Hello World
let program = "++++++++ [ >++++ [ >++ >+++ >+++ >+ <<<<- ] >+ >+ >- >>+ [<] <- ] >>. >---. +++++++..+++. >>. <-. <. +++.------.--------. >>+. >++."
// 2 + 5
// let program = "++> +++++ [<+>-] ++++++++ [<++++++>-] < ."
VM(program)

29
object/builtin_assert.go Normal file
View File

@@ -0,0 +1,29 @@
package object
import (
"fmt"
"os"
)
// Assert ...
func Assert(args ...Object) Object {
if len(args) != 2 {
return newError("wrong number of arguments. got=%d, want=2",
len(args))
}
if args[0].Type() != BOOLEAN_OBJ {
return newError("argument #1 to `assert` must be BOOLEAN, got %s",
args[0].Type())
}
if args[1].Type() != STRING_OBJ {
return newError("argument #2 to `assert` must be STRING, got %s",
args[0].Type())
}
if !args[0].(*Boolean).Value {
fmt.Printf("Assertion Error: %s", args[1].(*String).Value)
os.Exit(1)
}
return nil
}

17
object/builtin_exit.go Normal file
View File

@@ -0,0 +1,17 @@
package object
import "os"
// Exit ...
func Exit(args ...Object) Object {
if len(args) == 1 {
if args[0].Type() != INTEGER_OBJ {
return newError("argument to `exit` must be INTEGER, got %s",
args[0].Type())
}
os.Exit(int(args[0].(*Integer).Value))
} else {
os.Exit(0)
}
return nil
}

20
object/builtin_first.go Normal file
View File

@@ -0,0 +1,20 @@
package object
// First ...
func First(args ...Object) Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1",
len(args))
}
if args[0].Type() != ARRAY_OBJ {
return newError("argument to `first` must be ARRAY, got %s",
args[0].Type())
}
arr := args[0].(*Array)
if len(arr.Elements) > 0 {
return arr.Elements[0]
}
return nil
}

30
object/builtin_input.go Normal file
View File

@@ -0,0 +1,30 @@
package object
import (
"bufio"
"fmt"
"io"
"os"
)
// Input ...
func Input(args ...Object) Object {
if len(args) > 0 {
obj, ok := args[0].(*String)
if !ok {
return newError(
"argument to `input` not supported, got %s",
args[0].Type(),
)
}
fmt.Fprintf(os.Stdout, obj.Value)
}
buffer := bufio.NewReader(os.Stdin)
line, _, err := buffer.ReadLine()
if err != nil && err != io.EOF {
return newError(fmt.Sprintf("error reading input from stdin: %s", err))
}
return &String{Value: string(line)}
}

21
object/builtin_last.go Normal file
View File

@@ -0,0 +1,21 @@
package object
// Last ...
func Last(args ...Object) Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1",
len(args))
}
if args[0].Type() != ARRAY_OBJ {
return newError("argument to `last` must be ARRAY, got %s",
args[0].Type())
}
arr := args[0].(*Array)
length := len(arr.Elements)
if length > 0 {
return arr.Elements[length-1]
}
return nil
}

21
object/builtin_len.go Normal file
View File

@@ -0,0 +1,21 @@
package object
import "unicode/utf8"
// Len ...
func Len(args ...Object) Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1",
len(args))
}
switch arg := args[0].(type) {
case *Array:
return &Integer{Value: int64(len(arg.Elements))}
case *String:
return &Integer{Value: int64(utf8.RuneCountInString(arg.Value))}
default:
return newError("argument to `len` not supported, got %s",
args[0].Type())
}
}

25
object/builtin_pop.go Normal file
View File

@@ -0,0 +1,25 @@
package object
// Pop ...
func Pop(args ...Object) Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1",
len(args))
}
if args[0].Type() != ARRAY_OBJ {
return newError("argument to `pop` must be ARRAY, got %s",
args[0].Type())
}
arr := args[0].(*Array)
length := len(arr.Elements)
if length == 0 {
return newError("cannot pop from an empty array")
}
element := arr.Elements[length-1]
arr.Elements = arr.Elements[:length-1]
return element
}

12
object/builtin_print.go Normal file
View File

@@ -0,0 +1,12 @@
package object
import "fmt"
// Print ...
func Print(args ...Object) Object {
for _, arg := range args {
fmt.Println(arg.String())
}
return nil
}

26
object/builtin_push.go Normal file
View File

@@ -0,0 +1,26 @@
package object
// Push ...
func Push(args ...Object) Object {
if len(args) != 2 {
return newError("wrong number of arguments. got=%d, want=2",
len(args))
}
if args[0].Type() != ARRAY_OBJ {
return newError("argument to `push` must be ARRAY, got %s",
args[0].Type())
}
arr := args[0].(*Array)
length := len(arr.Elements)
newElements := make([]Object, length+1)
copy(newElements, arr.Elements)
if immutable, ok := args[1].(Immutable); ok {
newElements[length] = immutable.Clone()
} else {
newElements[length] = args[1]
}
return &Array{Elements: newElements}
}

23
object/builtin_rest.go Normal file
View File

@@ -0,0 +1,23 @@
package object
// Rest ...
func Rest(args ...Object) Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1",
len(args))
}
if args[0].Type() != ARRAY_OBJ {
return newError("argument to `rest` must be ARRAY, got %s",
args[0].Type())
}
arr := args[0].(*Array)
length := len(arr.Elements)
if length > 0 {
newElements := make([]Object, length-1, length-1)
copy(newElements, arr.Elements[1:length])
return &Array{Elements: newElements}
}
return nil
}

View File

@@ -1,12 +1,8 @@
package object
import (
"bufio"
"fmt"
"io"
"os"
"sort"
"unicode/utf8"
)
// Builtins ...
@@ -41,199 +37,3 @@ func init() {
func newError(format string, a ...interface{}) *Error {
return &Error{Message: fmt.Sprintf(format, a...)}
}
// Len ...
func Len(args ...Object) Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1",
len(args))
}
switch arg := args[0].(type) {
case *Array:
return &Integer{Value: int64(len(arg.Elements))}
case *String:
return &Integer{Value: int64(utf8.RuneCountInString(arg.Value))}
default:
return newError("argument to `len` not supported, got %s",
args[0].Type())
}
}
// Input ...
func Input(args ...Object) Object {
if len(args) > 0 {
obj, ok := args[0].(*String)
if !ok {
return newError(
"argument to `input` not supported, got %s",
args[0].Type(),
)
}
fmt.Fprintf(os.Stdout, obj.Value)
}
buffer := bufio.NewReader(os.Stdin)
line, _, err := buffer.ReadLine()
if err != nil && err != io.EOF {
return newError(fmt.Sprintf("error reading input from stdin: %s", err))
}
return &String{Value: string(line)}
}
// Print ...
func Print(args ...Object) Object {
for _, arg := range args {
fmt.Println(arg.String())
}
return nil
}
// First ...
func First(args ...Object) Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1",
len(args))
}
if args[0].Type() != ARRAY_OBJ {
return newError("argument to `first` must be ARRAY, got %s",
args[0].Type())
}
arr := args[0].(*Array)
if len(arr.Elements) > 0 {
return arr.Elements[0]
}
return nil
}
// Last ...
func Last(args ...Object) Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1",
len(args))
}
if args[0].Type() != ARRAY_OBJ {
return newError("argument to `last` must be ARRAY, got %s",
args[0].Type())
}
arr := args[0].(*Array)
length := len(arr.Elements)
if length > 0 {
return arr.Elements[length-1]
}
return nil
}
// Rest ...
func Rest(args ...Object) Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1",
len(args))
}
if args[0].Type() != ARRAY_OBJ {
return newError("argument to `rest` must be ARRAY, got %s",
args[0].Type())
}
arr := args[0].(*Array)
length := len(arr.Elements)
if length > 0 {
newElements := make([]Object, length-1, length-1)
copy(newElements, arr.Elements[1:length])
return &Array{Elements: newElements}
}
return nil
}
// Push ...
func Push(args ...Object) Object {
if len(args) != 2 {
return newError("wrong number of arguments. got=%d, want=2",
len(args))
}
if args[0].Type() != ARRAY_OBJ {
return newError("argument to `push` must be ARRAY, got %s",
args[0].Type())
}
arr := args[0].(*Array)
length := len(arr.Elements)
newElements := make([]Object, length+1)
copy(newElements, arr.Elements)
if immutable, ok := args[1].(Immutable); ok {
newElements[length] = immutable.Clone()
} else {
newElements[length] = args[1]
}
return &Array{Elements: newElements}
}
// Pop ...
func Pop(args ...Object) Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1",
len(args))
}
if args[0].Type() != ARRAY_OBJ {
return newError("argument to `pop` must be ARRAY, got %s",
args[0].Type())
}
arr := args[0].(*Array)
length := len(arr.Elements)
if length == 0 {
return newError("cannot pop from an empty array")
}
element := arr.Elements[length-1]
arr.Elements = arr.Elements[:length-1]
return element
}
// Exit ...
func Exit(args ...Object) Object {
if len(args) == 1 {
if args[0].Type() != INTEGER_OBJ {
return newError("argument to `exit` must be INTEGER, got %s",
args[0].Type())
}
os.Exit(int(args[0].(*Integer).Value))
} else {
os.Exit(0)
}
return nil
}
// Assert ...
func Assert(args ...Object) Object {
if len(args) != 2 {
return newError("wrong number of arguments. got=%d, want=2",
len(args))
}
if args[0].Type() != BOOLEAN_OBJ {
return newError("argument #1 to `assert` must be BOOLEAN, got %s",
args[0].Type())
}
if args[1].Type() != STRING_OBJ {
return newError("argument #2 to `assert` must be STRING, got %s",
args[0].Type())
}
if !args[0].(*Boolean).Value {
fmt.Printf("Assertion Error: %s", args[1].(*String).Value)
os.Exit(1)
}
return nil
}

View File

@@ -11,24 +11,25 @@ endif
syntax case match
syntax keyword xType true false
syntax keyword xType true false null
syntax keyword xKeyword let fn if else return while
syntax keyword xFunction len input print first last rest push pop exit
syntax keyword xFunction len input print first last rest push pop exit assert
syntax keyword xOperator == != < > !
syntax keyword xOperator + - * / =
syntax region xString start=/"/ skip=/\\./ end=/"/
" syntax region xComment start='#' end='$' keepend
syntax region xComment start='#' end='$' keepend
syntax region xComment start='//' end='$' keepend
highlight link xType Type
highlight link xKeyword Keyword
highlight link xFunction Function
highlight link xString String
" highlight link xComment Comment
highlight link xComment Comment
highlight link xOperator Operator
highlight Operator ctermfg=5