diff --git a/example.ar b/example.ar index 97fb867..4dfad77 100644 --- a/example.ar +++ b/example.ar @@ -1,8 +1,19 @@ -let password = do - let password = passwordInput("set password: ") - while (password.length < 8) do - term.log("password must be longer then 8 characters!") - password = passwordInput("set password: ") - return password +let getInitials(name) = do + name = name.upper() + namesplit = name.split(" ") + i = 0 + while (i < namesplit.length) do + namesplit[i] = namesplit[i][0] + i = i + 1 + return namesplit.join("") -term.log("your password is", password) \ No newline at end of file +name = "william bell" +term.log(getInitials(name)) +term.log(name) + +let addLastName(name) = do + name.append(" Bell") + +name = "William" +addLastName(name) +term.log(name) \ No newline at end of file diff --git a/modules/csv/init.ar b/modules/csv/init.ar new file mode 100644 index 0000000..91b3dd4 --- /dev/null +++ b/modules/csv/init.ar @@ -0,0 +1,34 @@ +let read(path) = do + file = open(path, "r") + text = file.text() + data = [] + splitlines = text.split("\n") + i = 0 + while (i < splitlines.length) do + values = [] + in_quotes = false + start = 0 + line = splitlines[i] + j = 0 + while (j < line.length) 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] + value = value.replace("\"\"", "\"") + values.append(value) + start = j + 1 + else if (char == "\"") do + 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] + value = value.replace("\"\"", "\"") + values.append(value) + j = j + 1 + data.append(values) + i = i + 1 + + return data \ No newline at end of file diff --git a/src/array.go b/src/array.go index 2c82f25..b65d374 100644 --- a/src/array.go +++ b/src/array.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "strings" ) @@ -25,6 +26,122 @@ func ArArray(arr []any) ArObject { "length": newNumber().SetUint64(uint64(len(arr))), }, } + val.obj["__setindex__"] = builtinFunc{ + "__setindex__", + func(a ...any) (any, ArErr) { + if len(a) != 2 { + return nil, ArErr{ + TYPE: "TypeError", + message: "expected 2 arguments, got " + fmt.Sprint(len(a)), + EXISTS: true, + } + } + if typeof(a[0]) != "number" { + return nil, ArErr{ + TYPE: "TypeError", + message: "index must be a number", + EXISTS: true, + } + } + if !a[0].(number).IsInt() { + return nil, ArErr{ + TYPE: "TypeError", + message: "index must be an integer", + EXISTS: true, + } + } + num := int(a[0].(number).Num().Int64()) + if num < 0 || num >= len(arr) { + return nil, ArErr{ + TYPE: "IndexError", + message: "index out of range", + EXISTS: true, + } + } + arr[num] = a[1] + return nil, ArErr{} + }, + } + val.obj["__getindex__"] = builtinFunc{ + "__getindex__", + func(a ...any) (any, ArErr) { + // a[0] is start + // a[1] is end + // a[2] is step + if len(a) < 0 || len(a) > 3 { + return nil, ArErr{"TypeError", "expected 1 to 3 arguments, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + var ( + start int = 0 + end any = nil + step int = 1 + ) + { + if a[0] == nil { + start = 0 + } else if typeof(a[0]) != "number" || !a[0].(number).IsInt() { + return "", ArErr{ + TYPE: "TypeError", + message: "slice index must be an integer", + EXISTS: true, + } + } else { + start = int(a[0].(number).Num().Int64()) + } + } + if len(a) > 1 { + if a[1] == nil { + end = len(arr) + } else if typeof(a[1]) != "number" || !a[1].(number).IsInt() { + return "", ArErr{ + TYPE: "TypeError", + message: "slice index must be an integer", + EXISTS: true, + } + } else { + end = int(a[1].(number).Num().Int64()) + } + } + if len(a) > 2 { + if a[2] == nil { + step = 1 + } else if typeof(a[2]) != "number" || !a[2].(number).IsInt() { + return "", ArErr{ + TYPE: "TypeError", + message: "slice index must be an integer", + EXISTS: true, + } + } else { + step = int(a[2].(number).Num().Int64()) + } + } + if start < 0 { + start = len(arr) + start + } + if _, ok := end.(int); ok && end.(int) < 0 { + end = len(arr) + end.(int) + } + if end != nil && end.(int) > len(arr) { + end = len(arr) + } + if end == nil { + return arr[start], ArErr{} + } else if step == 1 { + return arr[start:end.(int)], ArErr{} + } else { + output := []any{} + if step > 0 { + for i := start; i < end.(int); i += step { + output = append(output, arr[i]) + } + } else { + for i := end.(int) - 1; i >= start; i += step { + output = append(output, arr[i]) + } + } + return output, ArErr{} + } + }} val.obj["remove"] = builtinFunc{ "remove", func(args ...any) (any, ArErr) { diff --git a/src/built-ins.go b/src/built-ins.go index 11f975b..029968c 100644 --- a/src/built-ins.go +++ b/src/built-ins.go @@ -1,5 +1,7 @@ package main +import "fmt" + var vars = Map(anymap{}) func init() { @@ -133,4 +135,20 @@ func init() { vars.obj["json"] = ArJSON vars.obj["sin"] = ArSin vars.obj["arcsin"] = ArArcsin + vars.obj["dir"] = builtinFunc{"dir", func(a ...any) (any, ArErr) { + fmt.Println(a) + if len(a) == 0 { + return ArArray([]any{}), ArErr{} + } + t := AnyToArValid(a[0]) + switch x := t.(type) { + case ArObject: + newarray := []any{} + for key := range x.obj { + newarray = append(newarray, key) + } + return ArArray(newarray), ArErr{} + } + return ArArray([]any{}), ArErr{} + }} } diff --git a/src/file.go b/src/file.go index 7174124..a983f80 100644 --- a/src/file.go +++ b/src/file.go @@ -97,71 +97,3 @@ func readtext(file *os.File) (string, error) { } return buf.String(), nil } - -func ArRead(args ...any) (any, ArErr) { - if len(args) != 1 { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: "read takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} - } - if typeof(args[0]) != "string" { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: "read takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} - } - filename := args[0].(string) - file, err := os.Open(filename) - if err != nil { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} - } - return Map(anymap{ - "text": builtinFunc{"text", func(...any) (any, ArErr) { - text, err := readtext(file) - if err != nil { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} - } - return ArString(text), ArErr{} - }}, - "json": builtinFunc{"json", func(...any) (any, ArErr) { - text, err := readtext(file) - if err != nil { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} - } - return jsonparse(text), ArErr{} - }}, - }), ArErr{} -} - -func ArWrite(args ...any) (any, ArErr) { - if len(args) != 1 { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: "write takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} - } - if typeof(args[0]) != "string" { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: "write takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} - } - filename := args[0].(string) - file, err := os.Create(filename) - if err != nil { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} - } - return Map(anymap{ - "text": builtinFunc{"text", func(args ...any) (any, ArErr) { - if len(args) != 1 { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: "text takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} - } - if typeof(args[0]) != "string" { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: "text takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} - } - file.Write([]byte(args[0].(string))) - return nil, ArErr{} - }}, - "json": builtinFunc{"json", func(args ...any) (any, ArErr) { - if len(args) != 1 { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: "json takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} - } - jsonstr, err := jsonstringify(args[0], 0) - if err != nil { - return ArObject{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} - } - file.Write([]byte(jsonstr)) - return nil, ArErr{} - }}, - }), ArErr{} - -} diff --git a/src/getIndex.go b/src/getIndex.go index 06dbbb4..b564685 100644 --- a/src/getIndex.go +++ b/src/getIndex.go @@ -31,16 +31,19 @@ func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { } switch m := resp.(type) { case ArObject: - switch m.TYPE { - case "array": - resp, err := getFromArArray(m, r, stack, stacklevel+1) - if !err.EXISTS { - return resp, err - } - case "string": - resp, err := getFromString(m.obj["__value__"].(string), r, stack, stacklevel+1) - if !err.EXISTS { - return ArString(resp), err + 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 len(r.args) > 1 { @@ -253,94 +256,3 @@ func getFromArArray(m ArObject, r ArMapGet, stack stack, stacklevel int) (any, A return ArArray(output), ArErr{} } } - -func getFromString(m string, r ArMapGet, stack stack, stacklevel int) (string, ArErr) { - var ( - start int = 0 - end any = nil - step int = 1 - ) - { - startval, err := runVal(r.args[0], stack, stacklevel+1) - if err.EXISTS { - return "", err - } - if startval == nil { - start = 0 - } else if typeof(startval) != "number" || !startval.(number).IsInt() { - return "", ArErr{ - "TypeError", - "slice index must be an integer", - r.line, - r.path, - r.code, - true, - } - } else { - start = int(startval.(number).Num().Int64()) - } - } - if len(r.args) > 1 { - endval, err := runVal(r.args[1], stack, stacklevel+1) - if err.EXISTS { - return "", err - } - if endval == nil { - end = len(m) - } else if typeof(endval) != "number" && !endval.(number).IsInt() { - return "", ArErr{ - "TypeError", - "slice ending index must be an integer", - r.line, - r.path, - r.code, - true, - } - } else { - end = int(endval.(number).Num().Int64()) - } - } - if len(r.args) > 2 { - stepval, err := runVal(r.args[2], stack, stacklevel+1) - if err.EXISTS { - return "", err - } - if stepval == nil { - step = 1 - } else if typeof(stepval) != "number" && !stepval.(number).IsInt() { - return "", ArErr{ - "TypeError", - "slice step must be an integer", - r.line, - r.path, - r.code, - true, - } - } else { - step = int(stepval.(number).Num().Int64()) - } - } - if start < 0 { - start = len(m) + start - } - if _, ok := end.(int); ok && end.(int) < 0 { - end = len(m) + end.(int) - } - if end == nil { - return string(m[start]), ArErr{} - } else if step == 1 { - return m[start:end.(int)], ArErr{} - } else { - output := []byte{} - if step > 0 { - for i := start; i < end.(int); i += step { - output = append(output, m[i]) - } - } else { - for i := end.(int) - 1; i >= start; i += step { - output = append(output, m[i]) - } - } - return string(output), ArErr{} - } -} diff --git a/src/parseImport.go b/src/parseImport.go index 0b6f26a..f54fa48 100644 --- a/src/parseImport.go +++ b/src/parseImport.go @@ -45,6 +45,7 @@ func parseGenericImport(code UNPARSEcode, index int, codeline []UNPARSEcode) (Ar func runImport(importOBJ ArImport, stack stack, stacklevel int) (any, ArErr) { val, err := runVal(importOBJ.filePath, stack, stacklevel+1) + val = ArValidToAny(val) if err.EXISTS { return nil, err } diff --git a/src/string.go b/src/string.go index 794792d..f1644fd 100644 --- a/src/string.go +++ b/src/string.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "strconv" "strings" ) @@ -50,6 +51,118 @@ func ArString(str string) ArObject { "length": newNumber().SetUint64(uint64(len(str))), }, } + + obj.obj["__setindex__"] = builtinFunc{ + "__setindex__", + func(a ...any) (any, ArErr) { + if len(a) != 2 { + return nil, ArErr{"TypeError", "expected 2 arguments, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + if typeof(a[0]) != "number" { + return nil, ArErr{"TypeError", "expected number, got " + typeof(a[0]), 0, "", "", true} + } + if typeof(a[1]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[1]), 0, "", "", true} + } + if len(a[1].(string)) != 1 { + return nil, ArErr{"TypeError", "expected string of length 1, got " + fmt.Sprint(len(a[1].(string))), 0, "", "", true} + } + if !a[0].(number).IsInt() { + return nil, ArErr{"TypeError", "expected integer, got float", 0, "", "", true} + } + index := a[0].(number).Num().Int64() + if index < 0 { + index = int64(len(str)) + index + } + if index < 0 || index >= int64(len(str)) { + return nil, ArErr{"IndexError", "index out of range", 0, "", "", true} + } + str = strings.Join([]string{str[:index], a[1].(string), str[index+1:]}, "") + obj.obj["__value__"] = str + obj.obj["length"] = newNumber().SetUint64(uint64(len(str))) + return nil, ArErr{} + }} + obj.obj["__getindex__"] = builtinFunc{ + "__getindex__", + func(a ...any) (any, ArErr) { + // a[0] is start + // a[1] is end + // a[2] is step + if len(a) < 0 || len(a) > 3 { + return nil, ArErr{"TypeError", "expected 1 to 3 arguments, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + var ( + start int = 0 + end any = nil + step int = 1 + ) + { + if a[0] == nil { + start = 0 + } else if typeof(a[0]) != "number" || !a[0].(number).IsInt() { + return "", ArErr{ + TYPE: "TypeError", + message: "slice index must be an integer", + EXISTS: true, + } + } else { + start = int(a[0].(number).Num().Int64()) + } + } + if len(a) > 1 { + if a[1] == nil { + end = len(str) + } else if typeof(a[1]) != "number" || !a[1].(number).IsInt() { + return "", ArErr{ + TYPE: "TypeError", + message: "slice index must be an integer", + EXISTS: true, + } + } else { + end = int(a[1].(number).Num().Int64()) + } + } + if len(a) > 2 { + if a[2] == nil { + step = 1 + } else if typeof(a[2]) != "number" || !a[2].(number).IsInt() { + return "", ArErr{ + TYPE: "TypeError", + message: "slice index must be an integer", + EXISTS: true, + } + } else { + step = int(a[2].(number).Num().Int64()) + } + } + if start < 0 { + start = len(str) + start + } + if _, ok := end.(int); ok && end.(int) < 0 { + end = len(str) + end.(int) + } + + if end != nil && end.(int) > len(str) { + end = len(str) + } + if end == nil { + return string(str[start]), ArErr{} + } else if step == 1 { + return str[start:end.(int)], ArErr{} + } else { + output := []byte{} + if step > 0 { + for i := start; i < end.(int); i += step { + output = append(output, str[i]) + } + } else { + for i := end.(int) - 1; i >= start; i += step { + output = append(output, str[i]) + } + } + return string(output), ArErr{} + } + }} obj.obj["append"] = builtinFunc{ "append", func(a ...any) (any, ArErr) { @@ -68,5 +181,230 @@ func ArString(str string) ArObject { obj.obj["length"] = newNumber().SetUint64(uint64(len(str))) return nil, ArErr{} }} + obj.obj["extend"] = builtinFunc{ + "extend", + func(a ...any) (any, ArErr) { + if len(a) != 1 { + return nil, ArErr{"TypeError", "expected 1 argument, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + output := []string{str} + for _, v := range a { + if typeof(v) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(v), 0, "", "", true} + } + output = append(output, v.(string)) + } + str = strings.Join(output, "") + obj.obj["__value__"] = str + obj.obj["length"] = newNumber().SetUint64(uint64(len(str))) + return nil, ArErr{} + }, + } + obj.obj["insert"] = builtinFunc{ + "insert", + func(a ...any) (any, ArErr) { + if len(a) != 2 { + return nil, ArErr{"TypeError", "expected 2 argument, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + if typeof(a[0]) != "number" || !a[0].(number).IsInt() { + return nil, ArErr{"TypeError", "expected integer, got " + typeof(a[0]), 0, "", "", true} + } + if typeof(a[1]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[1]), 0, "", "", true} + } + index := int(a[0].(number).Num().Int64()) + if index < 0 { + index = len(str) + index + } + if index > len(str) { + index = len(str) + } + str = str[:index] + a[1].(string) + str[index:] + obj.obj["__value__"] = str + obj.obj["length"] = newNumber().SetUint64(uint64(len(str))) + return nil, ArErr{} + }} + obj.obj["concat"] = builtinFunc{ + "concat", + func(a ...any) (any, ArErr) { + if len(a) == 0 { + return nil, ArErr{"TypeError", "expected 1 or more argument, got 0", 0, "", "", true} + } + output := []string{str} + for _, v := range a { + if typeof(v) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(v), 0, "", "", true} + } + output = append(output, v.(string)) + } + return strings.Join(output, ""), ArErr{} + }} + obj.obj["split"] = builtinFunc{ + "split", + func(a ...any) (any, ArErr) { + if len(a) != 1 { + return nil, ArErr{"TypeError", "expected 1 or more argument, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + if typeof(a[0]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[0]), 0, "", "", true} + } + splitby := a[0].(string) + output := []any{} + splitted := any(strings.Split(str, splitby)) + for _, v := range splitted.([]string) { + output = append(output, ArString(v)) + } + return output, ArErr{} + }} + obj.obj["capitalise"] = builtinFunc{ + "capitalise", + func(a ...any) (any, ArErr) { + if len(a) != 0 { + return nil, ArErr{"TypeError", "expected 0 arguments, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + return strings.Title(str), ArErr{} + }} + obj.obj["lower"] = builtinFunc{ + "lower", + func(a ...any) (any, ArErr) { + if len(a) != 0 { + return nil, ArErr{"TypeError", "expected 0 arguments, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + return strings.ToLower(str), ArErr{} + }} + obj.obj["upper"] = builtinFunc{ + "upper", + func(a ...any) (any, ArErr) { + if len(a) != 0 { + return nil, ArErr{"TypeError", "expected 0 arguments, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + return strings.ToUpper(str), ArErr{} + }} + obj.obj["replace"] = builtinFunc{ + "replace", + func(a ...any) (any, ArErr) { + if len(a) != 2 { + return nil, ArErr{"TypeError", "expected 2 arguments, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + if typeof(a[0]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[0]), 0, "", "", true} + } + if typeof(a[1]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[1]), 0, "", "", true} + } + return strings.Replace(str, a[0].(string), a[1].(string), -1), ArErr{} + }} + obj.obj["contains"] = builtinFunc{ + "contains", + func(a ...any) (any, ArErr) { + if len(a) != 1 { + return nil, ArErr{"TypeError", "expected 1 argument, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + if typeof(a[0]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[0]), 0, "", "", true} + } + return strings.Contains(str, a[0].(string)), ArErr{} + }} + obj.obj["startswith"] = builtinFunc{ + "startswith", + func(a ...any) (any, ArErr) { + if len(a) != 1 { + return nil, ArErr{"TypeError", "expected 1 argument, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + if typeof(a[0]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[0]), 0, "", "", true} + } + return strings.HasPrefix(str, a[0].(string)), ArErr{} + }} + obj.obj["endswith"] = builtinFunc{ + "endswith", + func(a ...any) (any, ArErr) { + if len(a) != 1 { + return nil, ArErr{"TypeError", "expected 1 argument, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + if typeof(a[0]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[0]), 0, "", "", true} + } + return strings.HasSuffix(str, a[0].(string)), ArErr{} + }} + obj.obj["index"] = builtinFunc{ + "index", + func(a ...any) (any, ArErr) { + if len(a) != 1 { + return nil, ArErr{"TypeError", "expected 1 argument, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + if typeof(a[0]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[0]), 0, "", "", true} + } + return strings.Index(str, a[0].(string)), ArErr{} + }} + obj.obj["rindex"] = builtinFunc{ + "rindex", + func(a ...any) (any, ArErr) { + if len(a) != 1 { + return nil, ArErr{"TypeError", "expected 1 argument, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + if typeof(a[0]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[0]), 0, "", "", true} + } + return strings.LastIndex(str, a[0].(string)), ArErr{} + }} + obj.obj["count"] = builtinFunc{ + "count", + func(a ...any) (any, ArErr) { + if len(a) != 1 { + + return nil, ArErr{"TypeError", "expected 1 argument, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + if typeof(a[0]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[0]), 0, "", "", true} + } + return strings.Count(str, a[0].(string)), ArErr{} + }} + obj.obj["strip"] = builtinFunc{ + "strip", + func(a ...any) (any, ArErr) { + if len(a) > 1 { + return nil, ArErr{"TypeError", "expected 0 or 1 arguments, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + cutset := " " + if len(a) == 1 { + if typeof(a[0]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[0]), 0, "", "", true} + } + cutset = a[0].(string) + } + return strings.Trim(str, cutset), ArErr{} + }} + obj.obj["leftstrip"] = builtinFunc{ + "leftstrip", + func(a ...any) (any, ArErr) { + if len(a) > 1 { + return nil, ArErr{"TypeError", "expected 0 or 1 arguments, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + cutset := " " + if len(a) == 1 { + if typeof(a[0]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[0]), 0, "", "", true} + } + cutset = a[0].(string) + } + return strings.TrimLeft(str, cutset), ArErr{} + }} + obj.obj["rightstrip"] = builtinFunc{ + "rightstrip", + func(a ...any) (any, ArErr) { + if len(a) > 1 { + return nil, ArErr{"TypeError", "expected 0 or 1 arguments, got " + fmt.Sprint(len(a)), 0, "", "", true} + } + cutset := " " + if len(a) == 1 { + if typeof(a[0]) != "string" { + return nil, ArErr{"TypeError", "expected string, got " + typeof(a[0]), 0, "", "", true} + } + cutset = a[0].(string) + } + return strings.TrimRight(str, cutset), ArErr{} + }} return obj } diff --git a/src/variable.go b/src/variable.go index 5b1112d..b689e18 100644 --- a/src/variable.go +++ b/src/variable.go @@ -249,12 +249,27 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) { } switch y := respp.(type) { case ArObject: - 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} + 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) + 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() } - 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} }