diff --git a/src/built-ins.go b/src/built-ins.go index 2f08917..6016c14 100644 --- a/src/built-ins.go +++ b/src/built-ins.go @@ -20,13 +20,15 @@ func makeGlobal(allowDocument bool) ArObject { if x.TYPE == "array" { newmap := anymap{} for i, v := range x.obj["__value__"].([]any) { + v := ArValidToAny(v) switch y := v.(type) { case []any: if len(y) == 2 { 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] + key := ArValidToAny(y[0]) + newmap[key] = y[1] continue } } @@ -165,5 +167,22 @@ 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) { + 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" { + return x, ArErr{} + } + newclass := ArObject{TYPE: "class", obj: anymap{}} + for key, val := range x.obj { + newclass.obj[key] = val + } + return newclass, ArErr{} + } + return nil, ArErr{TYPE: "TypeError", message: "Cannot create class from '" + typeof(a[0]) + "'", EXISTS: true} + }} return vars } diff --git a/src/getIndex.go b/src/getIndex.go index 6cd912b..6d82ded 100644 --- a/src/getIndex.go +++ b/src/getIndex.go @@ -152,7 +152,7 @@ func indexGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapG return ArMapGet{tival, args, true, code.line, code.realcode, code.path}, true, ArErr{}, 1 } return ArMapGet{}, false, ArErr{ - "SyntaxError", + "Syntax Error", "invalid index get", code.line, code.path, diff --git a/src/run.go b/src/run.go index 0d509a2..e49a2d7 100644 --- a/src/run.go +++ b/src/run.go @@ -189,15 +189,11 @@ func runVal(line any, stack stack, stacklevel int) (any, ArErr) { break } return runImport(x, stack, stacklevel+1) - case ABS: - if stackoverflow { - linenum = x.line - path = x.path - code = x.code - break - } - return runAbs(x, stack, stacklevel+1) - case bool, ArObject, number, nil: + case bool: + return x, ArErr{} + case nil: + return x, ArErr{} + case ArObject: return x, ArErr{} } if stackoverflow { diff --git a/src/subprocess.go b/src/subprocess.go index c8573ff..ad1215a 100644 --- a/src/subprocess.go +++ b/src/subprocess.go @@ -14,13 +14,14 @@ func ArSubprocess(args ...any) (any, ArErr) { EXISTS: true, } } else if typeof(args[0]) != "array" { - fmt.Println(args[0]) return nil, ArErr{ TYPE: "TypeError", message: fmt.Sprintf("subprocess() argument must be an array, not %s", typeof(args[0])), EXISTS: true, } - } else if len(args[0].([]any)) == 0 { + } + args[0] = ArValidToAny(args[0]) + if len(args[0].([]any)) == 0 { return nil, ArErr{ TYPE: "RuntimeError", message: "subprocess() argument must be an array of strings, not an empty array", diff --git a/src/translate.go b/src/translate.go index 76864c4..b7aa27d 100644 --- a/src/translate.go +++ b/src/translate.go @@ -42,6 +42,8 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine i return parseForLoop(code, index, codelines) } else if isGenericImport(code) { return parseGenericImport(code, index, codelines) + } else if isTryCatch(code) { + return parseTryCatch(code, index, codelines) } } if isLine >= 1 { @@ -77,12 +79,6 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine i return resp, worked, err, i } } - if isAutoAsignVariable(code) { - resp, worked, err, i = parseAutoAsignVariable(code, index, codelines, isLine) - if worked { - return resp, worked, err, i - } - } if isNumber(code) { return parseNumber(code) } else if isString(code) { @@ -101,6 +97,12 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine i return resp, worked, err, i } } + if isAutoAsignVariable(code) { + resp, worked, err, i = parseAutoAsignVariable(code, index, codelines, isLine) + if worked { + return resp, worked, err, i + } + } { operation, worked, err, step := parseOperations(code, index, codelines) if worked { diff --git a/src/trycatch.go b/src/trycatch.go new file mode 100644 index 0000000..0866139 --- /dev/null +++ b/src/trycatch.go @@ -0,0 +1,73 @@ +package main + +import ( + "strings" +) + +var tryCompiled = makeRegex(`\s*try\s(.|\n)+`) +var catchCompiled = makeRegex(`\s*catch\s*\(\s*` + spacelessVariable + `\s*\)\s*(.|\n)+`) + +type TryCatch struct { + Try any + Catch any + errorName string + line int + path string + code string +} + +func isTryCatch(code UNPARSEcode) bool { + return tryCompiled.MatchString(code.code) +} + +func parseTryCatch(code UNPARSEcode, index int, codelines []UNPARSEcode) (TryCatch, bool, ArErr, int) { + trytrimmed := strings.TrimSpace(code.code) + totalIndex := 0 + tryparsed, worked, err, i := translateVal(UNPARSEcode{trytrimmed[4:], code.realcode, code.line, code.path}, index, codelines, 1) + if !worked { + return TryCatch{}, false, err, i + } + totalIndex += i + catchtrimmed := strings.TrimSpace(codelines[index+totalIndex].code) + if !catchCompiled.MatchString(catchtrimmed) { + return TryCatch{}, false, ArErr{"Syntax Error", "invalid syntax", code.line, code.path, code.realcode, true}, i + } + catchtrimmed = catchtrimmed[6:] + catchbracketSplit := strings.SplitN(catchtrimmed, ")", 2) + errorName := strings.TrimSpace(strings.TrimSpace(catchbracketSplit[0])[1:]) + errcode := catchbracketSplit[1] + + catchparsed, worked, err, i := translateVal(UNPARSEcode{errcode, code.realcode, code.line, code.path}, index+totalIndex, codelines, 1) + if !worked { + return TryCatch{}, false, err, i + } + totalIndex += i + + return TryCatch{ + tryparsed, + catchparsed, + errorName, + code.line, + code.path, + code.realcode, + }, true, ArErr{}, totalIndex +} + +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{ + "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) + if err.EXISTS { + return nil, err + } + } + return val, ArErr{} +} diff --git a/src/variable.go b/src/variable.go index b689e18..37b8bfd 100644 --- a/src/variable.go +++ b/src/variable.go @@ -35,6 +35,8 @@ var blockedVariableNames = map[string]bool{ "not": true, "and": true, "or": true, + "try": true, + "catch": true, } type accessVariable struct { @@ -85,7 +87,7 @@ func readVariable(v accessVariable, stack stack) (any, ArErr) { return val, ArErr{} } } - return nil, ArErr{"Runtime Error", "variable \"" + v.name + "\" does not exist", v.line, v.path, v.code, true} + return nil, ArErr{"Name Error", "variable \"" + v.name + "\" does not exist", v.line, v.path, v.code, true} } func isSetVariable(code UNPARSEcode) bool { @@ -212,7 +214,7 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) { _, ok := stack[len(stack)-1].obj[v.toset.(accessVariable).name] varMutex.RUnlock() if ok { - return nil, ArErr{"Runtime Error", "variable \"" + v.toset.(accessVariable).name + "\" already exists", v.line, v.path, v.code, true} + return nil, ArErr{"Name Error", "variable \"" + v.toset.(accessVariable).name + "\" already exists", v.line, v.path, v.code, true} } varMutex.Lock() stack[len(stack)-1].obj[v.toset.(accessVariable).name] = resp @@ -307,7 +309,7 @@ func runDelete(d ArDelete, stack stack, stacklevel int) (any, ArErr) { return nil, ArErr{} } } - return nil, ArErr{"Runtime Error", "variable \"" + x.name + "\" does not exist", d.line, d.path, d.code, true} + return nil, ArErr{"Name Error", "variable \"" + x.name + "\" does not exist", d.line, d.path, d.code, true} case ArMapGet: respp, err := runVal(x.VAL, stack, stacklevel+1) if err.EXISTS {