From 00a48f2d6e5f03931626317285bb67d668cd85a8 Mon Sep 17 00:00:00 2001 From: anirudh Date: Thu, 31 Oct 2024 12:46:03 +0530 Subject: [PATCH] func: add time, random and http to larder --- README.md | 7 +- .../java/com/tahini/lang/Interpreter.java | 21 ---- .../java/com/tahini/lang/StandardLibrary.java | 115 ++++++++++++++++++ tahini/app/src/main/resources/stdlib/http.tah | 5 + tahini/app/src/main/resources/stdlib/math.tah | 5 + .../app/src/main/resources/stdlib/random.tah | 11 ++ .../app/src/main/resources/stdlib/string.tah | 15 +++ tahini/app/src/main/resources/stdlib/time.tah | 89 ++++++++++++++ test1.tah | 16 ++- 9 files changed, 260 insertions(+), 24 deletions(-) create mode 100644 tahini/app/src/main/resources/stdlib/http.tah create mode 100644 tahini/app/src/main/resources/stdlib/random.tah create mode 100644 tahini/app/src/main/resources/stdlib/time.tah diff --git a/README.md b/README.md index e078d2e..154a80d 100644 --- a/README.md +++ b/README.md @@ -441,10 +441,13 @@ Tahini comes with a growing standard library, called [the `larder`](./tahini/app Here are some of the modules and functions available in the `larder`: -- `larder/math` - Mathematical functions like `sqrt`, `pow`, `sin` etc. -- `larder/string` - String manipulation functions like `split`, `join` etc. +- `larder/math` - Mathematical functions like `sqrt`, `pow`, `sin`, `round` etc. +- `larder/string` - String manipulation functions like `split`, `join`, `replace` etc. - `larder/io` - File I/O functions like `readFile`, `writeFile` etc. - `larder/collections` - Collection functions like `values`, `keys`, `append`, `remove` etc. +- `larder/time` - Time functions like `now`, `format` etc. +- `larder/random` - Random number generation functions like `random`, `randomInt` etc. +- `larder/http` - HTTP request functions like `get` (only `get` for now). You can import the `larder` modules in your Tahini code using the `scoop` keyword, similar to importing other Tahini files. diff --git a/tahini/app/src/main/java/com/tahini/lang/Interpreter.java b/tahini/app/src/main/java/com/tahini/lang/Interpreter.java index da3536a..64a78b7 100644 --- a/tahini/app/src/main/java/com/tahini/lang/Interpreter.java +++ b/tahini/app/src/main/java/com/tahini/lang/Interpreter.java @@ -32,27 +32,6 @@ class BreakException extends RuntimeException { public Interpreter(boolean repl) { this.repl = repl; - globals.define("clock", new TahiniCallable() { - @Override - public int arity() { - return 0; - } - - @Override - public Object call(Interpreter interpreter, List arguments) { - return (double) System.currentTimeMillis() / 1000.0; - } - - @Override - public String toString() { - return ""; - } - - @Override - public boolean isInternal() { - return false; - } - }); StandardLibrary.addStandardFunctions(environment); StandardLibrary.addInternalFunctions(environment); } diff --git a/tahini/app/src/main/java/com/tahini/lang/StandardLibrary.java b/tahini/app/src/main/java/com/tahini/lang/StandardLibrary.java index b2acdfc..8156611 100644 --- a/tahini/app/src/main/java/com/tahini/lang/StandardLibrary.java +++ b/tahini/app/src/main/java/com/tahini/lang/StandardLibrary.java @@ -1,6 +1,10 @@ package com.tahini.lang; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -14,6 +18,7 @@ class StandardLibrary { public static void addStandardFunctions(Environment globalEnv) { globalEnv.define("input", new InputFunction()); globalEnv.define("len", new ArrayLengthFunction()); + globalEnv.define("clock", new UnixEpochSecondsFunction()); } public static void addInternalFunctions(Environment globalEnv) { @@ -21,6 +26,116 @@ public static void addInternalFunctions(Environment globalEnv) { globalEnv.define("_values", new HashmapValuesFunction()); globalEnv.define("_read", new FileReadFunction()); globalEnv.define("_write", new FileWriteFunction()); + globalEnv.define("_random", new RandomHelperFunction()); + globalEnv.define("_http", new HTTPRestFunction()); + } +} + +class HTTPRestFunction implements TahiniCallable { + + @Override + public int arity() { + return 2; + } + + @Override + public Object call(Interpreter interpreter, List args) { + if (args.size() != 2) { + throw new RuntimeError(null, "Expected 2 arguments but got " + args.size() + ".", null); + } + Object urlarg = args.get(0); + if (!(urlarg instanceof String url)) { + throw new RuntimeError(null, "Expected a string url but got " + urlarg + ".", null); + } + Object methodarg = args.get(1); + if (!(methodarg.equals("GET"))) { + throw new RuntimeError(null, "Expected 'GET' but got " + methodarg + ".", null); + } + String response; + try { + response = sendGetRequest(url); + return response; + } catch (IOException e) { + throw new RuntimeError(null, "Error sending HTTP request: " + e.getMessage(), null); + } + } + + @Override + public String toString() { + return ""; + } + + @Override + public boolean isInternal() { + return false; + } + + private static String sendGetRequest(String apiUrl) throws IOException { + URL url = new URL(apiUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + + int responseCode = connection.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuilder response = new StringBuilder(); + String line; + + while ((line = reader.readLine()) != null) { + response.append(line); + } + + reader.close(); + return response.toString(); + } else { + return "Failed to get the API response. Response code: " + responseCode; + } + } +} + +class RandomHelperFunction implements TahiniCallable { + + @Override + public int arity() { + return 0; + } + + @Override + public Object call(Interpreter interpreter, List arguments) { + return Math.random(); + } + + @Override + public String toString() { + return ""; + } + + @Override + public boolean isInternal() { + return false; + } +} + +class UnixEpochSecondsFunction implements TahiniCallable { + + @Override + public int arity() { + return 0; + } + + @Override + public Object call(Interpreter interpreter, List arguments) { + return (double) System.currentTimeMillis() / 1000.0; + } + + @Override + public String toString() { + return ""; + } + + @Override + public boolean isInternal() { + return false; } } diff --git a/tahini/app/src/main/resources/stdlib/http.tah b/tahini/app/src/main/resources/stdlib/http.tah new file mode 100644 index 0000000..29811bf --- /dev/null +++ b/tahini/app/src/main/resources/stdlib/http.tah @@ -0,0 +1,5 @@ +// Send a GET request to the specified URL and return the response. +fun get(url) { + var response = _http(url, "GET"); + return response; +} diff --git a/tahini/app/src/main/resources/stdlib/math.tah b/tahini/app/src/main/resources/stdlib/math.tah index 9435157..ea8396b 100644 --- a/tahini/app/src/main/resources/stdlib/math.tah +++ b/tahini/app/src/main/resources/stdlib/math.tah @@ -88,3 +88,8 @@ fun cos(a) { fun tan(a) { return sin(a) / cos(a); } + +fun round(float, precision) { + var factor = pow(10, precision); + return ceil(float * factor - 0.5) / factor; +} diff --git a/tahini/app/src/main/resources/stdlib/random.tah b/tahini/app/src/main/resources/stdlib/random.tah new file mode 100644 index 0000000..6061ae7 --- /dev/null +++ b/tahini/app/src/main/resources/stdlib/random.tah @@ -0,0 +1,11 @@ +scoop "larder/math"; + +// Random float between 0 and 1 +fun random() { + return _random(); +} + +// Random integer between min and max +fun randomInt(min, max) { + return floor(random() * (max - min + 1) + min); +} diff --git a/tahini/app/src/main/resources/stdlib/string.tah b/tahini/app/src/main/resources/stdlib/string.tah index b70b083..071d463 100644 --- a/tahini/app/src/main/resources/stdlib/string.tah +++ b/tahini/app/src/main/resources/stdlib/string.tah @@ -23,3 +23,18 @@ fun join(arr, delimiter) { } return result; } + +fun replace(string, old, new) { + var result = ""; + var start = 0; + var oldLength = len(old); + for (var i = 0; i <= len(string) - oldLength; i = i + 1) { + if (string[i:i + oldLength] == old) { + result = result + string[start:i] + new; + start = i + oldLength; + i = i + oldLength - 1; + } + } + result = result + string[start:len(string)]; + return result; +} diff --git a/tahini/app/src/main/resources/stdlib/time.tah b/tahini/app/src/main/resources/stdlib/time.tah new file mode 100644 index 0000000..706c77b --- /dev/null +++ b/tahini/app/src/main/resources/stdlib/time.tah @@ -0,0 +1,89 @@ +// time standard lib funcs +scoop "larder/string" into string; +scoop "larder/math" into math; + +// unix epoch in seconds (using the inbuilt clock() function) +fun now() { + return clock(); +} + +fun isLeapYear(year) { + if (year % 4 == 0) { + if (year % 100 == 0) { + if (year % 400 == 0) { + return true; + } else { + return false; + } + } else { + return true; + } + } else { + return false; + } +} + +fun daysInMonth(month, year) { + if (month == 2) { + return isLeapYear(year) ? 29 : 28; + } else if (month == 4 or month == 6 or month == 9 or month == 11) { + return 30; + } else { + return 31; + } +} + +// Format a Unix timestamp into a string format "YYYY-MM-DD HH:mm:ss" +fun format(epoch, formatStr) { + var secondsInDay = 86400; + var secondsInYear = 365 * secondsInDay; + var remaining = epoch; + + // Calculate the year + var year = 1970; + while (remaining >= secondsInYear) { + remaining = remaining - secondsInYear; + year = year + 1; + secondsInYear = isLeapYear(year) ? 366 * secondsInDay : 365 * secondsInDay; + } + + // Calculate the month and day + var month = 1; + while (true) { + var daysInThisMonth = daysInMonth(month, year); + var secondsInThisMonth = daysInThisMonth * secondsInDay; + if (remaining < secondsInThisMonth) { + break; + } + remaining = remaining - secondsInThisMonth; + month = month + 1; + } + var day = 1 + remaining / secondsInDay; + day = math::floor(day); + remaining = remaining % secondsInDay; + + // Calculate the hour, minute, and second + var hour = remaining / 3600; + hour = math::floor(hour); + remaining = remaining % 3600; + var minute = remaining / 60; + minute = math::floor(minute); + var second = remaining % 60; + second = math::round(second, 3); + + // Replace placeholders in formatStr + var result = formatStr; + result = string::replace(result, "YYYY", year); + result = string::replace(result, "MM", month); + result = string::replace(result, "DD", day); + result = string::replace(result, "HH", hour); + result = string::replace(result, "mm", minute); + result = string::replace(result, "ss", second); + + return result; +} + +// Convert a Unix timestamp to ISO 8601 format "YYYY-MM-DDTHH:mm:ssZ" +fun toIso(epoch) { + return format(epoch, "YYYY-MM-DDTHH:mm:ssZ"); +} diff --git a/test1.tah b/test1.tah index 24dc5bb..2599d59 100644 --- a/test1.tah +++ b/test1.tah @@ -2,6 +2,21 @@ scoop "larder/math" into math; scoop "larder/collections" into collections; scoop "larder/string" into string; scoop "larder/io"; +scoop "larder/time" into time; +scoop "larder/random" into random; +scoop "larder/http" into http; + +print random::random(); +print random::randomInt(3,10); + +print http::get("https://jsonplaceholder.typicode.com/posts/1"); + +var curr = time::now(); +print curr; +print time::format(curr, "YYYY-MM-DD HH:mm"); +print time::toIso(curr); + +print string::replace("hello world cello", "llo", 44); print math::sin(0.5); @@ -26,4 +41,3 @@ var l = string::split("hello. world", ". "); print len(l); print l[1]; print string::join(l, " | "); -writeFile("test1.tah","//okllll");