diff --git a/src/anyToBool.go b/src/anyToBool.go new file mode 100644 index 0000000..7492d7c --- /dev/null +++ b/src/anyToBool.go @@ -0,0 +1,24 @@ +package main + +func anyToBool(x any) bool { + switch x := x.(type) { + case string: + return x != "" + case number: + return x.Cmp(newNumber()) != 0 + case bool: + return x + case nil: + return false + case ArMap: + return len(x) != 0 + case builtinFunc: + return true + case Callable: + return true + case ArClass: + return true + default: + return true + } +} diff --git a/src/brackets.go b/src/brackets.go index a406787..2ab9d6c 100644 --- a/src/brackets.go +++ b/src/brackets.go @@ -18,8 +18,9 @@ func isBrackets(code UNPARSEcode) bool { } func parseBrackets(code UNPARSEcode, index int, codeline []UNPARSEcode) (brackets, bool, ArErr, int) { + trimmed := strings.TrimSpace(code.code) resp, worked, err, i := translateVal(UNPARSEcode{ - code: strings.TrimSpace(code.code)[1 : len(code.code)-2], + code: trimmed[1 : len(trimmed)-1], realcode: code.realcode, line: code.line, path: code.path, diff --git a/src/built-in-functions.go b/src/built-in-functions.go index 190b293..61edcf5 100644 --- a/src/built-in-functions.go +++ b/src/built-in-functions.go @@ -1,8 +1,10 @@ package main import ( + "bufio" "fmt" "math/big" + "os" ) type builtinFunc struct { @@ -10,34 +12,6 @@ type builtinFunc struct { FUNC func(...any) (any, ArErr) } -func ArgonLog(args ...any) (any, ArErr) { - output := []any{} - for i := 0; i < len(args); i++ { - output = append(output, anyToArgon(args[i], false)) - } - fmt.Println(output...) - return nil, ArErr{} -} - -func ArgonAdd(args ...any) (any, ArErr) { - return reduce(func(x any, y any) any { - return newNumber().Add(x.(number), y.(number)) - }, args), ArErr{} -} -func ArgonDiv(args ...any) (any, ArErr) { - if len(args) == 0 { - return nil, ArErr{TYPE: "Division Error", message: "Cannot divide nothing", EXISTS: true} - } - output := args[0].(number) - for i := 1; i < len(args); i++ { - if args[i].(number).Cmp(newNumber()) == 0 { - return nil, ArErr{TYPE: "Division Error", message: "Cannot divide by zero", EXISTS: true} - } - output = newNumber().Quo(output, args[i].(number)) - } - return output, ArErr{} -} - func ArgonMult(args ...any) (any, ArErr) { return reduce(func(x any, y any) any { return newNumber().Mul(y.(number), x.(number)) @@ -45,12 +19,14 @@ func ArgonMult(args ...any) (any, ArErr) { } func ArgonInput(args ...any) (any, ArErr) { - // allow a message to be passed in as an argument - if len(args) > 0 { - fmt.Print(anyToArgon(args[0], false)) + output := []any{} + for i := 0; i < len(args); i++ { + output = append(output, anyToArgon(args[i], false, true, 3, 0)) } - var input string - fmt.Scanln(&input) + fmt.Print(output...) + scanner := bufio.NewScanner(os.Stdin) + scanner.Scan() + input := scanner.Text() return input, ArErr{} } @@ -61,7 +37,7 @@ func ArgonNumber(args ...any) (any, ArErr) { switch x := args[0].(type) { case string: if !numberCompile.MatchString(x) { - return nil, ArErr{TYPE: "Number Error", message: "Cannot convert type '" + typeof(x) + "' to a number", EXISTS: true} + return nil, ArErr{TYPE: "Number Error", message: "Cannot convert type '" + x + "' to a number", EXISTS: true} } N, _ := newNumber().SetString(x) return N, ArErr{} @@ -84,6 +60,11 @@ func ArgonSqrt(a ...any) (any, ArErr) { return nil, ArErr{TYPE: "sqrt", message: "sqrt takes 1 argument", EXISTS: true} } + if typeof(a[0]) != "number" { + return nil, ArErr{TYPE: "Runtime Error", message: "sqrt takes a number not a '" + typeof(a[0]) + "'", + EXISTS: true} + } + r := a[0].(number) if r.Sign() < 0 { diff --git a/src/built-ins.go b/src/built-ins.go index cb106c1..83f9928 100644 --- a/src/built-ins.go +++ b/src/built-ins.go @@ -1,11 +1,10 @@ package main -var vars = map[string]any{} +var vars = scope{} func init() { - vars["log"] = builtinFunc{"log", ArgonLog} - vars["add"] = builtinFunc{"add", ArgonAdd} - vars["div"] = builtinFunc{"div", ArgonDiv} + vars["window"] = vars + vars["term"] = ArTerm vars["true"] = true vars["false"] = false vars["null"] = nil @@ -22,11 +21,9 @@ func init() { return nil, ArErr{TYPE: "TypeError", message: "Cannot get length of " + typeof(a[0]), EXISTS: true} }} vars["time"] = ArTime - pi, _ := newNumber().SetString("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989") - vars["PI"] = pi - vars["π"] = pi - e, _ := newNumber().SetString("2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416928368190255151086574637721112523897844250569536967707854499699679468644549059879316368892300987931277361782154249992295763514822082698951936680331825288693984964651058209392398294887933203625094431173012381970684161403970198376793206832823764648042953118023287825098194558153017567173613320698112509961818815930416903515988885193458072738667385894228792284998920868058257492796104841984443634632449684875602336248270419786232090021609902353043699418491463140934317381436405462531520961836908887070167683964243781405927145635490613031072085103837505101157477041718986106873969655212671546889570350354") + vars["PI"] = PI + vars["π"] = PI vars["e"] = e - vars["sqrt"] = builtinFunc{"sqrt", ArgonSqrt} - vars["√"] = builtinFunc{"sqrt", ArgonSqrt} + sqrt := builtinFunc{"sqrt", ArgonSqrt} + vars["sqrt"] = sqrt } diff --git a/src/call.go b/src/call.go index 6930f37..c92b343 100644 --- a/src/call.go +++ b/src/call.go @@ -61,7 +61,7 @@ func runCall(c call, stack stack) (any, ArErr) { return nil, err } args := []any{} - level := append(stack, map[string]any{}) + level := append(stack, scope{}) for _, arg := range c.args { resp, err := runVal(arg, level) if err.EXISTS { @@ -80,7 +80,7 @@ func runCall(c call, stack stack) (any, ArErr) { if len(x.params) != len(args) { return nil, ArErr{"Runtime Error", "expected " + fmt.Sprint(len(x.params)) + " arguments, got " + fmt.Sprint(len(args)), c.line, c.path, c.code, true} } - level := map[string]any{} + level := scope{} for i, param := range x.params { level[param] = args[i] } diff --git a/src/import.go b/src/import.go index 6e1da72..217dc34 100644 --- a/src/import.go +++ b/src/import.go @@ -98,7 +98,7 @@ func importMod(realpath string, origin string, main bool) ArErr { if translationerr.EXISTS { return translationerr } - global := map[string]any{} + global := scope{} _, runimeErr := run(translated, stack{vars, global}) if runimeErr.EXISTS { return runimeErr diff --git a/src/main.go b/src/main.go index 9e87116..2a6d6cd 100644 --- a/src/main.go +++ b/src/main.go @@ -7,7 +7,8 @@ import ( // args without the program path var Args = os.Args[1:] -type stack = []map[string]any +type scope = ArMap +type stack = []scope func main() { ex, e := os.Getwd() diff --git a/src/map.go b/src/map.go index befd415..eac4840 100644 --- a/src/map.go +++ b/src/map.go @@ -12,7 +12,7 @@ type ArClass struct { MAP ArMap } -var mapGetCompile = makeRegex("(.|\n)+\\.([a-zA-Z_])([a-zA-Z0-9_])*( *)") +var mapGetCompile = makeRegex(`(.|\n)+\.([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|(\p{L}\p{M}*))*( *)`) type ArMapGet struct { VAL any @@ -60,7 +60,7 @@ func mapGet(r ArMapGet, stack stack) (any, ArErr) { } return nil, ArErr{ "TypeError", - "cannot read " + anyToArgon(key, true) + " from type '" + typeof(resp) + "'", + "cannot read " + anyToArgon(key, true, true, 3, 0) + " from type '" + typeof(resp) + "'", r.line, r.path, r.code, diff --git a/src/maths constants.go b/src/maths constants.go new file mode 100644 index 0000000..61385a3 --- /dev/null +++ b/src/maths constants.go @@ -0,0 +1,4 @@ +package main + +var PI, _ = newNumber().SetString("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989") +var e, _ = newNumber().SetString("2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416928368190255151086574637721112523897844250569536967707854499699679468644549059879316368892300987931277361782154249992295763514822082698951936680331825288693984964651058209392398294887933203625094431173012381970684161403970198376793206832823764648042953118023287825098194558153017567173613320698112509961818815930416903515988885193458072738667385894228792284998920868058257492796104841984443634632449684875602336248270419786232090021609902353043699418491463140934317381436405462531520961836908887070167683964243781405927145635490613031072085103837505101157477041718986106873969655212671546889570350354") diff --git a/src/number.go b/src/number.go index 55f6e2c..90b9dff 100644 --- a/src/number.go +++ b/src/number.go @@ -35,7 +35,7 @@ func isAnyNumber(x any) bool { } // converts a number type to a string -func numberToString(num number, fraction int) string { +func numberToString(num number, fraction int, simplify bool) string { if fraction != 0 { str := num.RatString() if fraction == 1 { @@ -58,7 +58,19 @@ func numberToString(num number, fraction int) string { } return strings.Join(super, "") + "/" + strings.Join(sub, "") } + if simplify { + divPI, _ := newNumber().Quo(num, PI).Float64() + floated := float64(int(divPI * 100)) + if divPI == 1 { + return "π" + } else if divPI == 0 { + return "0" + } else if (divPI*100) == floated && floated != 0 { + return fmt.Sprint(divPI) + "π" + } + } x, _ := num.Float64() + return fmt.Sprint(x) } diff --git a/src/operations.go b/src/operations.go index 3c2e687..445eed5 100644 --- a/src/operations.go +++ b/src/operations.go @@ -2,24 +2,49 @@ package main import ( "fmt" + "reflect" + "strings" ) var operations = [][]string{ - {"-"}, - {"+"}, - {"/"}, - {"*"}, - {"%"}, - {"**", "^"}, - {"=="}, - {"!=", "≠"}, - {"<=", "≤"}, - {">=", "≥"}, - {"<"}, - {">"}, - {"&&", " and "}, - {"||", " or "}, -} + { + "&&", + " and ", + }, { + "||", + " or ", + }, { + " not in ", + }, { + " in ", + }, { + "<=", + }, { + ">=", + }, { + "<", + }, { + ">", + }, { + "!=", + }, { + "==", + }, { + "+", + }, { + "-", + }, { + "*", + }, { + "%", + }, { + "//", + }, { + "/", + }, { + "^", + "**", + }} type operationType struct { operation int @@ -78,6 +103,99 @@ func parseOperations(code UNPARSEcode, index int, codelines []UNPARSEcode) (oper return operationType{}, false, ArErr{}, index } +func compareValues(o operationType, stack stack) (bool, ArErr) { + if len(o.values) != 2 { + return false, ArErr{ + "Runtime Error", + "Invalid number of values for comparison", + o.line, + o.path, + o.code, + true, + } + } + resp, err := runVal( + o.values[0], + stack, + ) + resp = classVal(resp) + if err.EXISTS { + return false, err + } + + resp2, err := runVal( + o.values[1], + stack, + ) + resp2 = classVal(resp2) + if err.EXISTS { + return false, err + } + switch o.operation { + case 4: + if isAnyNumber(resp) && isAnyNumber(resp2) { + return resp.(number).Cmp(resp2.(number)) <= 0, ArErr{} + } + return false, ArErr{ + "Runtime Error", + "Cannot compare type '" + typeof(resp) + "' with type '" + typeof(resp2) + "' with opperation '<='", + o.line, + o.path, + o.code, + true, + } + case 5: + if isAnyNumber(resp) && isAnyNumber(resp2) { + return resp.(number).Cmp(resp2.(number)) >= 0, ArErr{} + } + return false, ArErr{ + "Runtime Error", + "Cannot compare type '" + typeof(resp) + "' with type '" + typeof(resp2) + "' with opperation '>='", + o.line, + o.path, + o.code, + true, + } + case 6: + if isAnyNumber(resp) && isAnyNumber(resp2) { + return resp.(number).Cmp(resp2.(number)) < 0, ArErr{} + } + return false, ArErr{ + "Runtime Error", + "Cannot compare type '" + typeof(resp) + "' with type '" + typeof(resp2) + "' with opperation '<'", + o.line, + o.path, + o.code, + true, + } + case 7: + if isAnyNumber(resp) && isAnyNumber(resp2) { + return resp.(number).Cmp(resp2.(number)) > 0, ArErr{} + } + return false, ArErr{ + "Runtime Error", + "Cannot compare type '" + typeof(resp) + "' with type '" + typeof(resp2) + "' with opperation '>'", + o.line, + o.path, + o.code, + true, + } + case 8: + return !equals(resp, resp2), ArErr{} + case 9: + return equals(resp, resp2), ArErr{} + default: + return false, ArErr{ + "Runtime Error", + "Invalid comparison operation", + o.line, + o.path, + o.code, + true, + } + } +} + func calcNegative(o operationType, stack stack) (number, ArErr) { resp, err := runVal( @@ -108,7 +226,7 @@ func calcNegative(o operationType, stack stack) (number, ArErr) { if err.EXISTS { return nil, err } - if isAnyNumber(resp) { + if typeof(resp) == "number" { output = output.Sub(output, resp.(number)) } else { return nil, ArErr{ @@ -124,14 +242,176 @@ func calcNegative(o operationType, stack stack) (number, ArErr) { return output, ArErr{} } +func calcAdd(o operationType, stack stack) (any, ArErr) { + + resp, err := runVal( + o.values[0], + stack, + ) + resp = classVal(resp) + if err.EXISTS { + return nil, err + } + var output any = resp + if typeof(output) != "number" { + output = anyToArgon(resp, false, true, 3, 0) + } + for i := 1; i < len(o.values); i++ { + resp, err := runVal( + o.values[i], + stack, + ) + resp = classVal(resp) + if err.EXISTS { + return nil, err + } + if typeof(output) == "number" && typeof(resp) == "string" { + output = anyToArgon(output, false, true, 3, 0) + } + if typeof(output) == "number" { + output = newNumber().Add(output.(number), resp.(number)) + } else { + output = output.(string) + anyToArgon(resp, false, true, 3, 0) + } + + } + return output, ArErr{} +} + +func calcAnd(o operationType, stack stack) (any, ArErr) { + var output any = false + for i := 0; i < len(o.values); i++ { + resp, err := runVal( + o.values[i], + stack, + ) + resp = classVal(resp) + if err.EXISTS { + return nil, err + } + if !anyToBool(resp) { + return resp, ArErr{} + } + output = resp + } + return output, ArErr{} +} + +func calcOr(o operationType, stack stack) (any, ArErr) { + var output any = false + for i := 0; i < len(o.values); i++ { + resp, err := runVal( + o.values[i], + stack, + ) + resp = classVal(resp) + if err.EXISTS { + return nil, err + } + if anyToBool(resp) { + return resp, ArErr{} + } + output = resp + } + return output, ArErr{} +} + +func stringInSlice(a any, list []any) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} + +func calcIn(o operationType, stack stack) (bool, ArErr) { + if len(o.values) != 2 { + return false, ArErr{ + "Runtime Error", + "Invalid number of arguments for 'not in'", + o.line, + o.path, + o.code, + true, + } + } + resp, err := runVal( + o.values[0], + stack, + ) + resp = classVal(resp) + if err.EXISTS { + return false, err + } + + resp2, err := runVal( + o.values[1], + stack, + ) + resp2 = classVal(resp2) + if err.EXISTS { + return false, err + } + + switch x := resp2.(type) { + case string: + check := anyToArgon(resp, false, true, 3, 0) + return strings.Contains(x, check), ArErr{} + case []any: + return stringInSlice(resp, x), ArErr{} + case map[any]any: + _, ok := x[resp] + return ok, ArErr{} + default: + return false, ArErr{ + "Runtime Error", + "Cannot check if type '" + typeof(resp) + "' is in type '" + typeof(resp2) + "'", + o.line, + o.path, + o.code, + true, + } + } +} + +func equals(a any, b any) bool { + if typeof(a) == "number" && typeof(b) == "number" { + return a.(number).Cmp(b.(number)) == 0 + } else if typeof(a) == "string" || typeof(b) == "string" { + return anyToArgon(a, false, true, 3, 0) == anyToArgon(b, false, true, 3, 0) + } + return reflect.DeepEqual(a, b) +} + func runOperation(o operationType, stack stack) (any, ArErr) { switch o.operation { case 0: - resp, err := calcNegative(o, stack) - if err.EXISTS { - return resp, err - } - return resp, ArErr{} + return calcAnd(o, stack) + case 1: + return calcOr(o, stack) + case 2: + resp, err := calcIn(o, stack) + resp = !resp + return resp, err + case 3: + return calcIn(o, stack) + case 4: + return compareValues(o, stack) + case 5: + return compareValues(o, stack) + case 6: + return compareValues(o, stack) + case 7: + return compareValues(o, stack) + case 8: + return compareValues(o, stack) + case 9: + return compareValues(o, stack) + case 10: + return calcAdd(o, stack) + case 11: + return calcNegative(o, stack) } panic("Unknown operation: " + fmt.Sprint(o.operation)) diff --git a/src/term-class.go b/src/term-class.go new file mode 100644 index 0000000..b3392a8 --- /dev/null +++ b/src/term-class.go @@ -0,0 +1,16 @@ +package main + +import "fmt" + +func ArgonLog(args ...any) (any, ArErr) { + output := []any{} + for i := 0; i < len(args); i++ { + output = append(output, anyToArgon(args[i], false, true, 3, 0)) + } + fmt.Println(output...) + return nil, ArErr{} +} + +var ArTerm = ArMap{ + "log": builtinFunc{"log", ArgonLog}, +} diff --git a/src/to-argon.go b/src/to-argon.go index b71030b..3996e0a 100644 --- a/src/to-argon.go +++ b/src/to-argon.go @@ -7,7 +7,10 @@ import ( "strings" ) -func anyToArgon(x any, quote bool) string { +func anyToArgon(x any, quote bool, simplify bool, depth int, indent int) string { + if depth == 0 { + return "(...)" + } switch x := x.(type) { case string: if !quote { @@ -23,7 +26,10 @@ func anyToArgon(x any, quote bool) string { } else if math.IsInf(num, -1) { return "-infinity" } else { - return numberToString(x, 0) + if simplify { + return numberToString(x, 0, true) + } + return numberToString(x, 0, false) } case bool: return strconv.FormatBool(x) @@ -39,15 +45,15 @@ func anyToArgon(x any, quote bool) string { } output := []string{} for _, key := range keys { - output = append(output, anyToArgon(key, true)+": "+anyToArgon(x[key], true)) + output = append(output, anyToArgon(key, true, true, depth, indent+1)+": "+anyToArgon(x[key], true, true, depth-1, indent+1)) } - return "{" + strings.Join(output, ", ") + "}" + return "{\n" + (strings.Repeat(" ", indent+1)) + strings.Join(output, ",\n"+(strings.Repeat(" ", indent+1))) + "\n" + (strings.Repeat(" ", indent)) + "}" case builtinFunc: return "" case Callable: return "" case ArClass: - return anyToArgon(x.value, false) + return anyToArgon(x.value, false, true, depth-1, indent+1) default: return fmt.Sprint(x) } diff --git a/src/translate.go b/src/translate.go index e3560d8..125b824 100644 --- a/src/translate.go +++ b/src/translate.go @@ -9,6 +9,7 @@ type UNPARSEcode struct { // returns (number | string | nil), success, error, step func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine bool) (any, bool, ArErr, int) { + if isLine { if isBlank(code) { return nil, true, ArErr{}, 1 @@ -19,10 +20,12 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine b } } } + if isBrackets(code) { - return parseBrackets(code, index, codelines) - } else if isSetVariable(code) { - return parseSetVariable(code, index, codelines) + bracket, worked, err, step := parseBrackets(code, index, codelines) + if worked { + return bracket, worked, err, step + } } operation, worked, err, step := parseOperations(code, index, codelines) if worked { @@ -30,13 +33,19 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine b } else if err.EXISTS { return nil, worked, err, step } - if isNumber(code) { + if isSetVariable(code) { + return parseSetVariable(code, index, codelines) + } else if isNumber(code) { return parseNumber(code) } else if isNegative(code) { return parseNegative(code, index, codelines) } else if isCall(code) { - return parseCall(code, index, codelines) - } else if isVariable(code) { + call, worked, err, step := parseCall(code, index, codelines) + if worked { + return call, worked, err, step + } + } + if isVariable(code) { return parseVariable(code) } else if isMapGet(code) { return mapGetParse(code, index, codelines) diff --git a/test.ar b/test.ar index e8bbb63..9b8d6de 100644 --- a/test.ar +++ b/test.ar @@ -1,4 +1 @@ -y = time.now() -log('start') -time.snooze(1) -log(1e-250-1--1) \ No newline at end of file +term.log(sqrt(10)) \ No newline at end of file