diff --git a/cool_module.ar b/cool_module.ar deleted file mode 100644 index b913d03..0000000 --- a/cool_module.ar +++ /dev/null @@ -1,4 +0,0 @@ -cool_function() = do - term.log("this is a cool function") - time.snooze(1) - term.log("wow what a cool function") \ No newline at end of file diff --git a/example.ar b/example.ar index 8099a06..7575f47 100644 --- a/example.ar +++ b/example.ar @@ -1,3 +1,8 @@ -import "cool_module" as cool +i = 0 +output = [] -cool.cool_function() \ No newline at end of file +while (i < 1000) do + output = append(output, i) + i = i + 1 + +file.write('output.json').json(output) \ No newline at end of file diff --git a/src/built-ins.go b/src/built-ins.go index 7dfcf86..a2eadaa 100644 --- a/src/built-ins.go +++ b/src/built-ins.go @@ -42,9 +42,8 @@ func init() { switch y := v.(type) { case ArArray: if len(y) == 2 { - keytype := typeof(y[0]) - if keytype == "array" || keytype == "map" { - return nil, ArErr{TYPE: "TypeError", message: "Cannot use unhashable value as key: " + keytype, EXISTS: true} + if isUnhashable(y[0]) { + return nil, ArErr{TYPE: "TypeError", message: "Cannot use unhashable value as key: " + typeof(y[0]), EXISTS: true} } newmap[y[0]] = y[1] continue diff --git a/src/call.go b/src/call.go index d5d5080..1f8a715 100644 --- a/src/call.go +++ b/src/call.go @@ -106,7 +106,7 @@ func runCall(c call, stack stack, stacklevel int) (any, ArErr) { level[param] = args[i] } resp, err := runVal(x.run, append(x.stack, level), stacklevel+1) - return openReturn(resp), err + return ThrowOnNonLoop(openReturn(resp), err) } return nil, ArErr{"Runtime Error", "type '" + typeof(callable) + "' is not callable", c.line, c.path, c.code, true} } diff --git a/src/file.go b/src/file.go index 2863d9f..8085992 100644 --- a/src/file.go +++ b/src/file.go @@ -2,12 +2,14 @@ package main import ( "bytes" + "fmt" "io" "os" ) var ArFile = ArMap{ - "read": builtinFunc{"read", ArRead}, + "read": builtinFunc{"read", ArRead}, + "write": builtinFunc{"write", ArWrite}, } func readtext(file *os.File) (string, error) { @@ -16,15 +18,15 @@ func readtext(file *os.File) (string, error) { if err != nil { return "", err } - return string(buf.Bytes()), nil + return buf.String(), nil } func ArRead(args ...any) (any, ArErr) { - if len(args) == 0 { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "open takes 1 argument", EXISTS: true} + if len(args) != 1 { + return ArMap{}, ArErr{TYPE: "Runtime Error", message: "read takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} } if typeof(args[0]) != "string" { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "open takes a string not a '" + typeof(args[0]) + "'", EXISTS: true} + return ArMap{}, 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) @@ -44,11 +46,45 @@ func ArRead(args ...any) (any, ArErr) { if err != nil { return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} } - return parse(text), ArErr{} - }}, - "line": builtinFunc{"line", func(...any) (any, ArErr) { - - return "", ArErr{} + return jsonparse(text), ArErr{} }}, }, ArErr{} } + +func ArWrite(args ...any) (any, ArErr) { + if len(args) != 1 { + return ArMap{}, ArErr{TYPE: "Runtime Error", message: "write takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} + } + if typeof(args[0]) != "string" { + return ArMap{}, 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 ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} + } + return ArMap{ + "text": builtinFunc{"text", func(args ...any) (any, ArErr) { + if len(args) != 1 { + return ArMap{}, ArErr{TYPE: "Runtime Error", message: "text takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} + } + if typeof(args[0]) != "string" { + return ArMap{}, 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 ArMap{}, 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 ArMap{}, 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 25300e7..63a12d5 100644 --- a/src/getIndex.go +++ b/src/getIndex.go @@ -12,15 +12,12 @@ 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 - start any - end any - step any - index bool - numberofindex int - line int - code string - path string + VAL any + args ArArray + index bool + line int + code string + path string } func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { @@ -30,7 +27,7 @@ func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { } switch m := resp.(type) { case ArMap: - if r.numberofindex > 1 { + if len(r.args) > 1 { return nil, ArErr{ "IndexError", "index not found", @@ -40,10 +37,20 @@ func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { true, } } - key, err := runVal(r.start, stack, stacklevel+1) + key, err := runVal(r.args[0], stack, stacklevel+1) if err.EXISTS { return nil, err } + if isUnhashable(key) { + return nil, ArErr{ + "TypeError", + "unhashable type: '" + typeof(key) + "'", + r.line, + r.path, + r.code, + true, + } + } if _, ok := m[key]; !ok { return nil, ArErr{ "KeyError", @@ -57,335 +64,12 @@ func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { return m[key], ArErr{} case ArArray: - startindex := 0 - endindex := 1 - step := 1 - slice := false - - if !r.index { - key, err := runVal(r.start, stack, stacklevel+1) - if err.EXISTS { - return nil, err - } - switch key { - case "length": - return newNumber().SetInt64(int64(len(m))), ArErr{} - } - return nil, ArErr{ - "IndexError", - "" + anyToArgon(key, true, true, 3, 0, false, 0) + " does not exist in array", - r.line, - r.path, - r.code, - true, - } - } - if r.start != nil { - sindex, err := runVal(r.start, stack, stacklevel+1) - if err.EXISTS { - return nil, err - } - if typeof(sindex) != "number" { - return nil, ArErr{ - "TypeError", - "index must be a number", - r.line, - r.path, - r.code, - true, - } - } - num := sindex.(number) - if !num.IsInt() { - return nil, ArErr{ - "TypeError", - "index must be an integer", - r.line, - r.path, - r.code, - true, - } - } - startindex = int(num.Num().Int64()) - endindex = startindex + 1 - } - if r.end != nil { - eindex, err := runVal(r.end, stack, stacklevel+1) - if err.EXISTS { - return nil, err - } - if typeof(eindex) != "number" { - return nil, ArErr{ - "TypeError", - "ending index must be a number", - r.line, - r.path, - r.code, - true, - } - } - slice = true - num := eindex.(number) - if !num.IsInt() { - return nil, ArErr{ - "TypeError", - "ending index must be an integer", - r.line, - r.path, - r.code, - true, - } - } - endindex = int(num.Num().Int64()) - } else if r.numberofindex > 1 { - endindex = len(m) - } - if r.step != nil { - step, err := runVal(r.step, stack, stacklevel+1) - if err.EXISTS { - return nil, err - } - if typeof(step) != "number" { - return nil, ArErr{ - "TypeError", - "step must be a number", - r.line, - r.path, - r.code, - true, - } - } - slice = true - num := step.(number) - if !num.IsInt() { - return nil, ArErr{ - "TypeError", - "step must be an integer", - r.line, - r.path, - r.code, - true, - } - } - step = int(num.Num().Int64()) - } - if startindex < 0 { - startindex = len(m) + startindex - } - if endindex < 0 { - endindex = len(m) + endindex - } - if step < 0 { - step = -step - startindex, endindex = endindex, startindex - } - if startindex < 0 || startindex >= len(m) { - return nil, ArErr{ - "IndexError", - "index '" + fmt.Sprint(startindex) + "' out of range", - r.line, - r.path, - r.code, - true, - } - } - if endindex < 0 || endindex > len(m) { - return nil, ArErr{ - "IndexError", - "index '" + fmt.Sprint(endindex) + "' out of range", - r.line, - r.path, - r.code, - true, - } - } - if step == 0 { - return nil, ArErr{ - "ValueError", - "step cannot be 0", - r.line, - r.path, - r.code, - true, - } - } - if !slice { - return m[startindex], ArErr{} - } else if step == 1 { - return m[startindex:endindex], ArErr{} - } - output := ArArray{} - for i := startindex; i < endindex; i += step { - output = append(output, output[i]) - } - return output, ArErr{} + return getFromArArray(m, r, stack, stacklevel) case string: - startindex := 0 - endindex := 1 - step := 1 - slice := false - - if !r.index { - key, err := runVal(r.start, stack, stacklevel+1) - if err.EXISTS { - return nil, err - } - switch key { - case "length": - return newNumber().SetInt64(int64(len(m))), ArErr{} - } - return nil, ArErr{ - "IndexError", - "" + anyToArgon(key, true, true, 3, 0, false, 0) + " does not exist in array", - r.line, - r.path, - r.code, - true, - } - } - if r.start != nil { - sindex, err := runVal(r.start, stack, stacklevel+1) - if err.EXISTS { - return nil, err - } - if typeof(sindex) != "number" { - return nil, ArErr{ - "TypeError", - "index must be a number", - r.line, - r.path, - r.code, - true, - } - } - num := sindex.(number) - if !num.IsInt() { - return nil, ArErr{ - "TypeError", - "index must be an integer", - r.line, - r.path, - r.code, - true, - } - } - startindex = int(num.Num().Int64()) - endindex = startindex + 1 - } - if r.end != nil { - eindex, err := runVal(r.end, stack, stacklevel+1) - if err.EXISTS { - return nil, err - } - if typeof(eindex) != "number" { - return nil, ArErr{ - "TypeError", - "ending index must be a number", - r.line, - r.path, - r.code, - true, - } - } - slice = true - num := eindex.(number) - if !num.IsInt() { - return nil, ArErr{ - "TypeError", - "ending index must be an integer", - r.line, - r.path, - r.code, - true, - } - } - endindex = int(num.Num().Int64()) - } else if r.numberofindex > 1 { - endindex = len(m) - } - if r.step != nil { - step, err := runVal(r.step, stack, stacklevel+1) - if err.EXISTS { - return nil, err - } - if typeof(step) != "number" { - return nil, ArErr{ - "TypeError", - "step must be a number", - r.line, - r.path, - r.code, - true, - } - } - slice = true - num := step.(number) - if !num.IsInt() { - return nil, ArErr{ - "TypeError", - "step must be an integer", - r.line, - r.path, - r.code, - true, - } - } - step = int(num.Num().Int64()) - } - if startindex < 0 { - startindex = len(m) + startindex - } - if endindex < 0 { - endindex = len(m) + endindex - } - if step < 0 { - step = -step - startindex, endindex = endindex, startindex - } - if startindex < 0 || startindex >= len(m) { - return nil, ArErr{ - "IndexError", - "index '" + fmt.Sprint(startindex) + "' out of range", - r.line, - r.path, - r.code, - true, - } - } - if endindex < 0 || endindex > len(m) { - return nil, ArErr{ - "IndexError", - "index '" + fmt.Sprint(endindex) + "' out of range", - r.line, - r.path, - r.code, - true, - } - } - if step == 0 { - return nil, ArErr{ - "ValueError", - "step cannot be 0", - r.line, - r.path, - r.code, - true, - } - } - fmt.Println(startindex, endindex,step) - if !slice { - return string(m[startindex]), ArErr{} - } else if step == 1 { - return string(m[startindex:endindex]), ArErr{} - } - output := []byte{} - for i := startindex; i < endindex; i += step { - output = append(output, output[i]) - } - return string(output), ArErr{} + return getFromString(m, r, stack, stacklevel) } - key, err := runVal(r.start, stack, stacklevel+1) + key, err := runVal(r.args[0], stack, stacklevel+1) if err.EXISTS { return nil, err } @@ -421,8 +105,7 @@ func mapGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapGet if !worked { return ArMapGet{}, false, err, i } - k := key - return ArMapGet{resp, k, nil, nil, false, 1, code.line, code.realcode, code.path}, true, ArErr{}, 1 + return ArMapGet{resp, ArArray{key}, false, code.line, code.realcode, code.path}, true, ArErr{}, 1 } func isIndexGet(code UNPARSEcode) bool { @@ -433,11 +116,6 @@ func indexGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapG trim := strings.TrimSpace(code.code) trim = trim[:len(trim)-1] split := strings.Split(trim, "[") - var toindex any - var start any - var end any - var step any - numberofindexs := 0 for i := 1; i < len(split); i++ { ti := strings.Join(split[:i], "[") innerbrackets := strings.Join(split[i:], "[") @@ -448,7 +126,7 @@ func indexGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapG } continue } - fmt.Println(args) + fmt.Println(args) if len(args) > 3 { return ArMapGet{}, false, ArErr{ "SyntaxError", @@ -461,33 +139,210 @@ func indexGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapG } tival, worked, err, i := translateVal(UNPARSEcode{code: ti, realcode: code.realcode, line: code.line, path: code.path}, index, codelines, 0) if !worked { - fmt.Println(err) if i == len(split)-1 { return ArMapGet{}, false, err, i } continue } - numberofindexs = len(args) - if len(args) >= 1 { - toindex = tival - start = args[0] - } - if len(args) >= 2 { - end = args[1] - } - if len(args) >= 3 { - step = args[2] - } + return ArMapGet{tival, args, true, code.line, code.realcode, code.path}, true, ArErr{}, 1 + } + return ArMapGet{}, false, ArErr{ + "SyntaxError", + "invalid index get", + code.line, + code.path, + code.realcode, + true, + }, 1 +} + +func isUnhashable(val any) bool { + keytype := typeof(val) + return keytype == "array" || keytype == "map" +} + +func getFromArArray(m []any, r ArMapGet, stack stack, stacklevel int) (ArArray, ArErr) { + var ( + start int = 0 + end any = nil + step int = 1 + ) + { + startval, err := runVal(r.args[0], stack, stacklevel+1) + if err.EXISTS { + return nil, err + } + if startval == nil { + start = 0 + } else if typeof(startval) != "number" && !startval.(number).IsInt() { + return nil, 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 nil, err + } + if endval == nil { + end = len(m) + } else if typeof(endval) != "number" && !endval.(number).IsInt() { + return nil, 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 nil, err + } + if stepval == nil { + step = 1 + } else if typeof(stepval) != "number" && !stepval.(number).IsInt() { + return nil, 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) + } + + fmt.Println(start, end, step) + if end == nil { + return ArArray{m[start]}, ArErr{} + } else if step == 1 { + return m[start:end.(int)], ArErr{} + } else { + output := ArArray{} + 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 (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) + } + + fmt.Println(start, end, step) + 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{} } - if toindex == nil { - return ArMapGet{}, false, ArErr{ - "SyntaxError", - "invalid index get", - code.line, - code.path, - code.realcode, - true, - }, 1 - } - return ArMapGet{toindex, start, end, step, true, numberofindexs, code.line, code.realcode, code.path}, true, ArErr{}, 1 } diff --git a/src/jsonread.go b/src/json.go similarity index 78% rename from src/jsonread.go rename to src/json.go index 2a28aea..6c85664 100644 --- a/src/jsonread.go +++ b/src/json.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "errors" + "math" "strconv" "strings" ) @@ -33,19 +34,22 @@ func convertToArgon(obj any) any { return nil } -func parse(str string) any { +func jsonparse(str string) any { var jsonMap any json.Unmarshal([]byte(str), &jsonMap) return convertToArgon(jsonMap) } -func stringify(obj any) (string, error) { +func jsonstringify(obj any, level int) (string, error) { + if level > 100 { + return "", errors.New("json stringify error: too many levels") + } output := []string{} obj = classVal(obj) switch x := obj.(type) { case ArMap: for key, value := range x { - str, err := stringify(value) + str, err := jsonstringify(value, level+1) if err != nil { return "", err } @@ -53,20 +57,22 @@ func stringify(obj any) (string, error) { } return "{" + strings.Join(output, ", ") + "}", nil case ArArray: - output = append(output, "[") for _, value := range x { - str, err := stringify(value) + str, err := jsonstringify(value, level+1) if err != nil { return "", err } output = append(output, str) } - output = append(output, "]") - return strings.Join(output, ", "), nil + return "[" + strings.Join(output, ", ") + "]", nil case string: return strconv.Quote(x), nil case number: - return anyToArgon(x, true, false, 1, 0, false, 0), nil + num, _ := x.Float64() + if math.IsNaN(num) || math.IsInf(num, 0) { + return "null", nil + } + return numberToString(x, false), nil case bool: return strconv.FormatBool(x), nil case nil: @@ -84,13 +90,13 @@ var ArJSON = ArMap{ if typeof(args[0]) != "string" { return ArMap{}, ArErr{TYPE: "Runtime Error", message: "parse takes a string not a '" + typeof(args[0]) + "'", EXISTS: true} } - return parse(args[0].(string)), ArErr{} + return jsonparse(args[0].(string)), ArErr{} }}, "stringify": builtinFunc{"stringify", func(args ...any) (any, ArErr) { if len(args) == 0 { return ArMap{}, ArErr{TYPE: "Runtime Error", message: "stringify takes 1 argument", EXISTS: true} } - str, err := stringify(args[0]) + str, err := jsonstringify(args[0], 0) if err != nil { return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} } diff --git a/src/jumpStatements.go b/src/jumpStatements.go index 7671bb0..0d3cc25 100644 --- a/src/jumpStatements.go +++ b/src/jumpStatements.go @@ -6,6 +6,7 @@ import ( var returnCompile = makeRegex(`( *)return( +)(.|\n)+`) var breakCompile = makeRegex(`( *)break( *)`) +var continueCompile = makeRegex(`( *)continue( *)`) type CallReturn struct { value any @@ -20,13 +21,12 @@ type Return struct { code string path string } - -type CallBreak struct { +type Break struct { line int code string path string } -type Break struct { +type Continue struct { line int code string path string @@ -40,6 +40,10 @@ func isBreak(code UNPARSEcode) bool { return breakCompile.MatchString(code.code) } +func isContinue(code UNPARSEcode) bool { + return continueCompile.MatchString(code.code) +} + func parseReturn(code UNPARSEcode, index int, codeline []UNPARSEcode) (CallReturn, bool, ArErr, int) { resp, worked, err, i := translateVal(UNPARSEcode{ code: strings.TrimSpace(code.code)[6:], @@ -81,18 +85,29 @@ func openReturn(resp any) any { } } -func parseBreak(code UNPARSEcode, index int, codeline []UNPARSEcode) (CallBreak, bool, ArErr, int) { - return CallBreak{ +func parseBreak(code UNPARSEcode) (Break, bool, ArErr, int) { + return Break{ line: code.line, code: code.realcode, path: code.path, }, true, ArErr{}, 1 } -func runBreak(code CallBreak, stack stack, stacklevel int) (any, ArErr) { - return Break{ +func parseContinue(code UNPARSEcode) (Continue, bool, ArErr, int) { + return Continue{ line: code.line, - code: code.code, + code: code.realcode, path: code.path, - }, ArErr{} + }, true, ArErr{}, 1 +} + +func ThrowOnNonLoop(val any, err ArErr) (any, ArErr) { + switch x := val.(type) { + case Break: + return nil, ArErr{"Break Error", "break can only be used in loops", x.line, x.path, x.code, true} + case Continue: + return nil, ArErr{"Continue Error", "continue can only be used in loops", x.line, x.path, x.code, true} + default: + return x, err + } } diff --git a/src/number.go b/src/number.go index 3845b64..1e2abf3 100644 --- a/src/number.go +++ b/src/number.go @@ -44,6 +44,7 @@ func numberToString(num number, simplify bool) string { return fmt.Sprint(num, "π") } } + x, _ := num.Float64() return fmt.Sprint(x) diff --git a/src/run.go b/src/run.go index 6180e3a..502abcf 100644 --- a/src/run.go +++ b/src/run.go @@ -7,32 +7,64 @@ import ( // returns (number|string|nil), error func runVal(line any, stack stack, stacklevel int) (any, ArErr) { - if stacklevel >= 10000 { - return nil, ArErr{ - TYPE: "RuntimeError", - message: "stack overflow", - line: 0, - path: "", - code: "", - EXISTS: true, - } - } + var ( + linenum = 0 + path = "" + code = "" + stackoverflow = stacklevel >= 10000 + ) switch x := line.(type) { case number: return x, ArErr{} case string: return x, ArErr{} case call: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runCall(x, stack, stacklevel+1) case factorial: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runFactorial(x, stack, stacklevel+1) case accessVariable: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return readVariable(x, stack) case ArMapGet: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return mapGet(x, stack, stacklevel+1) case setVariable: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return setVariableValue(x, stack, stacklevel+1) case negative: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } resp, err := runVal(x.VAL, stack, stacklevel+1) resp = classVal(resp) if err.EXISTS { @@ -48,34 +80,124 @@ func runVal(line any, stack stack, stacklevel int) (any, ArErr) { EXISTS: true, } case brackets: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runVal(x.VAL, stack, stacklevel+1) case operationType: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runOperation(x, stack, stacklevel+1) case dowrap: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runDoWrap(x, stack, stacklevel+1) case CallReturn: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runReturn(x, stack, stacklevel+1) - case CallBreak: - return runBreak(x, stack, stacklevel+1) + case Break: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } + return x, ArErr{} + case Continue: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } + return x, ArErr{} case ArDelete: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runDelete(x, stack, stacklevel+1) case not: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runNot(x, stack, stacklevel+1) case ifstatement: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runIfStatement(x, stack, stacklevel+1) case whileLoop: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runWhileLoop(x, stack, stacklevel+1) case CreateArray: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runArray(x, stack, stacklevel+1) case squareroot: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runSquareroot(x, stack, stacklevel+1) case ArImport: + if stackoverflow { + linenum = x.line + path = x.path + code = x.code + break + } return runImport(x, stack, stacklevel+1) case bool: return x, ArErr{} case nil: return nil, ArErr{} } + if stackoverflow { + return nil, ArErr{ + TYPE: "RuntimeError", + message: "stack overflow", + line: linenum, + path: path, + code: code, + EXISTS: true, + } + } fmt.Println("unreachable", reflect.TypeOf(line)) panic("unreachable") } diff --git a/src/shell.go b/src/shell.go index 0c03acb..024dfb0 100644 --- a/src/shell.go +++ b/src/shell.go @@ -6,12 +6,6 @@ import ( "os/signal" ) -var endingWithDoCompiled = makeRegex(`.*do( )*`) - -func isEndingWithDo(str string) bool { - return endingWithDoCompiled.MatchString(str) -} - func shell() { global := stack{vars, scope{}} c := make(chan os.Signal, 1) diff --git a/src/translate.go b/src/translate.go index 03469ba..0bba0f3 100644 --- a/src/translate.go +++ b/src/translate.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "strings" ) @@ -30,7 +29,9 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine i } else if isReturn(code) { return parseReturn(code, index, codelines) } else if isBreak(code) { - return parseBreak(code, index, codelines) + return parseBreak(code) + } else if isContinue(code) { + return parseContinue(code) } else if isIfStatement(code) { return parseIfStatement(code, index, codelines) } else if isWhileLoop(code) { @@ -98,7 +99,6 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine i return parseVariable(code) } else if isArray(code) { resp, worked, err, i = parseArray(code, index, codelines) - fmt.Println(resp, worked, err, i) if worked { return resp, worked, err, i } diff --git a/src/variable.go b/src/variable.go index 3c2d47f..73235ed 100644 --- a/src/variable.go +++ b/src/variable.go @@ -227,7 +227,7 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) { varMutex.Lock() stack[i][x.name] = resp varMutex.Unlock() - return resp, ArErr{} + return ThrowOnNonLoop(resp, ArErr{}) } } varMutex.Lock() @@ -238,15 +238,17 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) { if err.EXISTS { return nil, err } - key, err := runVal(x.start, stack, stacklevel+1) + if len(x.args) != 1 { + return nil, ArErr{"Runtime Error", "cannot set by slice", v.line, v.path, v.code, true} + } + key, err := runVal(x.args[0], stack, stacklevel+1) if err.EXISTS { return nil, err } switch y := respp.(type) { case ArMap: - keytype := typeof(key) - if keytype == "array" || keytype == "map" { - return nil, ArErr{TYPE: "TypeError", message: "Cannot use unhashable value as key: " + keytype, EXISTS: true} + 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[key] = resp @@ -256,7 +258,7 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) { } } } - return resp, ArErr{} + return ThrowOnNonLoop(resp, ArErr{}) } func parseDelete(code UNPARSEcode, index int, lines []UNPARSEcode) (ArDelete, bool, ArErr, int) { @@ -294,7 +296,10 @@ func runDelete(d ArDelete, stack stack, stacklevel int) (any, ArErr) { if err.EXISTS { return nil, err } - key, err := runVal(x.start, stack, stacklevel+1) + if len(x.args) != 1 { + return nil, ArErr{"Runtime Error", "can't delete by slice", d.line, d.path, d.code, true} + } + key, err := runVal(x.args[0], stack, stacklevel+1) if err.EXISTS { return nil, err } diff --git a/src/whileloop.go b/src/whileloop.go index e8b6ec5..981c210 100644 --- a/src/whileloop.go +++ b/src/whileloop.go @@ -125,6 +125,8 @@ func runWhileLoop(loop whileLoop, stack stack, stacklevel int) (any, ArErr) { return x, ArErr{} case Break: return nil, ArErr{} + case Continue: + continue } } return nil, ArErr{} diff --git a/test.json b/test.json deleted file mode 100644 index ef57753..0000000 --- a/test.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "id": 1, - "name": "John", - "age": 20, - "city": "New York" - }, - { - "id": 2, - "name": "Peter", - "age": 21, - "city": "London" - }, - { - "id": 3, - "name": "Sally", - "age": 22, - "city": "Paris" - }, - { - "id": 4, - "name": "Jane", - "age": 23, - "city": "Tokyo" - } -] \ No newline at end of file