From 43362475f9707a1405a47fc421836c76c22edba9 Mon Sep 17 00:00:00 2001 From: Chuck Smith Date: Wed, 20 Mar 2024 17:01:45 -0400 Subject: [PATCH] seperated builtins --- examples/bf.monkey | 116 +++++++++++++++++++++++ object/builtin_assert.go | 29 ++++++ object/builtin_exit.go | 17 ++++ object/builtin_first.go | 20 ++++ object/builtin_input.go | 30 ++++++ object/builtin_last.go | 21 ++++ object/builtin_len.go | 21 ++++ object/builtin_pop.go | 25 +++++ object/builtin_print.go | 12 +++ object/builtin_push.go | 26 +++++ object/builtin_rest.go | 23 +++++ object/builtins.go | 200 --------------------------------------- vim/monkey.vim | 9 +- 13 files changed, 345 insertions(+), 204 deletions(-) create mode 100644 examples/bf.monkey create mode 100644 object/builtin_assert.go create mode 100644 object/builtin_exit.go create mode 100644 object/builtin_first.go create mode 100644 object/builtin_input.go create mode 100644 object/builtin_last.go create mode 100644 object/builtin_len.go create mode 100644 object/builtin_pop.go create mode 100644 object/builtin_print.go create mode 100644 object/builtin_push.go create mode 100644 object/builtin_rest.go diff --git a/examples/bf.monkey b/examples/bf.monkey new file mode 100644 index 0000000..231e01d --- /dev/null +++ b/examples/bf.monkey @@ -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) \ No newline at end of file diff --git a/object/builtin_assert.go b/object/builtin_assert.go new file mode 100644 index 0000000..0b88af7 --- /dev/null +++ b/object/builtin_assert.go @@ -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 +} diff --git a/object/builtin_exit.go b/object/builtin_exit.go new file mode 100644 index 0000000..cdf7112 --- /dev/null +++ b/object/builtin_exit.go @@ -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 +} diff --git a/object/builtin_first.go b/object/builtin_first.go new file mode 100644 index 0000000..3685033 --- /dev/null +++ b/object/builtin_first.go @@ -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 +} diff --git a/object/builtin_input.go b/object/builtin_input.go new file mode 100644 index 0000000..4be0858 --- /dev/null +++ b/object/builtin_input.go @@ -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)} +} diff --git a/object/builtin_last.go b/object/builtin_last.go new file mode 100644 index 0000000..74c32eb --- /dev/null +++ b/object/builtin_last.go @@ -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 +} diff --git a/object/builtin_len.go b/object/builtin_len.go new file mode 100644 index 0000000..1813290 --- /dev/null +++ b/object/builtin_len.go @@ -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()) + } +} diff --git a/object/builtin_pop.go b/object/builtin_pop.go new file mode 100644 index 0000000..ca9674c --- /dev/null +++ b/object/builtin_pop.go @@ -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 +} diff --git a/object/builtin_print.go b/object/builtin_print.go new file mode 100644 index 0000000..c1250a5 --- /dev/null +++ b/object/builtin_print.go @@ -0,0 +1,12 @@ +package object + +import "fmt" + +// Print ... +func Print(args ...Object) Object { + for _, arg := range args { + fmt.Println(arg.String()) + } + + return nil +} diff --git a/object/builtin_push.go b/object/builtin_push.go new file mode 100644 index 0000000..fd932e5 --- /dev/null +++ b/object/builtin_push.go @@ -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} +} diff --git a/object/builtin_rest.go b/object/builtin_rest.go new file mode 100644 index 0000000..29a5383 --- /dev/null +++ b/object/builtin_rest.go @@ -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 +} diff --git a/object/builtins.go b/object/builtins.go index 52f9841..8bac377 100644 --- a/object/builtins.go +++ b/object/builtins.go @@ -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 -} diff --git a/vim/monkey.vim b/vim/monkey.vim index 6684ba6..2a1c877 100644 --- a/vim/monkey.vim +++ b/vim/monkey.vim @@ -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