From 4d37d8d4de724f2f92c0a7a0d8b4a7f5d20145b0 Mon Sep 17 00:00:00 2001 From: William Bell Date: Mon, 10 Jul 2023 22:18:02 +0100 Subject: [PATCH] add * import --- src/array.go | 8 +++ src/file.go | 1 + src/import.go | 2 - src/map.go | 166 +++++++++++++++++++++++++++++++++++++++++---- src/number.go | 2 +- src/operations.go | 2 +- src/parseImport.go | 23 +++++++ src/path.go | 21 ++++++ src/variable.go | 6 +- 9 files changed, 213 insertions(+), 18 deletions(-) diff --git a/src/array.go b/src/array.go index aef6f48..f949c03 100644 --- a/src/array.go +++ b/src/array.go @@ -620,6 +620,14 @@ func ArArray(arr []any) ArObject { return true, ArErr{} }, } + val.obj["copy"] = builtinFunc{ + "copy", + func(args ...any) (any, ArErr) { + arrCopy := make([]any, len(arr)) + copy(arrCopy, arr) + return ArArray(arrCopy), ArErr{} + }, + } val.obj["__Boolean__"] = builtinFunc{ "__Boolean__", func(args ...any) (any, ArErr) { diff --git a/src/file.go b/src/file.go index 95ce4d6..5f537db 100644 --- a/src/file.go +++ b/src/file.go @@ -147,6 +147,7 @@ func ArWrite(args ...any) (any, ArErr) { if typeof(args[0]) != "string" { return ArObject{}, ArErr{TYPE: "Runtime Error", message: "text takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} } + args[0] = ArValidToAny(args[0]) file.Write([]byte(args[0].(string))) return nil, ArErr{} }}, diff --git a/src/import.go b/src/import.go index 38f280a..7eafd8c 100644 --- a/src/import.go +++ b/src/import.go @@ -36,10 +36,8 @@ func readFile(path string) []UNPARSEcode { // optionally, resize scanner's capacity for lines over 64K, see next example output := []UNPARSEcode{} line := 1 - textOutput := []string{} for scanner.Scan() { text := scanner.Text() - textOutput = append(textOutput, text) output = append(output, UNPARSEcode{text, text, line, path}) line++ } diff --git a/src/map.go b/src/map.go index 93f5c9c..47903a4 100644 --- a/src/map.go +++ b/src/map.go @@ -115,8 +115,11 @@ func parseMap(code UNPARSEcode, index int, codelines []UNPARSEcode) (any, bool, } var mutex = sync.RWMutex{} +var listenersMutex = sync.RWMutex{} func Map(m anymap) ArObject { + var currentID uint32 = 0 + listeners := map[any]map[uint32]any{} obj := ArObject{ obj: anymap{ "__value__": m, @@ -168,12 +171,29 @@ func Map(m anymap) ArObject { return false, ArErr{} } mutex.RLock() - if _, ok := m[key]; !ok { + if _, ok := m[key]; ok { mutex.RUnlock() - return false, ArErr{} + return true, ArErr{} + } + for k := range m { + compare, err := runOperation( + operationType{ + operation: 9, + values: []any{key, k}, + }, + stack{}, + 0, + ) + if err.EXISTS { + continue + } + if anyToBool(compare) { + mutex.RUnlock() + return true, ArErr{} + } } mutex.RUnlock() - return true, ArErr{} + return false, ArErr{} }, }, "__NotContains__": builtinFunc{ @@ -191,12 +211,29 @@ func Map(m anymap) ArObject { return true, ArErr{} } mutex.RLock() - if _, ok := m[key]; !ok { + if _, ok := m[key]; ok { mutex.RUnlock() - return true, ArErr{} + return false, ArErr{} + } + for k := range m { + compare, err := runOperation( + operationType{ + operation: 9, + values: []any{key, k}, + }, + stack{}, + 0, + ) + if err.EXISTS { + continue + } + if anyToBool(compare) { + mutex.RUnlock() + return false, ArErr{} + } } mutex.RUnlock() - return false, ArErr{} + return true, ArErr{} }, }, "__setindex__": builtinFunc{ @@ -217,6 +254,20 @@ func Map(m anymap) ArObject { } } key := ArValidToAny(args[0]) + listenersMutex.RLock() + if _, ok := listeners[key]; ok { + for _, v := range listeners[key] { + runCall( + call{ + Callable: v, + Args: []any{args[1]}, + }, + stack{}, + 0, + ) + } + } + listenersMutex.RUnlock() mutex.Lock() m[key] = args[1] mutex.Unlock() @@ -242,17 +293,33 @@ func Map(m anymap) ArObject { } } mutex.RLock() - if _, ok := m[key]; !ok { + if v, ok := m[key]; ok { mutex.RUnlock() - return nil, ArErr{ - TYPE: "KeyError", - message: "key " + fmt.Sprint(key) + " not found", - EXISTS: true, + return v, ArErr{} + } + for k := range m { + compare, err := runOperation( + operationType{ + operation: 9, + values: []any{key, k}, + }, + stack{}, + 0, + ) + if err.EXISTS { + continue + } + if anyToBool(compare) { + mutex.RUnlock() + return m[k], ArErr{} } } - v := m[key] mutex.RUnlock() - return v, ArErr{} + return nil, ArErr{ + TYPE: "KeyError", + message: "key " + fmt.Sprint(key) + " not found", + EXISTS: true, + } }, }, }, @@ -299,6 +366,79 @@ func Map(m anymap) ArObject { return true, ArErr{} }, } + obj.obj["copy"] = builtinFunc{ + "copy", + func(args ...any) (any, ArErr) { + debugPrintln("copy", args) + if len(args) != 0 { + return nil, ArErr{ + TYPE: "TypeError", + message: "expected 0 arguments, got " + fmt.Sprint(len(args)), + EXISTS: true, + } + } + mutex.RLock() + newMap := make(anymap) + for k, v := range m { + newMap[k] = v + } + mutex.RUnlock() + return newMap, ArErr{} + }, + } + obj.obj["addKeyChangeListener"] = builtinFunc{ + "addKeyChangeListener", + 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, + } + } + key := ArValidToAny(args[0]) + if isUnhashable(key) { + return nil, ArErr{ + TYPE: "Runtime Error", + message: "unhashable type: " + typeof(args[0]), + EXISTS: true, + } + } + if typeof(args[1]) != "function" { + return nil, ArErr{ + TYPE: "TypeError", + message: "expected function, got " + typeof(args[1]), + EXISTS: true, + } + } + id := currentID + currentID++ + listenersMutex.Lock() + if _, ok := listeners[key]; !ok { + listeners[key] = map[uint32]any{} + } + listeners[key][id] = args[1] + listenersMutex.Unlock() + return anymap{ + "remove": builtinFunc{ + "remove", + func(args ...any) (any, ArErr) { + if len(args) != 0 { + return nil, ArErr{ + TYPE: "TypeError", + message: "expected 0 arguments, got " + fmt.Sprint(len(args)), + EXISTS: true, + } + } + listenersMutex.Lock() + delete(listeners[key], id) + listenersMutex.Unlock() + return nil, ArErr{} + }, + }, + }, ArErr{} + }, + } obj.obj["keys"] = builtinFunc{ "keys", func(args ...any) (any, ArErr) { diff --git a/src/number.go b/src/number.go index f1510c7..75e9e0c 100644 --- a/src/number.go +++ b/src/number.go @@ -6,7 +6,7 @@ import ( "strings" ) -var numberCompile = makeRegex("( *)(-)?((([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)( *)") +var numberCompile = makeRegex("( *)(-)?(((([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)|([0-9]+/[0-9]+))( *)") var binaryCompile = makeRegex("( *)(-)?(0b[10]+(.\\[10]+)?(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)( *)") var hexCompile = makeRegex("( *)(-)?(0x[a-fA-F0-9]+(\\.[a-fA-F0-9]+)?)( *)") var octalCompile = makeRegex("( *)(-)?(0o[0-7]+(\\.[0-7]+)?(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)( *)") diff --git a/src/operations.go b/src/operations.go index 2bae353..8a37437 100644 --- a/src/operations.go +++ b/src/operations.go @@ -781,7 +781,7 @@ func calcIntDiv(o operationType, stack stack, stacklevel int) (any, ArErr) { return nil, err } if typeof(resp) == "number" && typeof(output) == "number" { - output = output.(number).Quo(output.(number), resp.(number)) + output = floor(output.(number).Quo(output.(number), resp.(number))) continue } else if x, ok := output.(ArObject); ok { if y, ok := x.obj["__IntDivide__"]; ok { diff --git a/src/parseImport.go b/src/parseImport.go index c88a995..d478a4c 100644 --- a/src/parseImport.go +++ b/src/parseImport.go @@ -56,6 +56,8 @@ func parseGenericImport(code UNPARSEcode, index int, codeline []UNPARSEcode) (Ar continue } if after == "" { + } else if after == "*" { + asStr = true } else if variableCompile.MatchString(after) { asStr = after } else { @@ -120,6 +122,27 @@ func runImport(importOBJ ArImport, stack stack, stacklevel int) (any, ArErr) { } case string: builtinCall(setindex, []any{x, stackMap}) + case bool: + keyGetter, ok := stackMap.obj["keys"] + if !ok { + return nil, ArErr{"Import Error", "could not find keys in module scope", importOBJ.Line, importOBJ.Path, importOBJ.Code, true} + } + valueGetter, ok := stackMap.obj["__getindex__"] + if !ok { + return nil, ArErr{"Import Error", "could not find __getindex__ in module scope", importOBJ.Line, importOBJ.Path, importOBJ.Code, true} + } + keys, err := builtinCall(keyGetter, []any{}) + if err.EXISTS { + return nil, err + } + keys = ArValidToAny(keys) + for _, v := range keys.([]any) { + val, err := builtinCall(valueGetter, []any{v}) + if err.EXISTS { + return nil, err + } + builtinCall(setindex, []any{v, val}) + } } return nil, ArErr{} } diff --git a/src/path.go b/src/path.go index 63eff70..1ce3467 100644 --- a/src/path.go +++ b/src/path.go @@ -73,4 +73,25 @@ var ArPath = Map( EXISTS: true, } }}, + "parent": builtinFunc{ + "parent", + func(args ...any) (any, ArErr) { + if len(args) != 1 { + return nil, ArErr{ + TYPE: "runtime", + message: "parent takes exactly 1 argument, got " + fmt.Sprint(len(args)), + EXISTS: true, + } + } + args[0] = ArValidToAny(args[0]) + if typeof(args[0]) != "string" { + return nil, ArErr{ + TYPE: "runtime", + message: "parent argument must be a string, got " + typeof(args[0]), + EXISTS: true, + } + } + return path.Dir(args[0].(string)), ArErr{} + }, + }, }) diff --git a/src/variable.go b/src/variable.go index 65c4deb..f30b828 100644 --- a/src/variable.go +++ b/src/variable.go @@ -216,11 +216,15 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) { } if v.TYPE == "let" { + name := v.toset.(accessVariable).Name + if v.function { + resp = Callable{name, v.params, v.value, v.code, stack, v.line} + } 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}) + _, err := builtinCall(stackcallable, []any{name, resp}) if err.EXISTS { return nil, err }