From 2d3c7c42cee86acafa6389b5bb9c15085ffe99a7 Mon Sep 17 00:00:00 2001 From: William Bell Date: Sat, 8 Apr 2023 15:34:15 +0200 Subject: [PATCH] make maps oop --- .gitignore | 3 +- app.py | 46 ++++++++++ modules/csv/init.ar | 9 +- src/array.go | 30 ++++++- src/arvalid.go | 16 ++-- src/boolean.go | 2 +- src/built-ins.go | 154 ++++++++++++++++++++++----------- src/call.go | 28 +++++- src/file.go | 2 + src/getIndex.go | 96 ++++++++------------ src/input.go | 3 +- src/main.go | 5 +- src/map.go | 151 +++++++++++++++++++++++++++++--- src/operations.go | 12 ++- src/sequence-and-series.go | 35 ++++++++ src/sortany.go | 10 +-- src/string.go | 35 +++++--- src/term-class.go | 13 +-- src/time.go | 109 ++++++++++++++++++----- src/to-argon.go | 6 +- src/translate.go | 17 ++-- src/trycatch.go | 6 +- src/typeof.go | 8 +- src/variable.go | 130 +++++++++++++++++----------- test | 4 + tests/brainfuck.ar | 34 ++++++++ tests/{test.ar => csv_test.ar} | 0 tests/example.ar | 3 +- tests/memoryLeakTest.ar | 5 +- tests/stack test.ar | 7 ++ tests/welcomemessage.ar | 4 +- 31 files changed, 708 insertions(+), 275 deletions(-) create mode 100644 app.py create mode 100644 src/sequence-and-series.go create mode 100755 test create mode 100644 tests/brainfuck.ar rename tests/{test.ar => csv_test.ar} (100%) create mode 100644 tests/stack test.ar diff --git a/.gitignore b/.gitignore index c5e82d7..57f219c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -bin \ No newline at end of file +bin +array.json \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..65ede69 --- /dev/null +++ b/app.py @@ -0,0 +1,46 @@ +def interpret(code): + memory = {} + pointer = 0 + code_ptr = 0 + loops = [] + + while code_ptr < len(code): + command = code[code_ptr] + + if command == '>': + pointer += 1 + elif command == '<': + pointer -= 1 + elif command == '+': + if pointer not in memory: + memory[pointer] = 0 + memory[pointer] += 1 + elif command == '-': + if pointer not in memory: + memory[pointer] = 0 + memory[pointer] -= 1 + elif command == '.': + print(chr(memory.get(pointer, 0)), end='') + elif command == ',': + memory[pointer] = ord(input()) + elif command == '[': + if memory.get(pointer, 0) == 0: + loop_depth = 1 + while loop_depth > 0: + code_ptr += 1 + if code[code_ptr] == '[': + loop_depth += 1 + elif code[code_ptr] == ']': + loop_depth -= 1 + else: + loops.append(code_ptr) + elif command == ']': + if memory.get(pointer, 0) != 0: + code_ptr = loops[-1] + else: + loops.pop() + code_ptr += 1 + +# Example usage +interpret("++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.") +# Output: Hello World! diff --git a/modules/csv/init.ar b/modules/csv/init.ar index d2accd1..91cd790 100644 --- a/modules/csv/init.ar +++ b/modules/csv/init.ar @@ -12,17 +12,14 @@ let read(path) = do char = line[j] if (char == "," && not in_quotes) do value = line[start:j].rightstrip("\r").strip() - if (value && value[0] == "\"" && value[-1] == "\"") do - value = value[1:value.length - 1] + if (value && value[0] == "\"" && value[-1] == "\"") value = value[1:value.length - 1] value = value.replace("\"\"", "\"") values.append(value) start = j + 1 - else if (char == "\"") do - in_quotes = not in_quotes + else if (char == "\"") in_quotes = not in_quotes if (j == line.length - 1) do value = line[start:j + 1].rightstrip("\r").strip() - if (value && value[0] == "\"" && value[-1] == "\"") do - value = value[1:value.length - 1] + if (value && value[0] == "\"" && value[-1] == "\"") value = value[1:value.length - 1] value = value.replace("\"\"", "\"") values.append(value) data.append(values) diff --git a/src/array.go b/src/array.go index aac033b..e093a67 100644 --- a/src/array.go +++ b/src/array.go @@ -20,8 +20,8 @@ func isArray(code UNPARSEcode) bool { func ArArray(arr []any) ArObject { val := ArObject{ - "array", anymap{ + "__name__": "array", "__value__": arr, "length": newNumber().SetUint64(uint64(len(arr))), }, @@ -39,7 +39,7 @@ func ArArray(arr []any) ArObject { if typeof(a[0]) != "number" { return nil, ArErr{ TYPE: "TypeError", - message: "index must be a number", + message: "dex must be a number", EXISTS: true, } } @@ -499,7 +499,6 @@ func ArArray(arr []any) ArObject { } output := []string{} for _, v := range arr { - v = ArValidToAny(v) if typeof(v) != "string" { return nil, ArErr{ TYPE: "TypeError", @@ -563,6 +562,31 @@ func ArArray(arr []any) ArObject { } return true, ArErr{} }} + val.obj["__Contains__"] = builtinFunc{ + "__Contains__", + func(args ...any) (any, ArErr) { + if len(args) != 1 { + return nil, ArErr{ + TYPE: "TypeError", + message: "missing argument", + EXISTS: true, + } + } + for _, v := range arr { + res, err := runOperation(operationType{ + operation: 8, + values: []any{v, args[0]}, + }, stack{}, 0) + if err.EXISTS { + return nil, err + } + if anyToBool(res) { + return true, ArErr{} + } + } + return false, ArErr{} + }, + } return val } diff --git a/src/arvalid.go b/src/arvalid.go index 298bccd..c1c4b59 100644 --- a/src/arvalid.go +++ b/src/arvalid.go @@ -6,6 +6,8 @@ func AnyToArValid(arr any) any { return ArArray(arr) case string: return ArString(arr) + case anymap: + return Map(arr) default: return arr } @@ -14,17 +16,9 @@ func AnyToArValid(arr any) any { func ArValidToAny(a any) any { switch a := a.(type) { case ArObject: - switch a.TYPE { - case "string": - return a.obj["__value__"] - case "array": - return a.obj["__value__"] - case "class": - return a.obj["__value__"] - default: - return a.obj + if v, ok := a.obj["__value__"]; ok { + return v } - default: - return a } + return a } diff --git a/src/boolean.go b/src/boolean.go index 1e20239..b545f25 100644 --- a/src/boolean.go +++ b/src/boolean.go @@ -13,7 +13,7 @@ func anyToBool(x any) bool { case nil: return false case ArObject: - if x.TYPE == "array" { + if typeof(x) == "array" { return len(x.obj["__value__"].([]any)) != 0 } return len(x.obj) != 0 diff --git a/src/built-ins.go b/src/built-ins.go index 6016c14..006988d 100644 --- a/src/built-ins.go +++ b/src/built-ins.go @@ -1,23 +1,28 @@ package main +import ( + "fmt" + "os" +) + func makeGlobal(allowDocument bool) ArObject { - var vars = Map(anymap{}) - vars.obj["global"] = vars + var vars = anymap{} + vars["global"] = vars + vars["term"] = ArTerm if allowDocument { - vars.obj["document"] = ArDocument + vars["document"] = ArDocument } - vars.obj["js"] = ArJS - vars.obj["term"] = ArTerm - vars.obj["number"] = builtinFunc{"number", ArgonNumber} - vars.obj["string"] = builtinFunc{"string", ArgonString} - vars.obj["infinity"] = infinity - vars.obj["map"] = builtinFunc{"map", func(a ...any) (any, ArErr) { + vars["js"] = ArJS + vars["number"] = builtinFunc{"number", ArgonNumber} + vars["string"] = builtinFunc{"string", ArgonString} + vars["infinity"] = infinity + vars["map"] = builtinFunc{"map", func(a ...any) (any, ArErr) { if len(a) == 0 { return Map(anymap{}), ArErr{} } switch x := a[0].(type) { case ArObject: - if x.TYPE == "array" { + if typeof(x) == "array" { newmap := anymap{} for i, v := range x.obj["__value__"].([]any) { v := ArValidToAny(v) @@ -35,7 +40,7 @@ func makeGlobal(allowDocument bool) ArObject { newmap[i] = v } return Map(newmap), ArErr{} - } else if x.TYPE == "string" { + } else if typeof(x) == "string" { newmap := anymap{} for i, v := range x.obj["__value__"].(string) { newmap[i] = ArString(string(v)) @@ -46,15 +51,15 @@ func makeGlobal(allowDocument bool) ArObject { } return nil, ArErr{TYPE: "TypeError", message: "Cannot create map from '" + typeof(a[0]) + "'", EXISTS: true} }} - vars.obj["array"] = builtinFunc{"array", func(a ...any) (any, ArErr) { + vars["array"] = builtinFunc{"array", func(a ...any) (any, ArErr) { if len(a) == 0 { return ArArray([]any{}), ArErr{} } switch x := a[0].(type) { case ArObject: - if x.TYPE == "array" { + if typeof(x) == "array" { return x, ArErr{} - } else if x.TYPE == "string" { + } else if typeof(x) == "string" { newarray := []any{} for _, v := range x.obj["__value__"].(string) { @@ -70,22 +75,22 @@ func makeGlobal(allowDocument bool) ArObject { } return nil, ArErr{TYPE: "TypeError", message: "Cannot create array from '" + typeof(a[0]) + "'", EXISTS: true} }} - vars.obj["boolean"] = builtinFunc{"boolean", func(a ...any) (any, ArErr) { + vars["boolean"] = builtinFunc{"boolean", func(a ...any) (any, ArErr) { if len(a) == 0 { return false, ArErr{} } return anyToBool(a[0]), ArErr{} }} - vars.obj["time"] = ArTime - vars.obj["PI"] = PI - vars.obj["π"] = PI - vars.obj["e"] = e - vars.obj["ln"] = builtinFunc{"ln", ArgonLn} - vars.obj["log"] = builtinFunc{"log", ArgonLog} - vars.obj["logN"] = builtinFunc{"logN", ArgonLogN} - vars.obj["thread"] = builtinFunc{"thread", ArThread} - vars.obj["input"] = ArInput - vars.obj["round"] = builtinFunc{"round", func(a ...any) (any, ArErr) { + vars["time"] = ArTime + vars["PI"] = PI + vars["π"] = PI + vars["e"] = e + vars["ln"] = builtinFunc{"ln", ArgonLn} + vars["log"] = builtinFunc{"log", ArgonLog} + vars["logN"] = builtinFunc{"logN", ArgonLogN} + vars["thread"] = builtinFunc{"thread", ArThread} + vars["input"] = ArInput + vars["round"] = builtinFunc{"round", func(a ...any) (any, ArErr) { if len(a) == 0 { return nil, ArErr{TYPE: "round", message: "round takes 1 argument", EXISTS: true} @@ -109,7 +114,7 @@ func makeGlobal(allowDocument bool) ArObject { } return nil, ArErr{TYPE: "TypeError", message: "Cannot round '" + typeof(a[0]) + "'", EXISTS: true} }} - vars.obj["floor"] = builtinFunc{"floor", func(a ...any) (any, ArErr) { + vars["floor"] = builtinFunc{"floor", func(a ...any) (any, ArErr) { if len(a) == 0 { return nil, ArErr{TYPE: "floor", message: "floor takes 1 argument", EXISTS: true} @@ -120,7 +125,7 @@ func makeGlobal(allowDocument bool) ArObject { } return nil, ArErr{TYPE: "TypeError", message: "Cannot floor '" + typeof(a[0]) + "'", EXISTS: true} }} - vars.obj["ceil"] = builtinFunc{"ceil", func(a ...any) (any, ArErr) { + vars["ceil"] = builtinFunc{"ceil", func(a ...any) (any, ArErr) { if len(a) == 0 { return nil, ArErr{TYPE: "ceil", message: "ceil takes 1 argument", EXISTS: true} @@ -132,26 +137,26 @@ func makeGlobal(allowDocument bool) ArObject { } return nil, ArErr{TYPE: "TypeError", message: "Cannot ceil '" + typeof(a[0]) + "'", EXISTS: true} }} - vars.obj["sqrt"] = builtinFunc{"sqrt", ArgonSqrt} - vars.obj["file"] = ArFile - vars.obj["random"] = ArRandom - vars.obj["json"] = ArJSON - vars.obj["sin"] = ArSin - vars.obj["arcsin"] = ArArcsin - vars.obj["cos"] = ArCos - vars.obj["arccos"] = ArArccos - vars.obj["tan"] = ArTan - vars.obj["arctan"] = ArArctan - vars.obj["cosec"] = ArCosec - vars.obj["arccosec"] = ArArccosec - vars.obj["sec"] = ArSec - vars.obj["arcsec"] = ArArcsec - vars.obj["cot"] = ArCot - vars.obj["arccot"] = ArArccot - vars.obj["todeg"] = ArToDeg - vars.obj["torad"] = ArToRad - vars.obj["abs"] = ArAbs - vars.obj["dir"] = builtinFunc{"dir", func(a ...any) (any, ArErr) { + vars["sqrt"] = builtinFunc{"sqrt", ArgonSqrt} + vars["file"] = ArFile + vars["random"] = ArRandom + vars["json"] = ArJSON + vars["sin"] = ArSin + vars["arcsin"] = ArArcsin + vars["cos"] = ArCos + vars["arccos"] = ArArccos + vars["tan"] = ArTan + vars["arctan"] = ArArctan + vars["cosec"] = ArCosec + vars["arccosec"] = ArArccosec + vars["sec"] = ArSec + vars["arcsec"] = ArArcsec + vars["cot"] = ArCot + vars["arccot"] = ArArccot + vars["todeg"] = ArToDeg + vars["torad"] = ArToRad + vars["abs"] = ArAbs + vars["dir"] = builtinFunc{"dir", func(a ...any) (any, ArErr) { if len(a) == 0 { return ArArray([]any{}), ArErr{} } @@ -166,17 +171,17 @@ func makeGlobal(allowDocument bool) ArObject { } return ArArray([]any{}), ArErr{} }} - vars.obj["subprocess"] = builtinFunc{"subprocess", ArSubprocess} - vars.obj["class"] = builtinFunc{"class", func(a ...any) (any, ArErr) { + vars["subprocess"] = builtinFunc{"subprocess", ArSubprocess} + vars["object"] = builtinFunc{"object", func(a ...any) (any, ArErr) { if len(a) == 0 { return nil, ArErr{TYPE: "TypeError", message: "Cannot create class from '" + typeof(a[0]) + "'", EXISTS: true} } switch x := a[0].(type) { case ArObject: - if x.TYPE == "class" { + if typeof(x) == "object" { return x, ArErr{} } - newclass := ArObject{TYPE: "class", obj: anymap{}} + newclass := ArObject{obj: anymap{}} for key, val := range x.obj { newclass.obj[key] = val } @@ -184,5 +189,50 @@ func makeGlobal(allowDocument bool) ArObject { } return nil, ArErr{TYPE: "TypeError", message: "Cannot create class from '" + typeof(a[0]) + "'", EXISTS: true} }} - return vars + vars["sequence"] = builtinFunc{"sequence", ArSequence} + vars["exit"] = builtinFunc{"exit", func(a ...any) (any, ArErr) { + if len(a) == 0 { + os.Exit(0) + } + switch x := a[0].(type) { + case number: + os.Exit(int(floor(x).Num().Int64())) + } + os.Exit(0) + return nil, ArErr{} + }} + vars["error"] = builtinFunc{"error", func(a ...any) (any, ArErr) { + if len(a) < 1 || len(a) > 2 { + return nil, ArErr{TYPE: "error", message: "error takes 1 or 2 arguments, got " + fmt.Sprint(len(a)), EXISTS: true} + } + if len(a) == 1 { + a[0] = ArValidToAny(a[0]) + switch x := a[0].(type) { + case string: + return nil, ArErr{TYPE: "Error", message: x, EXISTS: true} + } + } else { + a[0] = ArValidToAny(a[0]) + a[1] = ArValidToAny(a[1]) + switch x := a[0].(type) { + case string: + switch y := a[1].(type) { + case string: + return nil, ArErr{TYPE: x, message: y, EXISTS: true} + } + } + } + return nil, ArErr{TYPE: "TypeError", message: "Cannot create error from '" + typeof(a[0]) + "'", EXISTS: true} + }} + vars["chr"] = builtinFunc{"chr", func(a ...any) (any, ArErr) { + if len(a) != 1 { + return nil, ArErr{TYPE: "chr", message: "chr takes 1 argument, got " + fmt.Sprint(len(a)), EXISTS: true} + } + switch x := a[0].(type) { + case number: + return string([]rune{rune(floor(x).Num().Int64())}), ArErr{} + } + return nil, ArErr{TYPE: "TypeError", message: "Cannot convert '" + typeof(a[0]) + "' to string", EXISTS: true} + }} + return Map(vars) } diff --git a/src/call.go b/src/call.go index c504edc..7604cd6 100644 --- a/src/call.go +++ b/src/call.go @@ -16,6 +16,7 @@ type call struct { } type Callable struct { + name string params []string run any code string @@ -110,12 +111,33 @@ func runCall(c call, stack stack, stacklevel int) (any, ArErr) { if len(x.params) != len(args) { return nil, ArErr{"Runtime Error", "expected " + fmt.Sprint(len(x.params)) + " arguments, got " + fmt.Sprint(len(args)), c.line, c.path, c.code, true} } - level := newscope() + l := anymap{} for i, param := range x.params { - level.obj[param] = args[i] + l[param] = args[i] } - resp, err := runVal(x.run, append(x.stack, level), stacklevel+1) + resp, err := runVal(x.run, append(x.stack, Map(l)), stacklevel+1) return ThrowOnNonLoop(openReturn(resp), err) } return nil, ArErr{"Runtime Error", "type '" + typeof(callable) + "' is not callable", c.line, c.path, c.code, true} } + +func builtinCall(callable any, args []any) (any, ArErr) { + + switch x := callable.(type) { + case builtinFunc: + resp, err := x.FUNC(args...) + resp = AnyToArValid(resp) + return resp, err + case Callable: + if len(x.params) != len(args) { + return nil, ArErr{TYPE: "Runtime Error", message: "expected " + fmt.Sprint(len(x.params)) + " arguments, got " + fmt.Sprint(len(args)), EXISTS: true} + } + level := newscope() + for i, param := range x.params { + level.obj[param] = args[i] + } + resp, err := runVal(x.run, append(x.stack, level), 0) + return ThrowOnNonLoop(openReturn(resp), err) + } + return nil, ArErr{TYPE: "Runtime Error", message: "type '" + typeof(callable) + "' is not callable", EXISTS: true} +} diff --git a/src/file.go b/src/file.go index b69ef2f..ff58052 100644 --- a/src/file.go +++ b/src/file.go @@ -28,6 +28,7 @@ func ArRead(args ...any) (any, ArErr) { if typeof(args[0]) != "string" { return ArObject{}, ArErr{TYPE: "Runtime Error", message: "read takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} } + args[0] = ArValidToAny(args[0]) filename := args[0].(string) file, err := os.Open(filename) if err != nil { @@ -58,6 +59,7 @@ func ArWrite(args ...any) (any, ArErr) { if typeof(args[0]) != "string" { return ArObject{}, ArErr{TYPE: "Runtime Error", message: "write takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} } + args[0] = ArValidToAny(args[0]) filename := args[0].(string) file, err := os.Create(filename) if err != nil { diff --git a/src/getIndex.go b/src/getIndex.go index 6d82ded..7f992e3 100644 --- a/src/getIndex.go +++ b/src/getIndex.go @@ -1,13 +1,11 @@ package main import ( - "fmt" "strings" ) type ArObject struct { - TYPE string - obj anymap + obj anymap } type anymap map[any]any @@ -16,12 +14,11 @@ var mapGetCompile = makeRegex(`(.|\n)+\.([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|( var indexGetCompile = makeRegex(`(.|\n)+\[(.|\n)+\]( *)`) type ArMapGet struct { - VAL any - args []any - index bool - line int - code string - path string + VAL any + args []any + line int + code string + path string } func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { @@ -31,57 +28,24 @@ func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { } switch m := resp.(type) { case ArObject: - if r.index && m.TYPE != "map" { - if _, ok := m.obj["__getindex__"]; ok { - callable := m.obj["__getindex__"] - resp, err := runCall(call{ - callable: callable, - args: r.args, - line: r.line, - path: r.path, - code: r.code, - }, stack, stacklevel+1) - if !err.EXISTS { - return resp, ArErr{} + if _, ok := m.obj["__getindex__"]; ok { + callable := m.obj["__getindex__"] + resp, err := runCall(call{ + callable: callable, + args: r.args, + line: r.line, + path: r.path, + code: r.code, + }, stack, stacklevel+1) + if !err.EXISTS { + return resp, ArErr{} + } + if len(r.args) == 1 && !isUnhashable(r.args[0]) { + if _, ok := m.obj[r.args[0]]; ok { + return m.obj[r.args[0]], ArErr{} } } } - if len(r.args) > 1 { - return nil, ArErr{ - "IndexError", - "index not found", - r.line, - r.path, - r.code, - true, - } - } - key, err := runVal(r.args[0], stack, stacklevel+1) - if err.EXISTS { - return nil, err - } - key = ArValidToAny(key) - if isUnhashable(key) { - return nil, ArErr{ - "TypeError", - "unhashable type: '" + typeof(key) + "'", - r.line, - r.path, - r.code, - true, - } - } - if _, ok := m.obj[key]; !ok { - return nil, ArErr{ - "KeyError", - "key '" + fmt.Sprint(key) + "' not found", - r.line, - r.path, - r.code, - true, - } - } - return AnyToArValid(m.obj[key]), ArErr{} } key, err := runVal(r.args[0], stack, stacklevel+1) @@ -111,7 +75,7 @@ func mapGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapGet if !worked { return ArMapGet{}, false, err, i } - return ArMapGet{resp, []any{key}, false, code.line, code.realcode, code.path}, true, ArErr{}, 1 + return ArMapGet{resp, []any{key}, code.line, code.realcode, code.path}, true, ArErr{}, 1 } func isIndexGet(code UNPARSEcode) bool { @@ -149,7 +113,7 @@ func indexGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapG } continue } - return ArMapGet{tival, args, true, code.line, code.realcode, code.path}, true, ArErr{}, 1 + return ArMapGet{tival, args, code.line, code.realcode, code.path}, true, ArErr{}, 1 } return ArMapGet{}, false, ArErr{ "Syntax Error", @@ -161,7 +125,19 @@ func indexGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapG }, 1 } +var hashabletypes = []string{ + "number", + "string", + "bool", + "null", +} + func isUnhashable(val any) bool { keytype := typeof(val) - return keytype == "array" || keytype == "map" + for _, v := range hashabletypes { + if v == keytype { + return false + } + } + return true } diff --git a/src/input.go b/src/input.go index 88c9ac5..b0e976f 100644 --- a/src/input.go +++ b/src/input.go @@ -41,8 +41,7 @@ func getPassword(args ...any) (string, error) { char := make([]byte, 1) _, err := os.Stdin.Read(char) if err != nil { - fmt.Println(err) - break + return "", err } if char[0] == 3 || char[0] == 4 { return "", fmt.Errorf("keyboard interupt") diff --git a/src/main.go b/src/main.go index 90054bf..a7ad568 100644 --- a/src/main.go +++ b/src/main.go @@ -12,10 +12,7 @@ var Args = os.Args[1:] type stack = []ArObject func newscope() ArObject { - return ArObject{ - TYPE: "map", - obj: make(anymap), - } + return Map(anymap{}) } func main() { diff --git a/src/map.go b/src/map.go index 08b616b..c975ffb 100644 --- a/src/map.go +++ b/src/map.go @@ -3,6 +3,7 @@ package main import ( "fmt" "strings" + "sync" ) var mapCompiled = makeRegex(`( *)\{(((( *).+( *):( *).+( *))|(` + spacelessVariable + `))(( *)\,(( *).+( *):( *).+( *))|(` + spacelessVariable + `)))*\}( *)`) @@ -25,16 +26,146 @@ func parseMap(code UNPARSEcode) (any, UNPARSEcode) { return nil, UNPARSEcode{} } -func Map(val anymap) ArObject { +func Map(m anymap) ArObject { + var mutex = sync.RWMutex{} return ArObject{ - TYPE: "map", - obj: val, - } -} - -func Class(val anymap) ArObject { - return ArObject{ - TYPE: "class", - obj: val, + obj: anymap{ + "__value__": m, + "__name__": "map", + "get": builtinFunc{ + "get", + func(args ...any) (any, ArErr) { + if len(args) < 1 || len(args) > 2 { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "expected 1 or 2 argument, got " + fmt.Sprint(len(args)), + EXISTS: true, + } + } + var DEFAULT any + key := ArValidToAny(args[0]) + if isUnhashable(key) { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "unhashable type: " + typeof(key), + EXISTS: true, + } + } + if len(args) == 2 { + DEFAULT = (args[1]) + } + mutex.RLock() + if _, ok := m[key]; !ok { + mutex.RUnlock() + return DEFAULT, ArErr{} + } + v := m[key] + mutex.RUnlock() + return v, ArErr{} + }, + }, + "__Contains__": builtinFunc{ + "__Contains__", + func(args ...any) (any, ArErr) { + if len(args) != 1 { + return nil, ArErr{ + TYPE: "TypeError", + message: "expected 1 argument, got " + fmt.Sprint(len(args)), + EXISTS: true, + } + } + key := ArValidToAny(args[0]) + if isUnhashable(key) { + return false, ArErr{} + } + mutex.RLock() + if _, ok := m[key]; !ok { + mutex.RUnlock() + return false, ArErr{} + } + mutex.RUnlock() + return true, ArErr{} + }, + }, + "__NotContains__": builtinFunc{ + "__NotContains__", + func(args ...any) (any, ArErr) { + if len(args) != 1 { + return nil, ArErr{ + TYPE: "TypeError", + message: "expected 1 argument, got " + fmt.Sprint(len(args)), + EXISTS: true, + } + } + key := ArValidToAny(args[0]) + if isUnhashable(key) { + return true, ArErr{} + } + mutex.RLock() + if _, ok := m[key]; !ok { + mutex.RUnlock() + return true, ArErr{} + } + mutex.RUnlock() + return false, ArErr{} + }, + }, + "__setindex__": builtinFunc{ + "__setindex__", + func(args ...any) (any, ArErr) { + if len(args) != 2 { + return nil, ArErr{ + TYPE: "TypeError", + message: "expected 2 arguments, got " + fmt.Sprint(len(args)), + EXISTS: true, + } + } + if isUnhashable(args[0]) { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "unhashable type: " + typeof(args[0]), + EXISTS: true, + } + } + key := ArValidToAny(args[0]) + mutex.Lock() + m[key] = args[1] + mutex.Unlock() + return nil, ArErr{} + }, + }, + "__getindex__": builtinFunc{ + "__getindex__", + func(args ...any) (any, ArErr) { + if len(args) != 1 { + return nil, ArErr{ + TYPE: "TypeError", + message: "expected 1 argument, got " + fmt.Sprint(len(args)), + EXISTS: true, + } + } + key := ArValidToAny(args[0]) + if isUnhashable(key) { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "unhashable type: " + typeof(key), + EXISTS: true, + } + } + mutex.RLock() + if _, ok := m[key]; !ok { + mutex.RUnlock() + return nil, ArErr{ + TYPE: "KeyError", + message: "key " + fmt.Sprint(key) + " not found", + EXISTS: true, + } + } + v := m[key] + mutex.RUnlock() + return v, ArErr{} + }, + }, + }, } } diff --git a/src/operations.go b/src/operations.go index 3648826..9df0132 100644 --- a/src/operations.go +++ b/src/operations.go @@ -557,12 +557,12 @@ func calcNotIn(o operationType, stack stack, stacklevel int) (any, ArErr) { if err.EXISTS { return false, err } - if x, ok := resp.(ArObject); ok { + if x, ok := resp2.(ArObject); ok { if y, ok := x.obj["__NotContains__"]; ok { return runCall( call{ y, - []any{resp2}, + []any{resp}, o.code, o.line, o.path, @@ -571,7 +571,7 @@ func calcNotIn(o operationType, stack stack, stacklevel int) (any, ArErr) { } return false, ArErr{ "Runtime Error", - "Cannot check if type '" + typeof(resp) + "' is in type '" + typeof(resp2) + "'", + "Cannot check if type '" + typeof(resp) + "' is not in type '" + typeof(resp2) + "'", o.line, o.path, o.code, @@ -697,7 +697,11 @@ func calcMod(o operationType, stack stack, stacklevel int) (any, ArErr) { return nil, err } if typeof(resp) == "number" && typeof(output) == "number" { - output = floor(newNumber().Quo(resp.(number), output.(number))) + x := newNumber().Set(resp.(number)) + x.Quo(output.(number), x) + x = floor(x) + x.Mul(x, resp.(number)) + output.(number).Sub(output.(number), x) continue } else if x, ok := output.(ArObject); ok { if y, ok := x.obj["__Modulo__"]; ok { diff --git a/src/sequence-and-series.go b/src/sequence-and-series.go new file mode 100644 index 0000000..5914db9 --- /dev/null +++ b/src/sequence-and-series.go @@ -0,0 +1,35 @@ +package main + +import "fmt" + +func ArSequence(a ...any) (any, ArErr) { + if len(a) < 1 || len(a) > 2 { + return nil, ArErr{TYPE: "Runtime Error", + message: fmt.Sprintf("sequence expected 1 or 2 arguments, got %d", len(a)), + EXISTS: true, + } + } + f := a[0] + initial := newNumber() + if typeof(f) != "function" { + return nil, ArErr{TYPE: "Runtime Error", + message: fmt.Sprintf("sequence expected function, got %s", typeof(f)), + EXISTS: true, + } + } + if len(a) == 2 { + if typeof(a[1]) != "number" { + return nil, ArErr{TYPE: "Runtime Error", + message: fmt.Sprintf("sequence expected number, got %s", typeof(a[1])), + EXISTS: true, + } + } + initial.Set(a[1].(number)) + } + return ArObject{ + obj: map[any]any{ + "__name__": "sequence", + "__value__": "test", + }, + }, ArErr{} +} diff --git a/src/sortany.go b/src/sortany.go index 19261f5..49518f6 100644 --- a/src/sortany.go +++ b/src/sortany.go @@ -2,16 +2,16 @@ package main import "fmt" -type keyCache map[interface{}]interface{} +type keyCache map[any]any -func quickSort(list []interface{}, getKey func(interface{}) (interface{}, ArErr)) ([]interface{}, ArErr) { +func quickSort(list []any, getKey func(any) (any, ArErr)) ([]any, ArErr) { if len(list) <= 1 { return list, ArErr{} } pivot := list[0] - var left []interface{} - var right []interface{} + var left []any + var right []any var cache = make(keyCache) @@ -51,7 +51,7 @@ func quickSort(list []interface{}, getKey func(interface{}) (interface{}, ArErr) return append(append(left, pivot), right...), ArErr{} } -func getkeyCache(getKey func(interface{}) (interface{}, ArErr), index interface{}, cache keyCache) (interface{}, ArErr) { +func getkeyCache(getKey func(any) (any, ArErr), index any, cache keyCache) (any, ArErr) { key := ArValidToAny(index) if cacheval, ok := cache[key]; ok { return cacheval, ArErr{} diff --git a/src/string.go b/src/string.go index bb08ae4..7b67ba6 100644 --- a/src/string.go +++ b/src/string.go @@ -47,9 +47,9 @@ func parseString(code UNPARSEcode) (string, bool, ArErr, int) { func ArString(str string) ArObject { obj := ArObject{ - "string", anymap{ "__value__": str, + "__name__": "string", "length": newNumber().SetUint64(uint64(len(str))), }, } @@ -173,6 +173,7 @@ func ArString(str string) ArObject { } output := []string{str} for _, v := range a { + v = ArValidToAny(v) if typeof(v) != "string" { return nil, ArErr{"TypeError", "expected string, got " + typeof(v), 0, "", "", true} } @@ -297,7 +298,9 @@ func ArString(str string) ArObject { if typeof(a[1]) != "string" { return nil, ArErr{"TypeError", "expected string, got " + typeof(a[1]), 0, "", "", true} } - return strings.Replace(str, a[0].(ArObject).obj["__value__"].(string), a[1].(string), -1), ArErr{} + a[0] = ArValidToAny(a[0]) + a[1] = ArValidToAny(a[1]) + return strings.Replace(str, a[0].(string), a[1].(string), -1), ArErr{} }} obj.obj["contains"] = builtinFunc{ "contains", @@ -494,7 +497,8 @@ func ArString(str string) ArObject { if typeof(a[0]) != "string" { return nil, ArErr{"TypeError", "cannot get less than or equal to of type " + typeof(a[0]) + " from string", 0, "", "", true} } - return str <= a[0].(ArObject).obj["__value__"].(string), ArErr{} + a[0] = ArValidToAny(a[0]) + return str <= a[0].(string), ArErr{} }} obj.obj["__LessThan__"] = builtinFunc{ "__LessThan__", @@ -505,7 +509,8 @@ func ArString(str string) ArObject { if typeof(a[0]) != "string" { return nil, ArErr{"TypeError", "cannot get less than of type " + typeof(a[0]) + " from string", 0, "", "", true} } - return str < a[0].(ArObject).obj["__value__"].(string), ArErr{} + a[0] = ArValidToAny(a[0]) + return str < a[0].(string), ArErr{} }} obj.obj["__GreaterThan__"] = builtinFunc{ "__GreaterThan__", @@ -516,7 +521,8 @@ func ArString(str string) ArObject { if typeof(a[0]) != "string" { return nil, ArErr{"TypeError", "cannot get greater than of type " + typeof(a[0]) + " from string", 0, "", "", true} } - return str > a[0].(ArObject).obj["__value__"].(string), ArErr{} + a[0] = ArValidToAny(a[0]) + return str > a[0].(string), ArErr{} }} obj.obj["__GreaterThanEqual__"] = builtinFunc{ @@ -528,7 +534,8 @@ func ArString(str string) ArObject { if typeof(a[0]) != "string" { return nil, ArErr{"TypeError", "cannot get greater than or equal to of type " + typeof(a[0]) + " from string", 0, "", "", true} } - return str >= a[0].(ArObject).obj["__value__"].(string), ArErr{} + a[0] = ArValidToAny(a[0]) + return str >= a[0].(string), ArErr{} }} obj.obj["__Equal__"] = builtinFunc{ "__Equal__", @@ -536,6 +543,7 @@ func ArString(str string) ArObject { if len(a) != 1 { return nil, ArErr{"TypeError", "expected 1 argument, got " + fmt.Sprint(len(a)), 0, "", "", true} } + a[0] = ArValidToAny(a[0]) return str == a[0], ArErr{} }} obj.obj["__NotEqual__"] = builtinFunc{ @@ -544,6 +552,7 @@ func ArString(str string) ArObject { if len(a) != 1 { return nil, ArErr{"TypeError", "expected 1 argument, got " + fmt.Sprint(len(a)), 0, "", "", true} } + a[0] = ArValidToAny(a[0]) return str != a[0], ArErr{} }} obj.obj["__Add__"] = builtinFunc{ @@ -552,10 +561,11 @@ func ArString(str string) ArObject { if len(a) != 1 { return nil, ArErr{"TypeError", "expected 1 argument, got " + fmt.Sprint(len(a)), 0, "", "", true} } + a[0] = ArValidToAny(a[0]) if typeof(a[0]) != "string" { - return nil, ArErr{"TypeError", "cannot add " + typeof(a[0]) + " to string", 0, "", "", true} + a[0] = anyToArgon(a[0], false, false, 3, 0, false, 0) } - return strings.Join([]string{str, a[0].(ArObject).obj["__value__"].(string)}, ""), ArErr{} + return strings.Join([]string{str, a[0].(string)}, ""), ArErr{} }} obj.obj["__Multiply__"] = builtinFunc{ "__Multiply__", @@ -584,7 +594,8 @@ func ArString(str string) ArObject { if typeof(a[0]) != "string" { return nil, ArErr{"TypeError", "cannot check if string contains " + typeof(a[0]), 0, "", "", true} } - return strings.Contains(str, a[0].(ArObject).obj["__value__"].(string)), ArErr{} + a[0] = ArValidToAny(a[0]) + return strings.Contains(str, a[0].(string)), ArErr{} }} obj.obj["__Subtract__"] = builtinFunc{ "__Subtract__", @@ -595,7 +606,8 @@ func ArString(str string) ArObject { if typeof(a[0]) != "string" { return nil, ArErr{"TypeError", "cannot subtract " + typeof(a[0]) + " from string", 0, "", "", true} } - return strings.Replace(str, a[0].(ArObject).obj["__value__"].(string), "", -1), ArErr{} + a[0] = ArValidToAny(a[0]) + return strings.Replace(str, a[0].(string), "", -1), ArErr{} }} obj.obj["__Divide__"] = builtinFunc{ "__Divide__", @@ -606,7 +618,8 @@ func ArString(str string) ArObject { if typeof(a[0]) != "string" { return nil, ArErr{"TypeError", "cannot divide string by " + typeof(a[0]), 0, "", "", true} } - splitby := a[0].(ArObject).obj["__value__"].(string) + a[0] = ArValidToAny(a[0]) + splitby := a[0].(string) output := []any{} splitted := (strings.Split(str, splitby)) for _, v := range splitted { diff --git a/src/term-class.go b/src/term-class.go index a985e57..f61f784 100644 --- a/src/term-class.go +++ b/src/term-class.go @@ -98,7 +98,7 @@ var ArTerm = Map(anymap{ "time": builtinFunc{"time", func(args ...any) (any, ArErr) { var id any = nil if len(args) > 0 { - id = args[0] + id = ArValidToAny(args[0]) } timing[id] = time.Now() return nil, ArErr{} @@ -107,7 +107,7 @@ var ArTerm = Map(anymap{ "timeEnd": builtinFunc{"timeEnd", func(args ...any) (any, ArErr) { var id any = nil if len(args) > 0 { - id = args[0] + id = ArValidToAny(args[0]) } if _, ok := timing[id]; !ok { return nil, ArErr{TYPE: "TypeError", message: "Cannot find timer with id '" + fmt.Sprint(id) + "'", EXISTS: true} @@ -131,8 +131,11 @@ var ArInput = Map( } return ArString(resp), ArErr{} }}, - "__call__": builtinFunc{"input", func(args ...any) (any, ArErr) { - return input(args...), ArErr{} - }}, }, ) + +func init() { + ArInput.obj["__call__"] = builtinFunc{"input", func(args ...any) (any, ArErr) { + return input(args...), ArErr{} + }} +} diff --git a/src/time.go b/src/time.go index 1c1707b..c5f77de 100644 --- a/src/time.go +++ b/src/time.go @@ -7,8 +7,7 @@ import ( var MicroSeconds = newNumber().SetInt64(1000000) func ArTimeClass(N time.Time) ArObject { - return Class(anymap{ - "__value__": newNumber().Quo(newNumber().SetInt64(N.UnixMicro()), MicroSeconds), + m := Map(anymap{ "year": builtinFunc{ "year", func(a ...any) (any, ArErr) { @@ -97,6 +96,8 @@ func ArTimeClass(N time.Time) ArObject { }, }, }) + m.obj["__value__"] = newNumber().Quo(newNumber().SetInt64(N.UnixMicro()), MicroSeconds) + return m } var ArTime = Map(anymap{ @@ -112,19 +113,43 @@ var ArTime = Map(anymap{ }}, "parse": builtinFunc{"parse", func(a ...any) (any, ArErr) { if len(a) == 1 { + if typeof(a[0]) != "string" { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "parse requires a string", + EXISTS: true, + } + } + a[0] = ArValidToAny(a[0]) N, err := time.Parse(time.UnixDate, a[0].(string)) if err != nil { return nil, ArErr{ - TYPE: "ArErr", + TYPE: "Runtime Error", message: err.Error(), } } return ArTimeClass(N), ArErr{} } else if len(a) > 1 { + if typeof(a[0]) != "string" { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "parse requires a string", + EXISTS: true, + } + } + a[0] = ArValidToAny(a[0]) + if typeof(a[1]) != "string" { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "parse requires a string", + EXISTS: true, + } + } + a[1] = ArValidToAny(a[1]) N, err := time.Parse(a[0].(string), a[1].(string)) if err != nil { return nil, ArErr{ - TYPE: "ArErr", + TYPE: "Runtime Error", message: err.Error(), EXISTS: true, } @@ -132,17 +157,26 @@ var ArTime = Map(anymap{ return ArTimeClass(N), ArErr{} } return nil, ArErr{ - TYPE: "ArErr", - message: "parse requires 2 arguments", + TYPE: "Runtime Error", + message: "parse requires 1 or 2 arguments", EXISTS: true, } }}, "parseInLocation": builtinFunc{"parseInLocation", func(a ...any) (any, ArErr) { - if len(a) > 2 { + if len(a) != 2 { + if typeof(a[0]) != "string" || typeof(a[1]) != "string" { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "parseInLocation requires a string", + EXISTS: true, + } + } + a[0] = ArValidToAny(a[0]) + a[1] = ArValidToAny(a[1]) N, err := time.ParseInLocation(a[0].(string), a[1].(string), time.Local) if err != nil { return nil, ArErr{ - TYPE: "ArErr", + TYPE: "Runtime Error", message: err.Error(), EXISTS: true, } @@ -150,18 +184,26 @@ var ArTime = Map(anymap{ return ArTimeClass(N), ArErr{} } return nil, ArErr{ - TYPE: "ArErr", - message: "parseInLocation requires 3 arguments", + TYPE: "Runtime Error", + message: "parseInLocation requires 2 arguments", EXISTS: true, } }, }, "date": builtinFunc{"date", func(a ...any) (any, ArErr) { - if len(a) > 0 { + if len(a) != 1 { + if typeof(a[0]) != "string" { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "date requires a string", + EXISTS: true, + } + } + a[0] = ArValidToAny(a[0]) N, err := time.Parse(time.UnixDate, a[0].(string)) if err != nil { return nil, ArErr{ - TYPE: "ArErr", + TYPE: "Runtime Error", message: err.Error(), EXISTS: true, } @@ -169,45 +211,66 @@ var ArTime = Map(anymap{ return ArTimeClass(N), ArErr{} } return nil, ArErr{ - TYPE: "ArErr", + TYPE: "Runtime Error", message: "date requires 1 argument", EXISTS: true, } }, }, - "Unix": builtinFunc{"Unix", func(a ...any) (any, ArErr) { - if len(a) > 1 { + "unix": builtinFunc{"unix", func(a ...any) (any, ArErr) { + if len(a) != 2 { + if typeof(a[0]) != "number" || typeof(a[1]) != "number" { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "unix requires a number", + EXISTS: true, + } + } sec, _ := a[0].(number).Float64() nsec, _ := a[1].(number).Float64() return ArTimeClass(time.Unix(int64(sec), int64(nsec))), ArErr{} } return nil, ArErr{ - TYPE: "ArErr", - message: "Unix requires 2 arguments", + TYPE: "Runtime Error", + message: "unix requires 2 arguments", EXISTS: true, } }, }, - "UnixMilli": builtinFunc{"UnixMilli", func(a ...any) (any, ArErr) { - if len(a) > 0 { + "unixMilli": builtinFunc{"unixMilli", func(a ...any) (any, ArErr) { + if len(a) != 1 { + if typeof(a[0]) != "number" { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "unixMilli requires a number", + EXISTS: true, + } + } msec, _ := a[0].(number).Float64() return ArTimeClass(time.UnixMilli(int64(msec))), ArErr{} } return nil, ArErr{ - TYPE: "ArErr", + TYPE: "Runtime Error", message: "UnixMilli requires 1 argument", EXISTS: true, } }, }, - "UnixMicro": builtinFunc{"UnixMicro", func(a ...any) (any, ArErr) { + "unixMicro": builtinFunc{"unixMicro", func(a ...any) (any, ArErr) { if len(a) > 0 { + if typeof(a[0]) != "number" { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "unixMicro requires a number", + EXISTS: true, + } + } usec, _ := a[0].(number).Float64() return ArTimeClass(time.UnixMicro(int64(usec))), ArErr{} } return nil, ArErr{ - TYPE: "ArErr", - message: "UnixMicro requires 1 argument", + TYPE: "Runtime Error", + message: "unixMicro requires 1 argument", EXISTS: true, } }, diff --git a/src/to-argon.go b/src/to-argon.go index 2de7e75..c45307f 100644 --- a/src/to-argon.go +++ b/src/to-argon.go @@ -3,6 +3,7 @@ package main import ( "fmt" "math" + "sort" "strconv" "strings" @@ -78,6 +79,9 @@ func anyToArgon(x any, quote bool, simplify bool, depth int, indent int, colored return "{}" } keys := make([]any, len(x)) + sort.Slice(keys, func(i, j int) bool { + return anyToArgon(keys[i], false, true, 0, 0, false, 0) < anyToArgon(keys[j], false, true, 0, 0, false, 0) + }) i := 0 for k := range x { @@ -148,7 +152,7 @@ func anyToArgon(x any, quote bool, simplify bool, depth int, indent int, colored if colored { output = append(output, "\x1b[38;5;240m") } - output = append(output, "") + output = append(output, "") if colored { output = append(output, "\x1b[0m") } diff --git a/src/translate.go b/src/translate.go index 00c549a..785099c 100644 --- a/src/translate.go +++ b/src/translate.go @@ -103,6 +103,12 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine i return resp, worked, err, i } } + if isCall(code) { + resp, worked, err, i = parseCall(code, index, codelines) + if worked { + return resp, worked, err, i + } + } { operation, worked, err, step := parseOperations(code, index, codelines) if worked { @@ -113,16 +119,9 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine i } if isNegative(code) { return parseNegative(code, index, codelines) - } else if isCall(code) { - resp, worked, err, i = parseCall(code, index, codelines) - if worked { - return resp, worked, err, i - } - } - if isMapGet(code) { + } else if isMapGet(code) { return mapGetParse(code, index, codelines) - } - if isIndexGet(code) { + } else if isIndexGet(code) { resp, worked, err, i = indexGetParse(code, index, codelines) if worked { return resp, worked, err, i diff --git a/src/trycatch.go b/src/trycatch.go index 9521950..1fd0c52 100644 --- a/src/trycatch.go +++ b/src/trycatch.go @@ -59,15 +59,15 @@ func parseTryCatch(code UNPARSEcode, index int, codelines []UNPARSEcode) (TryCat func runTryCatch(t TryCatch, stack stack, stacklevel int) (any, ArErr) { val, err := runVal(t.Try, stack, stacklevel) if err.EXISTS { - scope := newscope() - scope.obj[t.errorName] = Map(anymap{ + vars := anymap{} + vars[t.errorName] = Map(anymap{ "type": err.TYPE, "message": err.message, "line": newNumber().SetInt64(int64(err.line)), "path": err.path, "code": err.code, }) - val, err = runVal(t.Catch, append(stack, scope), stacklevel) + val, err = runVal(t.Catch, append(stack, Map(vars)), stacklevel) if err.EXISTS { return nil, err } diff --git a/src/typeof.go b/src/typeof.go index 3984d16..c00f44c 100644 --- a/src/typeof.go +++ b/src/typeof.go @@ -19,7 +19,13 @@ func typeof(val any) string { case builtinFunc: return "function" case ArObject: - return x.TYPE + if val, ok := x.obj["__name__"]; ok { + val := ArValidToAny(val) + if val, ok := val.(string); ok { + return val + } + } + return "object" case accessVariable: return "variable" } diff --git a/src/variable.go b/src/variable.go index 37b8bfd..aca7d7f 100644 --- a/src/variable.go +++ b/src/variable.go @@ -2,19 +2,16 @@ package main import ( "strings" - "sync" ) var spacelessVariable = `([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|(\p{L}\p{M}*))*` var SpacelessVariableCompiled = makeRegex(spacelessVariable) -var variableCompile = makeRegex(`( *)([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|(\p{L}\p{M}*))*( *)`) +var variableCompile = makeRegex(`( *)` + spacelessVariable + `( *)`) var validname = makeRegex(`(.|\n)*(\(( *)((([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|(\p{L}\p{M}*))*)(( *)\,( *)([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|(\p{L}\p{M}*))*)*)?( *)\))`) var setVariableCompile = makeRegex(`( *)(let( +))(.|\n)+( *)=(.|\n)+`) var autoAsignVariableCompile = makeRegex(`(.|\n)+=(.|\n)+`) var deleteVariableCompile = makeRegex(`( *)delete( +)(.|\n)+( *)`) -var varMutex = sync.RWMutex{} - var blockedVariableNames = map[string]bool{ "if": true, "else": true, @@ -80,11 +77,20 @@ func parseVariable(code UNPARSEcode) (accessVariable, bool, ArErr, int) { func readVariable(v accessVariable, stack stack) (any, ArErr) { for i := len(stack) - 1; i >= 0; i-- { - varMutex.RLock() - val, ok := stack[i].obj[v.name] - varMutex.RUnlock() - if ok { - return val, ArErr{} + callable, ok := stack[i].obj["__Contains__"] + if !ok { + continue + } + contains, err := builtinCall(callable, []any{v.name}) + if err.EXISTS { + return nil, err + } + if anyToBool(contains) { + callable, ok := stack[i].obj["__getindex__"] + if !ok { + continue + } + return builtinCall(callable, []any{v.name}) } } return nil, ArErr{"Name Error", "variable \"" + v.name + "\" does not exist", v.line, v.path, v.code, true} @@ -200,7 +206,7 @@ func parseAutoAsignVariable(code UNPARSEcode, index int, lines []UNPARSEcode, is func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) { var resp any if v.function { - resp = Callable{v.params, v.value, v.code, stack, v.line} + resp = Callable{"anonymous", v.params, v.value, v.code, stack, v.line} } else { respp, err := runVal(v.value, stack, stacklevel+1) if err.EXISTS { @@ -210,32 +216,48 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) { } if v.TYPE == "let" { - varMutex.RLock() - _, ok := stack[len(stack)-1].obj[v.toset.(accessVariable).name] - varMutex.RUnlock() - if ok { - return nil, ArErr{"Name Error", "variable \"" + v.toset.(accessVariable).name + "\" already exists", v.line, v.path, v.code, true} + stackcallable, ok := stack[len(stack)-1].obj["__setindex__"] + if !ok { + return nil, ArErr{"Type Error", "stack doesn't have __setindex__", v.line, v.path, v.code, true} + } + _, err := builtinCall(stackcallable, []any{v.toset.(accessVariable).name, resp}) + if err.EXISTS { + return nil, err } - varMutex.Lock() - stack[len(stack)-1].obj[v.toset.(accessVariable).name] = resp - varMutex.Unlock() } else { switch x := v.toset.(type) { case accessVariable: + name := x.name + hasSet := false + if v.function { + resp = Callable{name, v.params, v.value, v.code, stack, v.line} + } for i := len(stack) - 1; i >= 0; i-- { - varMutex.RLock() - _, ok := stack[i].obj[x.name] - varMutex.RUnlock() - if ok { - varMutex.Lock() - stack[i].obj[x.name] = resp - varMutex.Unlock() - return ThrowOnNonLoop(resp, ArErr{}) + callable, ok := stack[i].obj["__Contains__"] + if !ok { + continue + } + contains, err := builtinCall(callable, []any{name}) + if err.EXISTS { + return nil, err + } + if anyToBool(contains) { + callable, ok := stack[i].obj["__setindex__"] + if !ok { + continue + } + builtinCall(callable, []any{name, resp}) + hasSet = true + break } } - varMutex.Lock() - stack[len(stack)-1].obj[x.name] = resp - varMutex.Unlock() + if !hasSet { + callable, ok := stack[len(stack)-1].obj["__setindex__"] + if !ok { + return nil, ArErr{"Type Error", "stack doesn't have __setindex__", v.line, v.path, v.code, true} + } + builtinCall(callable, []any{name, resp}) + } case ArMapGet: respp, err := runVal(x.VAL, stack, stacklevel+1) if err.EXISTS { @@ -251,29 +273,22 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) { } switch y := respp.(type) { case ArObject: - if y.TYPE != "map" { - if _, ok := y.obj["__setindex__"]; ok { - callable := y.obj["__setindex__"] - r := ArValidToAny(resp) - _, err := runCall(call{ - callable: callable, - args: []any{key, r}, - line: v.line, - path: v.path, - code: v.code, - }, stack, stacklevel+1) + if _, ok := y.obj["__setindex__"]; ok { + callable := y.obj["__setindex__"] + r := ArValidToAny(resp) + _, err := runCall(call{ + callable: callable, + args: []any{key, r}, + line: v.line, + path: v.path, + code: v.code, + }, stack, stacklevel+1) + if err.EXISTS { return nil, err } - } else { - if isUnhashable(key) { - return nil, ArErr{"Runtime Error", "can't use unhashable type as map key: " + typeof(key), v.line, v.path, v.code, true} - } - varMutex.Lock() - y.obj[key] = resp - varMutex.Unlock() } default: - return nil, ArErr{"Runtime Error", "can't set for non map", v.line, v.path, v.code, true} + return nil, ArErr{"Runtime Error", "can't set for non object", v.line, v.path, v.code, true} } } } @@ -304,9 +319,20 @@ func runDelete(d ArDelete, stack stack, stacklevel int) (any, ArErr) { switch x := d.value.(type) { case accessVariable: for i := len(stack) - 1; i >= 0; i-- { - if _, ok := stack[i].obj[x.name]; ok { - delete(stack[i].obj, x.name) - return nil, ArErr{} + callable, ok := stack[i].obj["__Contains__"] + if !ok { + continue + } + contains, err := builtinCall(callable, []any{x.name}) + if err.EXISTS { + return nil, err + } + if anyToBool(contains) { + callable, ok := stack[i].obj["__deleteindex__"] + if !ok { + continue + } + return builtinCall(callable, []any{x.name}) } } return nil, ArErr{"Name Error", "variable \"" + x.name + "\" does not exist", d.line, d.path, d.code, true} @@ -324,7 +350,7 @@ func runDelete(d ArDelete, stack stack, stacklevel int) (any, ArErr) { } switch y := respp.(type) { case ArObject: - if y.TYPE == "array" { + if typeof(y) == "array" { return nil, ArErr{"Runtime Error", "can't delete from array", d.line, d.path, d.code, true} } if isUnhashable(key) { diff --git a/test b/test new file mode 100755 index 0000000..8b83815 --- /dev/null +++ b/test @@ -0,0 +1,4 @@ +for FILE in ./tests/*.ar; do + echo "running : $FILE"; + ./run "$FILE"; +done \ No newline at end of file diff --git a/tests/brainfuck.ar b/tests/brainfuck.ar new file mode 100644 index 0000000..3568287 --- /dev/null +++ b/tests/brainfuck.ar @@ -0,0 +1,34 @@ +let interpret(code) = do + memory = map() + pointer = 0 + code_ptr = 0 + loops = [] + + while (code_ptr < code.length) do + command = code[code_ptr] + + if (command == '>') pointer = pointer + 1 + else if (command == '<') pointer = pointer - 1 + else if (command == '+') do + if (pointer not in memory) memory[pointer] = 0 + memory[pointer] = memory[pointer] + 1 + else if (command == '-') do + if (pointer not in memory) memory[pointer] = 0 + memory[pointer] = memory[pointer] - 1 + else if (command == '.') term.plain.oneLine(chr(memory.get(pointer, 0)), end='') + else if (command == ',') memory[pointer] = ord(input()) + else if (command == '[') do + if (memory.get(pointer, 0) == 0) do + loop_depth = 1 + while (loop_depth > 0) do + code_ptr = code_ptr + 1 + if (code[code_ptr] == '[') loop_depth = loop_depth + 1 + else if (code[code_ptr] == ']') loop_depth = loop_depth - 1 + else loops.append(code_ptr) + else if (command == ']') do + if (memory.get(pointer, 0) != 0) code_ptr = loops[-1] + else loops.pop() + code_ptr = code_ptr + 1 + + +interpret('>++++++++[<+++++++++>-]<.>++++[<+++++++>-]<+.+++++++..+++.>>++++++[<+++++++>-]<++.------------.>++++++[<+++++++++>-]<+.<.+++.------.--------.>>>++++[<++++++++>-]<+.') \ No newline at end of file diff --git a/tests/test.ar b/tests/csv_test.ar similarity index 100% rename from tests/test.ar rename to tests/csv_test.ar diff --git a/tests/example.ar b/tests/example.ar index 4dfad77..8cf0833 100644 --- a/tests/example.ar +++ b/tests/example.ar @@ -11,8 +11,7 @@ name = "william bell" term.log(getInitials(name)) term.log(name) -let addLastName(name) = do - name.append(" Bell") +let addLastName(name) = name.append(" Bell") name = "William" addLastName(name) diff --git a/tests/memoryLeakTest.ar b/tests/memoryLeakTest.ar index 00ecb63..6fc19ec 100644 --- a/tests/memoryLeakTest.ar +++ b/tests/memoryLeakTest.ar @@ -1,8 +1,7 @@ f() = do a = [] - for (i from 0 to 10000000) a.append(i) + for (i from 0 to 10000) a.append(i) term.log("start") f() -term.log("end") -time.snooze(100000000) \ No newline at end of file +term.log("end") \ No newline at end of file diff --git a/tests/stack test.ar b/tests/stack test.ar new file mode 100644 index 0000000..a22007d --- /dev/null +++ b/tests/stack test.ar @@ -0,0 +1,7 @@ +x = 10 + +do + let x = 20 + term.log(x) + +term.log(x) \ No newline at end of file diff --git a/tests/welcomemessage.ar b/tests/welcomemessage.ar index da84222..727813b 100644 --- a/tests/welcomemessage.ar +++ b/tests/welcomemessage.ar @@ -1,4 +1,3 @@ -term.log(" ____ ") term.log(" /\\ |___ \\ ") term.log(" / \\ _ __ __ _ ___ _ __ __ ____) |") term.log(" / /\\ \\ | '__/ _` |/ _ \\| '_ \\ \\ \\ / /__ < ") @@ -7,5 +6,4 @@ term.log(" /_/ \\_\\_| \\__, |\\___/|_| |_| \\_/|____/ ") term.log(" __/ | ") term.log(" |___/ ") term.log("----------------------------------------------") -term.log("Welcome to ARGON for WASM!") -term.log("write code above and click run to see it work like magic!") \ No newline at end of file +term.log("Welcome to ARGON!") \ No newline at end of file