From 1b3a4c44d9eae40b8f8c4f87c1464dbbcac8647a Mon Sep 17 00:00:00 2001 From: William Bell Date: Mon, 27 Feb 2023 00:07:13 +0000 Subject: [PATCH] add create functions --- .DS_Store | Bin 6148 -> 6148 bytes package.json | 23 ---- readme.md | 2 +- src/brackets.go | 31 ++++++ src/built-in-functions.go | 75 +++++++++++-- src/built-ins.go | 59 +++++------ src/call.go | 21 +++- src/callable.go | 3 +- src/comment.go | 2 +- src/error.go | 8 +- src/import.go | 3 +- src/main.go | 2 +- src/map.go | 20 +++- src/negative.go | 31 ++++++ src/number.go | 26 ++--- src/run.go | 35 +++++- src/string.go | 9 +- src/time.go | 216 ++++++++++++++++++++++++++++++++++++++ src/to-argon.go | 26 ++++- src/translate.go | 20 ++-- src/typeof.go | 2 + src/variable.go | 85 +++++++++++++-- test.ar | 6 +- 23 files changed, 580 insertions(+), 125 deletions(-) delete mode 100644 package.json create mode 100644 src/brackets.go create mode 100644 src/negative.go create mode 100644 src/time.go diff --git a/.DS_Store b/.DS_Store index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..a3585876b6842aef6ec2eca2c673c194487ea3b6 100644 GIT binary patch delta 75 zcmZoMXfc?eJ=s8n#h95Pi6N6AuPnGIFDE}Qoq>UY5r`QWn7|~22NV%tK*(=QT*?m8 KyP2EgCqDo=Xb#i> delta 51 vcmZoMXfc?e&B4IH0LBvwMFg0D92j6^U=Y|?IE{T`gVbaL5thx|96$L1%DD*$ diff --git a/package.json b/package.json deleted file mode 100644 index 5457a25..0000000 --- a/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "argon-v3", - "version": "1.0.0", - "description": "The go to language for mathematicians!", - "scripts": { - "build": "mkdir -p build && go build -o build/argon ./src", - "dev": "nodemon -x go run ./src/. --signal SIGKILL -e go --verbose", - "start": "go run ./src/." - }, - "repository": { - "type": "git", - "url": "git+https://github.com/Ugric/argon-v3.git" - }, - "author": "", - "license": "MIT", - "bugs": { - "url": "https://github.com/Ugric/argon-v3/issues" - }, - "homepage": "https://github.com/Ugric/argon-v3#readme", - "dependencies": { - "nodemon": "^2.0.20" - } -} diff --git a/readme.md b/readme.md index 2f7c12a..86276ef 100644 --- a/readme.md +++ b/readme.md @@ -17,7 +17,7 @@ ARGON 3 is a math-driven programming language designed to make code easy to read - Lightweight: The Argon 3 interpreter is small and doesn't require a lot of system resources to run. ## 💻 Installation -As of now, Argon 3 does not have an installer. Feel free to clone this repo and run the `build` file for your plateform. the build will be found in `bin/Argon-v3(.exe)`. +As of now, Argon 3 does not have an installer. Feel free to clone this repo and run the `build` file for your plateform. the build will be found in `bin/argon(.exe)`. ## 📖 Usage diff --git a/src/brackets.go b/src/brackets.go new file mode 100644 index 0000000..f36eb71 --- /dev/null +++ b/src/brackets.go @@ -0,0 +1,31 @@ +package main + +import "strings" + +var bracketsCompile = makeRegex(`( *)\((.|\n)+\)( *)`) + +type brackets struct { + VAL any + line int + code string + path string +} + +func isBrackets(code UNPARSEcode) bool { + return bracketsCompile.MatchString(code.code) +} + +func parseBrackets(code UNPARSEcode, index int, codeline []UNPARSEcode) (brackets, bool, ArErr, int) { + resp, worked, err, i := translateVal(UNPARSEcode{ + code: strings.TrimSpace(code.code)[1 : len(code.code)-1], + realcode: code.realcode, + line: code.line, + path: code.path, + }, index, codeline, false) + return brackets{ + VAL: resp, + line: code.line, + code: code.realcode, + path: code.path, + }, worked, err, i +} diff --git a/src/built-in-functions.go b/src/built-in-functions.go index ae96948..190b293 100644 --- a/src/built-in-functions.go +++ b/src/built-in-functions.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "time" + "math/big" ) type builtinFunc struct { @@ -25,9 +25,17 @@ func ArgonAdd(args ...any) (any, ArErr) { }, args), ArErr{} } func ArgonDiv(args ...any) (any, ArErr) { - return reduce(func(x any, y any) any { - return newNumber().Quo(y.(number), x.(number)) - }, args), 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) { @@ -36,14 +44,61 @@ func ArgonMult(args ...any) (any, ArErr) { }, args), ArErr{} } -func ArgonSleep(args ...any) (any, ArErr) { +func ArgonInput(args ...any) (any, ArErr) { + // allow a message to be passed in as an argument if len(args) > 0 { - float, _ := args[0].(number).Float64() - time.Sleep(time.Duration(float*1000000000) * time.Nanosecond) + fmt.Print(anyToArgon(args[0], false)) } - return nil, ArErr{} + var input string + fmt.Scanln(&input) + return input, ArErr{} } -func ArgonTimestamp(args ...any) (any, ArErr) { - return newNumber().Quo(newNumber().SetInt64(time.Now().UnixNano()), newNumber().SetInt64(1000000000)), ArErr{} +func ArgonNumber(args ...any) (any, ArErr) { + if len(args) == 0 { + return newNumber(), 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} + } + N, _ := newNumber().SetString(x) + return N, ArErr{} + case number: + return x, ArErr{} + case bool: + if x { + return newNumber().SetInt64(1), ArErr{} + } + return newNumber().SetInt64(0), ArErr{} + case nil: + return newNumber(), ArErr{} + } + + return nil, ArErr{TYPE: "Number Error", message: "Cannot convert " + typeof(args[0]) + " to a number", EXISTS: true} +} + +func ArgonSqrt(a ...any) (any, ArErr) { + if len(a) == 0 { + return nil, ArErr{TYPE: "sqrt", message: "sqrt takes 1 argument", + EXISTS: true} + } + r := a[0].(number) + + if r.Sign() < 0 { + return nil, ArErr{TYPE: "sqrt", message: "sqrt takes a positive number", + EXISTS: true} + } + + var x big.Float + x.SetPrec(30) // I didn't figure out the 'Prec' part correctly, read the docs more carefully than I did and experiement + x.SetRat(r) + + var s big.Float + s.SetPrec(15) + s.Sqrt(&x) + + r, _ = s.Rat(nil) + return r, ArErr{} } diff --git a/src/built-ins.go b/src/built-ins.go index d904c33..cb106c1 100644 --- a/src/built-ins.go +++ b/src/built-ins.go @@ -1,39 +1,32 @@ package main -var vars = map[string]variableValue{} +var vars = map[string]any{} func init() { - vars["log"] = variableValue{ - EXISTS: true, - VAL: builtinFunc{"log", ArgonLog}, - } - vars["add"] = variableValue{ - EXISTS: true, - VAL: builtinFunc{"add", ArgonAdd}, - } - vars["div"] = variableValue{ - EXISTS: true, - VAL: builtinFunc{"div", ArgonDiv}, - } - vars["true"] = variableValue{ - EXISTS: true, - VAL: true, - } - vars["false"] = variableValue{ - EXISTS: true, - VAL: false, - } - vars["mult"] = variableValue{ - EXISTS: true, - VAL: builtinFunc{"mult", ArgonMult}, - } - vars["time"] = variableValue{ - EXISTS: true, - VAL: ArMap{"snooze": builtinFunc{"snooze", ArgonSleep}, "now": builtinFunc{"now", ArgonTimestamp}}, - } + vars["log"] = builtinFunc{"log", ArgonLog} + vars["add"] = builtinFunc{"add", ArgonAdd} + vars["div"] = builtinFunc{"div", ArgonDiv} + vars["true"] = true + vars["false"] = false + vars["null"] = nil + vars["input"] = builtinFunc{"input", ArgonInput} + vars["number"] = builtinFunc{"number", ArgonNumber} + vars["mult"] = builtinFunc{"mult", ArgonMult} + vars["length"] = builtinFunc{"length", func(a ...any) (any, ArErr) { + switch x := a[0].(type) { + case string: + return len(x), ArErr{} + case ArMap: + return len(x), ArErr{} + } + 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"] = variableValue{ - EXISTS: true, - VAL: pi, - } + vars["PI"] = pi + vars["π"] = pi + e, _ := newNumber().SetString("2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416928368190255151086574637721112523897844250569536967707854499699679468644549059879316368892300987931277361782154249992295763514822082698951936680331825288693984964651058209392398294887933203625094431173012381970684161403970198376793206832823764648042953118023287825098194558153017567173613320698112509961818815930416903515988885193458072738667385894228792284998920868058257492796104841984443634632449684875602336248270419786232090021609902353043699418491463140934317381436405462531520961836908887070167683964243781405927145635490613031072085103837505101157477041718986106873969655212671546889570350354") + vars["e"] = e + vars["sqrt"] = builtinFunc{"sqrt", ArgonSqrt} + vars["√"] = builtinFunc{"sqrt", ArgonSqrt} } diff --git a/src/call.go b/src/call.go index bfe4bec..11869db 100644 --- a/src/call.go +++ b/src/call.go @@ -1,10 +1,11 @@ package main import ( + "fmt" "strings" ) -var callCompile = makeRegex("( *).+\\(.*\\)( *)") +var callCompile = makeRegex("( *)(.|\n)+\\((.|\n)*\\)( *)") type call struct { callable any @@ -60,8 +61,9 @@ func runCall(c call, stack stack) (any, ArErr) { return nil, err } args := []any{} + level := append(stack, map[string]any{}) for _, arg := range c.args { - resp, err := runVal(arg, stack) + resp, err := runVal(arg, level) if err.EXISTS { return nil, err } @@ -69,9 +71,20 @@ func runCall(c call, stack stack) (any, ArErr) { } switch x := callable.(type) { case builtinFunc: - return x.FUNC(args...) + resp, err := x.FUNC(args...) + if err.EXISTS { + err = ArErr{err.TYPE, err.message, c.line, c.path, c.code, true} + } + return resp, err case Callable: - return nil, ArErr{"Runtime Error", "cannot call a class", c.line, c.path, c.code, true} + 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{} + for i, param := range x.params { + level[param] = args[i] + } + return runVal(x.run, append(stack, level)) } return nil, ArErr{"Runtime Error", "type '" + typeof(callable) + "' is not callable", c.line, c.path, c.code, true} } diff --git a/src/callable.go b/src/callable.go index fbee1d9..66bd627 100644 --- a/src/callable.go +++ b/src/callable.go @@ -3,7 +3,8 @@ package main type Callable struct { name string params []string - code []any + run any + code string stack stack line int } diff --git a/src/comment.go b/src/comment.go index fcdfb47..d1a5ec2 100644 --- a/src/comment.go +++ b/src/comment.go @@ -4,7 +4,7 @@ import ( "strings" ) -var commentCompile = makeRegex(".*//.*") +var commentCompile = makeRegex("(.|\n)*//(.|\n)*") func isComment(code UNPARSEcode) bool { return commentCompile.MatchString(code.code) diff --git a/src/error.go b/src/error.go index bc6e882..c1ae632 100644 --- a/src/error.go +++ b/src/error.go @@ -15,9 +15,11 @@ type ArErr struct { } func panicErr(err ArErr) { - fmt.Println(" File:", err.path+":"+fmt.Sprint(err.line)) - fmt.Println(" " + err.code) - fmt.Println() + if err.code != "" && err.line != 0 && err.path != "" { + fmt.Println(" File:", err.path+":"+fmt.Sprint(err.line)) + fmt.Println(" " + err.code) + fmt.Println() + } fmt.Println(err.TYPE+":", err.message) os.Exit(1) } diff --git a/src/import.go b/src/import.go index 709a0a1..6e1da72 100644 --- a/src/import.go +++ b/src/import.go @@ -98,7 +98,8 @@ func importMod(realpath string, origin string, main bool) ArErr { if translationerr.EXISTS { return translationerr } - _, runimeErr := run(translated, stack{vars}) + global := map[string]any{} + _, runimeErr := run(translated, stack{vars, global}) if runimeErr.EXISTS { return runimeErr } diff --git a/src/main.go b/src/main.go index 7f8765b..9e87116 100644 --- a/src/main.go +++ b/src/main.go @@ -7,7 +7,7 @@ import ( // args without the program path var Args = os.Args[1:] -type stack = []map[string]variableValue +type stack = []map[string]any func main() { ex, e := os.Getwd() diff --git a/src/map.go b/src/map.go index 914a2dc..bb2458f 100644 --- a/src/map.go +++ b/src/map.go @@ -7,7 +7,12 @@ import ( type ArMap = map[any]any -var mapGetCompile = makeRegex(".+\\.([a-zA-Z_])([a-zA-Z0-9_])*( *)") +type ArClass struct { + value any + MAP ArMap +} + +var mapGetCompile = makeRegex("(.|\n)+\\.([a-zA-Z_])([a-zA-Z0-9_])*( *)") type ArMapGet struct { VAL any @@ -39,6 +44,19 @@ func mapGet(r ArMapGet, stack stack) (any, ArErr) { } } return m[key], ArErr{} + case ArClass: + if _, ok := m.MAP[key]; !ok { + return nil, ArErr{ + "KeyError", + "key '" + fmt.Sprint(key) + "' not found", + r.line, + r.path, + r.code, + true, + } + } + return m.MAP[key], ArErr{} + } return nil, ArErr{ "TypeError", diff --git a/src/negative.go b/src/negative.go new file mode 100644 index 0000000..9a2a06e --- /dev/null +++ b/src/negative.go @@ -0,0 +1,31 @@ +package main + +import "strings" + +var negativeCompile = makeRegex(`( *)-(.|\n)+( *)`) + +type negative struct { + VAL any + line int + code string + path string +} + +func isNegative(code UNPARSEcode) bool { + return negativeCompile.MatchString(code.code) +} + +func parseNegative(code UNPARSEcode, index int, codeline []UNPARSEcode) (negative, bool, ArErr, int) { + resp, worked, err, i := translateVal(UNPARSEcode{ + code: strings.TrimSpace(code.code)[1:], + realcode: code.realcode, + line: code.line, + path: code.path, + }, index, codeline, false) + return negative{ + VAL: resp, + line: code.line, + code: code.realcode, + path: code.path, + }, worked, err, i +} diff --git a/src/number.go b/src/number.go index d20c31e..17d1a77 100644 --- a/src/number.go +++ b/src/number.go @@ -7,15 +7,15 @@ import ( ) type translateNumber struct { - number - code string - line int + number number + code string + line int } -var numberCompile = makeRegex("( *)(\\-)?((([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)( *)") -var binaryCompile = makeRegex("( *)(0b[10]+(.[10]+)?(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)( *)") -var hexCompile = makeRegex("( *)(0x[a-fA-F0-9]+(.[a-fA-F0-9]+)?)( *)") -var octalCompile = makeRegex("( *)(0o[0-7]+(.[0-7]+)?(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)( *)") +var numberCompile = makeRegex("( *)(-)?((([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)( *)") +var binaryCompile = makeRegex("( *)(-)?(0b[10]+(.\\[10]+)?(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)( *)") +var hexCompile = makeRegex("( *)(-)?(0x[a-fA-F0-9]+(\\.[a-fA-F0-9]+)?)( *)") +var octalCompile = makeRegex("( *)(-)?(0o[0-7]+(\\.[0-7]+)?(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)( *)") // a number type type number = *big.Rat @@ -25,11 +25,6 @@ func newNumber() *big.Rat { return new(big.Rat) } -// converts a string into a number -func stringToNumber(str string) (*big.Rat, bool) { - return newNumber().SetString(str) -} - func isNumber(code UNPARSEcode) bool { return numberCompile.MatchString(code.code) || binaryCompile.MatchString(code.code) || hexCompile.MatchString(code.code) || octalCompile.MatchString(code.code) } @@ -89,10 +84,7 @@ var subscript = map[byte]string{ } // returns translateNumber, success, error -func parseNumber(code UNPARSEcode) (translateNumber, bool, ArErr, int) { +func parseNumber(code UNPARSEcode) (number, bool, ArErr, int) { output, _ := newNumber().SetString(strings.TrimSpace(code.code)) - return translateNumber{ - number: output, - line: code.line, - }, true, ArErr{}, 1 + return output, true, ArErr{}, 1 } diff --git a/src/run.go b/src/run.go index dc2b62a..4cb683d 100644 --- a/src/run.go +++ b/src/run.go @@ -2,17 +2,44 @@ package main // returns (number|string|nil), error func runVal(line any, stack stack) (any, ArErr) { + if len(stack) > 500 { + return nil, ArErr{ + TYPE: "Stack overflow", + message: "the stack has exceeded 500 levels", + EXISTS: true, + } + } switch x := line.(type) { - case translateNumber: - return (x.number), ArErr{} - case translateString: - return (x.str), ArErr{} + case number: + return x, ArErr{} + case string: + return x, ArErr{} case call: return runCall(x, stack) case accessVariable: return readVariable(x, stack) case ArMapGet: return mapGet(x, stack) + case ArClass: + return x.MAP, ArErr{} + case setVariable: + return setVariableValue(x, stack) + case negative: + resp, err := runVal(x.VAL, stack) + if err.EXISTS { + return nil, err + } + switch y := resp.(type) { + case number: + return newNumber().Neg(y), ArErr{} + } + return nil, ArErr{ + TYPE: "TypeError", + message: "cannot negate a non-number", + EXISTS: true, + } + case brackets: + return runVal(x.VAL, stack) } panic("unreachable") } diff --git a/src/string.go b/src/string.go index 225ac02..0e386d1 100644 --- a/src/string.go +++ b/src/string.go @@ -32,16 +32,13 @@ func unquoted( } // returns translateString, success, error -func parseString(code UNPARSEcode) (translateString, bool, ArErr, int) { +func parseString(code UNPARSEcode) (string, bool, ArErr, int) { trim := strings.Trim(code.code, " ") unquoted, err := unquoted(trim) if err != nil { - return translateString{}, false, ArErr{"Syntax Error", "invalid string", code.line, code.path, code.realcode, true}, 1 + return "", false, ArErr{"Syntax Error", "invalid string", code.line, code.path, code.realcode, true}, 1 } - return translateString{ - str: unquoted, - line: code.line, - }, true, ArErr{}, 1 + return unquoted, true, ArErr{}, 1 } diff --git a/src/time.go b/src/time.go new file mode 100644 index 0000000..408e672 --- /dev/null +++ b/src/time.go @@ -0,0 +1,216 @@ +package main + +import ( + "time" +) + +var MicroSeconds = newNumber().SetInt64(1000000) + +func ArTimeClass(N time.Time) ArClass { + return ArClass{ + newNumber().Quo(newNumber().SetInt64(N.UnixMicro()), MicroSeconds), + ArMap{ + "year": builtinFunc{ + "year", + func(a ...any) (any, ArErr) { + return newNumber().SetInt64(int64(N.Year())), ArErr{} + }, + }, + "month": builtinFunc{ + "month", + func(a ...any) (any, ArErr) { + return N.Month().String(), ArErr{} + }, + }, + "day": builtinFunc{ + "day", + func(a ...any) (any, ArErr) { + return newNumber().SetInt64(int64(N.Day())), ArErr{} + }, + }, + "hour": builtinFunc{ + "hour", + func(a ...any) (any, ArErr) { + return newNumber().SetInt64(int64(N.Hour())), ArErr{} + }, + }, + "minute": builtinFunc{ + "minute", + func(a ...any) (any, ArErr) { + return newNumber().SetInt64(int64(N.Minute())), ArErr{} + }, + }, + "second": builtinFunc{ + "second", + func(a ...any) (any, ArErr) { + return newNumber().SetInt64(int64(N.Second())), ArErr{} + }, + }, + "nanosecond": builtinFunc{ + "nanosecond", + func(a ...any) (any, ArErr) { + return newNumber().SetInt64(int64(N.Nanosecond())), ArErr{} + }, + }, + "weekday": builtinFunc{ + "weekday", + func(a ...any) (any, ArErr) { + return N.Weekday().String(), ArErr{} + }, + }, + "yearDay": builtinFunc{ + "yearDay", + func(a ...any) (any, ArErr) { + return newNumber().SetInt64(int64(N.YearDay())), ArErr{} + }, + }, + "unix": builtinFunc{ + "unix", + func(a ...any) (any, ArErr) { + return newNumber().SetInt64(N.Unix()), ArErr{} + }, + }, + "unixNano": builtinFunc{ + "unixNano", + func(a ...any) (any, ArErr) { + return newNumber().SetInt64(N.UnixNano()), ArErr{} + }, + }, + "unixMilli": builtinFunc{ + "unixMilli", + func(a ...any) (any, ArErr) { + return newNumber().SetInt64(N.UnixMilli()), ArErr{} + }, + }, + "unixMicro": builtinFunc{ + "unixMicro", + func(a ...any) (any, ArErr) { + return newNumber().SetInt64(N.UnixMicro()), ArErr{} + }, + }, + "format": builtinFunc{ + "date", + func(a ...any) (any, ArErr) { + if len(a) == 0 { + return N.Format(time.UnixDate), ArErr{} + } + return N.Format(a[0].(string)), ArErr{} + }, + }, + }} +} + +var ArTime = map[any]any{ + "snooze": builtinFunc{"snooze", func(a ...any) (any, ArErr) { + if len(a) > 0 { + float, _ := a[0].(number).Float64() + time.Sleep(time.Duration(float*1000000000) * time.Nanosecond) + } + return nil, ArErr{} + }}, + "now": builtinFunc{"now", func(a ...any) (any, ArErr) { + return ArTimeClass(time.Now()), ArErr{} + }}, + "parse": builtinFunc{"parse", func(a ...any) (any, ArErr) { + if len(a) == 1 { + N, err := time.Parse(time.UnixDate, a[0].(string)) + if err != nil { + return nil, ArErr{ + TYPE: "ArErr", + message: err.Error(), + } + } + return ArTimeClass(N), ArErr{} + } else if len(a) > 1 { + N, err := time.Parse(a[0].(string), a[1].(string)) + if err != nil { + return nil, ArErr{ + TYPE: "ArErr", + message: err.Error(), + EXISTS: true, + } + } + return ArTimeClass(N), ArErr{} + } + return nil, ArErr{ + TYPE: "ArErr", + message: "parse requires 2 arguments", + EXISTS: true, + } + }}, + "parseInLocation": builtinFunc{"parseInLocation", func(a ...any) (any, ArErr) { + if len(a) > 2 { + N, err := time.ParseInLocation(a[0].(string), a[1].(string), time.Local) + if err != nil { + return nil, ArErr{ + TYPE: "ArErr", + message: err.Error(), + EXISTS: true, + } + } + return ArTimeClass(N), ArErr{} + } + return nil, ArErr{ + TYPE: "ArErr", + message: "parseInLocation requires 3 arguments", + EXISTS: true, + } + }, + }, + "date": builtinFunc{"date", func(a ...any) (any, ArErr) { + if len(a) > 0 { + N, err := time.Parse(time.UnixDate, a[0].(string)) + if err != nil { + return nil, ArErr{ + TYPE: "ArErr", + message: err.Error(), + EXISTS: true, + } + } + return ArTimeClass(N), ArErr{} + } + return nil, ArErr{ + TYPE: "ArErr", + message: "date requires 1 argument", + EXISTS: true, + } + }, + }, + "Unix": builtinFunc{"Unix", func(a ...any) (any, ArErr) { + if len(a) > 1 { + sec, _ := a[0].(number).Float64() + nsec, _ := a[1].(number).Float64() + return ArTimeClass(time.Unix(int64(sec), int64(nsec))), ArErr{} + } + return nil, ArErr{ + TYPE: "ArErr", + message: "Unix requires 2 arguments", + EXISTS: true, + } + }, + }, + "UnixMilli": builtinFunc{"UnixMilli", func(a ...any) (any, ArErr) { + if len(a) > 0 { + msec, _ := a[0].(number).Float64() + return ArTimeClass(time.UnixMilli(int64(msec))), ArErr{} + } + return nil, ArErr{ + TYPE: "ArErr", + message: "UnixMilli requires 1 argument", + EXISTS: true, + } + }, + }, + "UnixMicro": builtinFunc{"UnixMicro", func(a ...any) (any, ArErr) { + if len(a) > 0 { + usec, _ := a[0].(number).Float64() + return ArTimeClass(time.UnixMicro(int64(usec))), ArErr{} + } + return nil, ArErr{ + TYPE: "ArErr", + message: "UnixMicro requires 1 argument", + EXISTS: true, + } + }, + }, +} diff --git a/src/to-argon.go b/src/to-argon.go index b0b5946..b71030b 100644 --- a/src/to-argon.go +++ b/src/to-argon.go @@ -4,6 +4,7 @@ import ( "fmt" "math" "strconv" + "strings" ) func anyToArgon(x any, quote bool) string { @@ -22,8 +23,31 @@ func anyToArgon(x any, quote bool) string { } else if math.IsInf(num, -1) { return "-infinity" } else { - return strconv.FormatFloat(num, 'f', -1, 64) + return numberToString(x, 0) } + case bool: + return strconv.FormatBool(x) + case nil: + return "null" + case ArMap: + keys := make([]any, len(x)) + + i := 0 + for k := range x { + keys[i] = k + i++ + } + output := []string{} + for _, key := range keys { + output = append(output, anyToArgon(key, true)+": "+anyToArgon(x[key], true)) + } + return "{" + strings.Join(output, ", ") + "}" + case builtinFunc: + return "" + case Callable: + return "" + case ArClass: + return anyToArgon(x.value, false) default: return fmt.Sprint(x) } diff --git a/src/translate.go b/src/translate.go index 4b91321..01b167d 100644 --- a/src/translate.go +++ b/src/translate.go @@ -7,7 +7,7 @@ type UNPARSEcode struct { path string } -// returns (translateNumber | translateString| nil), success, error +// returns (translateNumber | translateString| nil), success, error, step func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine bool) (any, bool, ArErr, int) { if isLine { if isBlank(code) { @@ -19,14 +19,20 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine b } } } - if isCall(code) { + if isSetVariable(code) { + return parseSetVariable(code, index, codelines) + } else if isBrackets(code) { + return parseBrackets(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) { return parseVariable(code) } else if isMapGet(code) { return mapGetParse(code, index, codelines) - } else if isNumber(code) { - return parseNumber(code) } else if isString(code) { return parseString(code) } @@ -36,9 +42,9 @@ func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine b // returns [](translateNumber | translateString), error func translate(codelines []UNPARSEcode) ([]any, ArErr) { translated := []any{} - for i, code := range codelines { - val, _, err, _ := translateVal(code, i, codelines, true) - + for i := 0; i < len(codelines); { + val, _, err, step := translateVal(codelines[i], i, codelines, true) + i += step if err.EXISTS { return nil, err } diff --git a/src/typeof.go b/src/typeof.go index 2a56992..f4a299e 100644 --- a/src/typeof.go +++ b/src/typeof.go @@ -14,6 +14,8 @@ func typeof(val any) string { return "function" case builtinFunc: return "function" + case ArMap: + return "map" } return "unknown" } diff --git a/src/variable.go b/src/variable.go index 33ff63c..db73446 100644 --- a/src/variable.go +++ b/src/variable.go @@ -4,7 +4,8 @@ import ( "strings" ) -var variableCompile = makeRegex(`([a-zA-Z_])([a-zA-Z0-9_])*`) +var variableCompile = makeRegex(`( *)([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|(\p{L}\p{M}*))*( *)`) +var setVariableCompile = makeRegex(`( *)(let( +))?([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|(\p{L}\p{M}*))*(\(( *)((([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|(\p{L}\p{M}*))*)(( *)\,( *)([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|(\p{L}\p{M}*))*)*)?( *)\))?( *)=(.|\n)+`) var blockedVariableNames = map[string]bool{ "if": true, @@ -21,12 +22,6 @@ var blockedVariableNames = map[string]bool{ "do": true, } -type variableValue struct { - VAL any - EXISTS any - origin string -} - type accessVariable struct { name string line int @@ -34,6 +29,17 @@ type accessVariable struct { path string } +type setVariable struct { + TYPE string + name string + value any + function bool + params []string + line int + code string + path string +} + func isVariable(code UNPARSEcode) bool { return variableCompile.MatchString(code.code) } @@ -49,8 +55,71 @@ func parseVariable(code UNPARSEcode) (accessVariable, bool, ArErr, int) { func readVariable(v accessVariable, stack stack) (any, ArErr) { for i := len(stack) - 1; i >= 0; i-- { if val, ok := stack[i][v.name]; ok { - return val.VAL, ArErr{} + return val, ArErr{} } } return nil, ArErr{"Runtime Error", "variable \"" + v.name + "\" does not exist", v.line, v.path, v.code, true} } + +func isSetVariable(code UNPARSEcode) bool { + return setVariableCompile.MatchString(code.code) +} + +func parseSetVariable(code UNPARSEcode, index int, lines []UNPARSEcode) (setVariable, bool, ArErr, int) { + trim := strings.TrimSpace(code.code) + equalsplit := strings.SplitN(trim, "=", 2) + spacesplit := strings.SplitN(equalsplit[0], " ", 2) + TYPE := "auto" + name := strings.TrimSpace(equalsplit[0]) + params := []string{} + function := false + if spacesplit[0] == "let" { + TYPE = "let" + name = strings.TrimSpace(spacesplit[1]) + } + if name[len(name)-1] == ')' { + function = true + bracketsplit := strings.SplitN(name, "(", 2) + name = bracketsplit[0] + params = strings.Split(bracketsplit[1][:len(bracketsplit[1])-1], ",") + for i := 0; i < len(params); i++ { + params[i] = strings.TrimSpace(params[i]) + } + } + if blockedVariableNames[name] { + return setVariable{}, false, ArErr{"Naming Error", "Naming Error: \"" + name + "\" is a reserved keyword", code.line, code.path, code.realcode, true}, 1 + } + value, success, err, i := translateVal(UNPARSEcode{code: equalsplit[1], realcode: code.realcode, line: code.line, path: code.path}, index, lines, true) + if !success { + return setVariable{}, false, err, i + } + return setVariable{TYPE: TYPE, name: name, value: value, function: function, params: params, line: code.line, code: code.code, path: code.path}, true, ArErr{}, i +} + +func setVariableValue(v setVariable, stack stack) (any, ArErr) { + var resp any + if v.function { + resp = Callable{v.name, v.params, v.value, v.code, stack, v.line} + } else { + respp, err := runVal(v.value, stack) + if err.EXISTS { + return nil, err + } + resp = respp + } + if v.TYPE == "let" { + if _, ok := stack[len(stack)-1][v.name]; ok { + return stack, ArErr{"Runtime Error", "variable \"" + v.name + "\" already exists", v.line, v.path, v.code, true} + } + stack[len(stack)-1][v.name] = resp + } else { + for i := len(stack) - 1; i >= 0; i-- { + if _, ok := stack[i][v.name]; ok { + stack[i][v.name] = resp + return stack, ArErr{} + } + } + stack[len(stack)-1][v.name] = resp + } + return resp, ArErr{} +} diff --git a/test.ar b/test.ar index cf013f8..0a1c87d 100644 --- a/test.ar +++ b/test.ar @@ -1,3 +1,3 @@ -log(time.now()) -time.snooze(1) -log(time.now()) \ No newline at end of file +let f(x) = x + +log(f(1)) \ No newline at end of file