fix infinity in json stringify

This commit is contained in:
2023-03-15 16:48:47 +00:00
parent ebc3df801b
commit b53f2b4a0a
15 changed files with 471 additions and 461 deletions

View File

@@ -1,4 +0,0 @@
cool_function() = do
term.log("this is a cool function")
time.snooze(1)
term.log("wow what a cool function")

View File

@@ -1,3 +1,8 @@
import "cool_module" as cool i = 0
output = []
cool.cool_function() while (i < 1000) do
output = append(output, i)
i = i + 1
file.write('output.json').json(output)

View File

@@ -42,9 +42,8 @@ func init() {
switch y := v.(type) { switch y := v.(type) {
case ArArray: case ArArray:
if len(y) == 2 { if len(y) == 2 {
keytype := typeof(y[0]) if isUnhashable(y[0]) {
if keytype == "array" || keytype == "map" { return nil, ArErr{TYPE: "TypeError", message: "Cannot use unhashable value as key: " + typeof(y[0]), EXISTS: true}
return nil, ArErr{TYPE: "TypeError", message: "Cannot use unhashable value as key: " + keytype, EXISTS: true}
} }
newmap[y[0]] = y[1] newmap[y[0]] = y[1]
continue continue

View File

@@ -106,7 +106,7 @@ func runCall(c call, stack stack, stacklevel int) (any, ArErr) {
level[param] = args[i] level[param] = args[i]
} }
resp, err := runVal(x.run, append(x.stack, level), stacklevel+1) 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} return nil, ArErr{"Runtime Error", "type '" + typeof(callable) + "' is not callable", c.line, c.path, c.code, true}
} }

View File

@@ -2,12 +2,14 @@ package main
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"os" "os"
) )
var ArFile = ArMap{ var ArFile = ArMap{
"read": builtinFunc{"read", ArRead}, "read": builtinFunc{"read", ArRead},
"write": builtinFunc{"write", ArWrite},
} }
func readtext(file *os.File) (string, error) { func readtext(file *os.File) (string, error) {
@@ -16,15 +18,15 @@ func readtext(file *os.File) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return string(buf.Bytes()), nil return buf.String(), nil
} }
func ArRead(args ...any) (any, ArErr) { func ArRead(args ...any) (any, ArErr) {
if len(args) == 0 { if len(args) != 1 {
return ArMap{}, ArErr{TYPE: "Runtime Error", message: "open takes 1 argument", EXISTS: true} return ArMap{}, ArErr{TYPE: "Runtime Error", message: "read takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true}
} }
if typeof(args[0]) != "string" { 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) filename := args[0].(string)
file, err := os.Open(filename) file, err := os.Open(filename)
@@ -44,11 +46,45 @@ func ArRead(args ...any) (any, ArErr) {
if err != nil { if err != nil {
return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true}
} }
return parse(text), ArErr{} return jsonparse(text), ArErr{}
}},
"line": builtinFunc{"line", func(...any) (any, ArErr) {
return "", ArErr{}
}}, }},
}, 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{}
}

View File

@@ -12,15 +12,12 @@ var mapGetCompile = makeRegex(`(.|\n)+\.([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|(
var indexGetCompile = makeRegex(`(.|\n)+\[(.|\n)+\]( *)`) var indexGetCompile = makeRegex(`(.|\n)+\[(.|\n)+\]( *)`)
type ArMapGet struct { type ArMapGet struct {
VAL any VAL any
start any args ArArray
end any index bool
step any line int
index bool code string
numberofindex int path string
line int
code string
path string
} }
func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { 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) { switch m := resp.(type) {
case ArMap: case ArMap:
if r.numberofindex > 1 { if len(r.args) > 1 {
return nil, ArErr{ return nil, ArErr{
"IndexError", "IndexError",
"index not found", "index not found",
@@ -40,10 +37,20 @@ func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) {
true, true,
} }
} }
key, err := runVal(r.start, stack, stacklevel+1) key, err := runVal(r.args[0], stack, stacklevel+1)
if err.EXISTS { if err.EXISTS {
return nil, err 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 { if _, ok := m[key]; !ok {
return nil, ArErr{ return nil, ArErr{
"KeyError", "KeyError",
@@ -57,335 +64,12 @@ func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) {
return m[key], ArErr{} return m[key], ArErr{}
case ArArray: case ArArray:
startindex := 0 return getFromArArray(m, r, stack, stacklevel)
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{}
case string: case string:
startindex := 0 return getFromString(m, r, stack, stacklevel)
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{}
} }
key, err := runVal(r.start, stack, stacklevel+1) key, err := runVal(r.args[0], stack, stacklevel+1)
if err.EXISTS { if err.EXISTS {
return nil, err return nil, err
} }
@@ -421,8 +105,7 @@ func mapGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapGet
if !worked { if !worked {
return ArMapGet{}, false, err, i return ArMapGet{}, false, err, i
} }
k := key return ArMapGet{resp, ArArray{key}, false, code.line, code.realcode, code.path}, true, ArErr{}, 1
return ArMapGet{resp, k, nil, nil, false, 1, code.line, code.realcode, code.path}, true, ArErr{}, 1
} }
func isIndexGet(code UNPARSEcode) bool { func isIndexGet(code UNPARSEcode) bool {
@@ -433,11 +116,6 @@ func indexGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapG
trim := strings.TrimSpace(code.code) trim := strings.TrimSpace(code.code)
trim = trim[:len(trim)-1] trim = trim[:len(trim)-1]
split := strings.Split(trim, "[") 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++ { for i := 1; i < len(split); i++ {
ti := strings.Join(split[:i], "[") ti := strings.Join(split[:i], "[")
innerbrackets := strings.Join(split[i:], "[") innerbrackets := strings.Join(split[i:], "[")
@@ -448,7 +126,7 @@ func indexGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapG
} }
continue continue
} }
fmt.Println(args) fmt.Println(args)
if len(args) > 3 { if len(args) > 3 {
return ArMapGet{}, false, ArErr{ return ArMapGet{}, false, ArErr{
"SyntaxError", "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) tival, worked, err, i := translateVal(UNPARSEcode{code: ti, realcode: code.realcode, line: code.line, path: code.path}, index, codelines, 0)
if !worked { if !worked {
fmt.Println(err)
if i == len(split)-1 { if i == len(split)-1 {
return ArMapGet{}, false, err, i return ArMapGet{}, false, err, i
} }
continue continue
} }
numberofindexs = len(args) return ArMapGet{tival, args, true, code.line, code.realcode, code.path}, true, ArErr{}, 1
if len(args) >= 1 { }
toindex = tival return ArMapGet{}, false, ArErr{
start = args[0] "SyntaxError",
} "invalid index get",
if len(args) >= 2 { code.line,
end = args[1] code.path,
} code.realcode,
if len(args) >= 3 { true,
step = args[2] }, 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
} }

View File

@@ -3,6 +3,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"math"
"strconv" "strconv"
"strings" "strings"
) )
@@ -33,19 +34,22 @@ func convertToArgon(obj any) any {
return nil return nil
} }
func parse(str string) any { func jsonparse(str string) any {
var jsonMap any var jsonMap any
json.Unmarshal([]byte(str), &jsonMap) json.Unmarshal([]byte(str), &jsonMap)
return convertToArgon(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{} output := []string{}
obj = classVal(obj) obj = classVal(obj)
switch x := obj.(type) { switch x := obj.(type) {
case ArMap: case ArMap:
for key, value := range x { for key, value := range x {
str, err := stringify(value) str, err := jsonstringify(value, level+1)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -53,20 +57,22 @@ func stringify(obj any) (string, error) {
} }
return "{" + strings.Join(output, ", ") + "}", nil return "{" + strings.Join(output, ", ") + "}", nil
case ArArray: case ArArray:
output = append(output, "[")
for _, value := range x { for _, value := range x {
str, err := stringify(value) str, err := jsonstringify(value, level+1)
if err != nil { if err != nil {
return "", err return "", err
} }
output = append(output, str) output = append(output, str)
} }
output = append(output, "]") return "[" + strings.Join(output, ", ") + "]", nil
return strings.Join(output, ", "), nil
case string: case string:
return strconv.Quote(x), nil return strconv.Quote(x), nil
case number: 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: case bool:
return strconv.FormatBool(x), nil return strconv.FormatBool(x), nil
case nil: case nil:
@@ -84,13 +90,13 @@ var ArJSON = ArMap{
if typeof(args[0]) != "string" { if typeof(args[0]) != "string" {
return ArMap{}, ArErr{TYPE: "Runtime Error", message: "parse takes a string not a '" + typeof(args[0]) + "'", EXISTS: true} 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) { "stringify": builtinFunc{"stringify", func(args ...any) (any, ArErr) {
if len(args) == 0 { if len(args) == 0 {
return ArMap{}, ArErr{TYPE: "Runtime Error", message: "stringify takes 1 argument", EXISTS: true} 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 { if err != nil {
return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true}
} }

View File

@@ -6,6 +6,7 @@ import (
var returnCompile = makeRegex(`( *)return( +)(.|\n)+`) var returnCompile = makeRegex(`( *)return( +)(.|\n)+`)
var breakCompile = makeRegex(`( *)break( *)`) var breakCompile = makeRegex(`( *)break( *)`)
var continueCompile = makeRegex(`( *)continue( *)`)
type CallReturn struct { type CallReturn struct {
value any value any
@@ -20,13 +21,12 @@ type Return struct {
code string code string
path string path string
} }
type Break struct {
type CallBreak struct {
line int line int
code string code string
path string path string
} }
type Break struct { type Continue struct {
line int line int
code string code string
path string path string
@@ -40,6 +40,10 @@ func isBreak(code UNPARSEcode) bool {
return breakCompile.MatchString(code.code) 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) { func parseReturn(code UNPARSEcode, index int, codeline []UNPARSEcode) (CallReturn, bool, ArErr, int) {
resp, worked, err, i := translateVal(UNPARSEcode{ resp, worked, err, i := translateVal(UNPARSEcode{
code: strings.TrimSpace(code.code)[6:], 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) { func parseBreak(code UNPARSEcode) (Break, bool, ArErr, int) {
return CallBreak{ return Break{
line: code.line, line: code.line,
code: code.realcode, code: code.realcode,
path: code.path, path: code.path,
}, true, ArErr{}, 1 }, true, ArErr{}, 1
} }
func runBreak(code CallBreak, stack stack, stacklevel int) (any, ArErr) { func parseContinue(code UNPARSEcode) (Continue, bool, ArErr, int) {
return Break{ return Continue{
line: code.line, line: code.line,
code: code.code, code: code.realcode,
path: code.path, 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
}
} }

View File

@@ -44,6 +44,7 @@ func numberToString(num number, simplify bool) string {
return fmt.Sprint(num, "π") return fmt.Sprint(num, "π")
} }
} }
x, _ := num.Float64() x, _ := num.Float64()
return fmt.Sprint(x) return fmt.Sprint(x)

View File

@@ -7,32 +7,64 @@ import (
// returns (number|string|nil), error // returns (number|string|nil), error
func runVal(line any, stack stack, stacklevel int) (any, ArErr) { func runVal(line any, stack stack, stacklevel int) (any, ArErr) {
if stacklevel >= 10000 { var (
return nil, ArErr{ linenum = 0
TYPE: "RuntimeError", path = ""
message: "stack overflow", code = ""
line: 0, stackoverflow = stacklevel >= 10000
path: "", )
code: "",
EXISTS: true,
}
}
switch x := line.(type) { switch x := line.(type) {
case number: case number:
return x, ArErr{} return x, ArErr{}
case string: case string:
return x, ArErr{} return x, ArErr{}
case call: case call:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runCall(x, stack, stacklevel+1) return runCall(x, stack, stacklevel+1)
case factorial: case factorial:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runFactorial(x, stack, stacklevel+1) return runFactorial(x, stack, stacklevel+1)
case accessVariable: case accessVariable:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return readVariable(x, stack) return readVariable(x, stack)
case ArMapGet: case ArMapGet:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return mapGet(x, stack, stacklevel+1) return mapGet(x, stack, stacklevel+1)
case setVariable: case setVariable:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return setVariableValue(x, stack, stacklevel+1) return setVariableValue(x, stack, stacklevel+1)
case negative: case negative:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
resp, err := runVal(x.VAL, stack, stacklevel+1) resp, err := runVal(x.VAL, stack, stacklevel+1)
resp = classVal(resp) resp = classVal(resp)
if err.EXISTS { if err.EXISTS {
@@ -48,34 +80,124 @@ func runVal(line any, stack stack, stacklevel int) (any, ArErr) {
EXISTS: true, EXISTS: true,
} }
case brackets: case brackets:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runVal(x.VAL, stack, stacklevel+1) return runVal(x.VAL, stack, stacklevel+1)
case operationType: case operationType:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runOperation(x, stack, stacklevel+1) return runOperation(x, stack, stacklevel+1)
case dowrap: case dowrap:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runDoWrap(x, stack, stacklevel+1) return runDoWrap(x, stack, stacklevel+1)
case CallReturn: case CallReturn:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runReturn(x, stack, stacklevel+1) return runReturn(x, stack, stacklevel+1)
case CallBreak: case Break:
return runBreak(x, stack, stacklevel+1) 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: case ArDelete:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runDelete(x, stack, stacklevel+1) return runDelete(x, stack, stacklevel+1)
case not: case not:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runNot(x, stack, stacklevel+1) return runNot(x, stack, stacklevel+1)
case ifstatement: case ifstatement:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runIfStatement(x, stack, stacklevel+1) return runIfStatement(x, stack, stacklevel+1)
case whileLoop: case whileLoop:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runWhileLoop(x, stack, stacklevel+1) return runWhileLoop(x, stack, stacklevel+1)
case CreateArray: case CreateArray:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runArray(x, stack, stacklevel+1) return runArray(x, stack, stacklevel+1)
case squareroot: case squareroot:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runSquareroot(x, stack, stacklevel+1) return runSquareroot(x, stack, stacklevel+1)
case ArImport: case ArImport:
if stackoverflow {
linenum = x.line
path = x.path
code = x.code
break
}
return runImport(x, stack, stacklevel+1) return runImport(x, stack, stacklevel+1)
case bool: case bool:
return x, ArErr{} return x, ArErr{}
case nil: case nil:
return nil, ArErr{} 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)) fmt.Println("unreachable", reflect.TypeOf(line))
panic("unreachable") panic("unreachable")
} }

View File

@@ -6,12 +6,6 @@ import (
"os/signal" "os/signal"
) )
var endingWithDoCompiled = makeRegex(`.*do( )*`)
func isEndingWithDo(str string) bool {
return endingWithDoCompiled.MatchString(str)
}
func shell() { func shell() {
global := stack{vars, scope{}} global := stack{vars, scope{}}
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)

View File

@@ -1,7 +1,6 @@
package main package main
import ( import (
"fmt"
"strings" "strings"
) )
@@ -30,7 +29,9 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine i
} else if isReturn(code) { } else if isReturn(code) {
return parseReturn(code, index, codelines) return parseReturn(code, index, codelines)
} else if isBreak(code) { } else if isBreak(code) {
return parseBreak(code, index, codelines) return parseBreak(code)
} else if isContinue(code) {
return parseContinue(code)
} else if isIfStatement(code) { } else if isIfStatement(code) {
return parseIfStatement(code, index, codelines) return parseIfStatement(code, index, codelines)
} else if isWhileLoop(code) { } else if isWhileLoop(code) {
@@ -98,7 +99,6 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine i
return parseVariable(code) return parseVariable(code)
} else if isArray(code) { } else if isArray(code) {
resp, worked, err, i = parseArray(code, index, codelines) resp, worked, err, i = parseArray(code, index, codelines)
fmt.Println(resp, worked, err, i)
if worked { if worked {
return resp, worked, err, i return resp, worked, err, i
} }

View File

@@ -227,7 +227,7 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) {
varMutex.Lock() varMutex.Lock()
stack[i][x.name] = resp stack[i][x.name] = resp
varMutex.Unlock() varMutex.Unlock()
return resp, ArErr{} return ThrowOnNonLoop(resp, ArErr{})
} }
} }
varMutex.Lock() varMutex.Lock()
@@ -238,15 +238,17 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) {
if err.EXISTS { if err.EXISTS {
return nil, err 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 { if err.EXISTS {
return nil, err return nil, err
} }
switch y := respp.(type) { switch y := respp.(type) {
case ArMap: case ArMap:
keytype := typeof(key) if isUnhashable(key) {
if keytype == "array" || keytype == "map" { return nil, ArErr{"Runtime Error", "can't use unhashable type as map key: " + typeof(key), v.line, v.path, v.code, true}
return nil, ArErr{TYPE: "TypeError", message: "Cannot use unhashable value as key: " + keytype, EXISTS: true}
} }
varMutex.Lock() varMutex.Lock()
y[key] = resp 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) { 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 { if err.EXISTS {
return nil, err 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 { if err.EXISTS {
return nil, err return nil, err
} }

View File

@@ -125,6 +125,8 @@ func runWhileLoop(loop whileLoop, stack stack, stacklevel int) (any, ArErr) {
return x, ArErr{} return x, ArErr{}
case Break: case Break:
return nil, ArErr{} return nil, ArErr{}
case Continue:
continue
} }
} }
return nil, ArErr{} return nil, ArErr{}

View File

@@ -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"
}
]