From 4372b513cab6cda55b70fd22f92788b083c523b9 Mon Sep 17 00:00:00 2001 From: Yanis Date: Mon, 26 May 2025 18:33:36 +0200 Subject: [PATCH 01/10] time.gem std module first implementation, with C utils functions epochClockNative and sleepNative --- src/vm.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ stl/time.gem | 28 ++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 stl/time.gem diff --git a/src/vm.c b/src/vm.c index c33351a..c7ca02a 100644 --- a/src/vm.c +++ b/src/vm.c @@ -9,6 +9,7 @@ //< Strings vm-include-string //> Calls and Functions vm-include-time #include +#include //< Calls and Functions vm-include-time #include #include @@ -1208,6 +1209,55 @@ static Value httpDeleteWithOptionsNative(int argCount, Value* args) { return OBJ_VAL(responseHash); } +//> TIME Native Functions + +// Returns the current Unix epoch time (seconds since 1970-01-01T00:00:00 UTC). +// Includes microsecond precision. +static Value epochClockNative(int argCount, Value* args) { + struct timeval tv; + gettimeofday(&tv, NULL); + const double now = (double)tv.tv_sec + (double)tv.tv_usec / CLOCKS_PER_SEC; + return NUMBER_VAL(now); +} + +// Set execution to sleep for a specified number of milliseconds. +static Value sleepNative(int argCount, Value* args) { + if (argCount != 1) { + runtimeError("sleep() takes 1 argument: time (in milliseconds)"); + return NIL_VAL; + } + + if (!IS_NUMBER(args[0])) { + runtimeError("sleep() first argument must be a positive time int"); + return NIL_VAL; + } + + const double seconds = AS_NUMBER(args[0]); + + if (seconds < 0) { + runtimeError("sleep() first argument must be a positive number"); + return NIL_VAL; + } + + if (seconds > UINT_MAX) { + runtimeError("sleep() first argument must not be bigger than 4294967295U"); + return NIL_VAL; + } + +#if _WIN32 + #include + // Windows Sleep takes milliseconds + Sleep((DWORD)(seconds)); +#else + #include + // Unix usleep takes nanoseconds + usleep((useconds_t)seconds * 1e3); +#endif + + return NIL_VAL; +} +//< TIME Native Functions + void initVM() { #if FAST_STACK_ENABLED // Fast stack is pre-allocated, just reset the pointer @@ -1253,6 +1303,11 @@ void initVM() { defineNative("httpPutWithOptions", httpPutWithOptionsNative); defineNative("httpDeleteWithOptions", httpDeleteWithOptionsNative); //< HTTP Native Functions define + //> TIME Native Functions define + defineNative("epochClock", epochClockNative); + defineNative("sleepMs", sleepNative); + //< TIME Native Functions define + //> Initialize Compiler Tables initCompilerTables(); //< Initialize Compiler Tables diff --git a/stl/time.gem b/stl/time.gem new file mode 100644 index 0000000..18b182e --- /dev/null +++ b/stl/time.gem @@ -0,0 +1,28 @@ +module TIME + def now() int + return epochClock(); + end + + def elapsed(int since) int? + int current = TIME.now(); + # should raise an error instead of returning int? + if (since > current) + return nil; + end + return current - since; + end + + def sleep(int seconds) void + sleepMs(seconds); + end + + def measure(func f) int? + int start = TIME.now(); + f(); + return TIME.elapsed(start); + end + + def timeout(func f, int maxSeconds) bool + return TIME.measure(f) <= maxSeconds; + end +end From bb2de58efea85befddb2c21ac4932f831ccba903 Mon Sep 17 00:00:00 2001 From: Yanis Date: Tue, 27 May 2025 12:25:52 +0200 Subject: [PATCH 02/10] added struct Duration to time.gem --- stl/time.gem | 111 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 85 insertions(+), 26 deletions(-) diff --git a/stl/time.gem b/stl/time.gem index 18b182e..00d2fc9 100644 --- a/stl/time.gem +++ b/stl/time.gem @@ -1,28 +1,87 @@ module TIME - def now() int - return epochClock(); - end - - def elapsed(int since) int? - int current = TIME.now(); - # should raise an error instead of returning int? - if (since > current) - return nil; - end - return current - since; - end - - def sleep(int seconds) void - sleepMs(seconds); - end - - def measure(func f) int? - int start = TIME.now(); - f(); - return TIME.elapsed(start); - end - - def timeout(func f, int maxSeconds) bool - return TIME.measure(f) <= maxSeconds; - end + def now() int + return epochClock(); + end + + def elapsed(int since) int? + int current = TIME.now(); + # should raise an error instead of returning int? + if (since > current) + return nil; + end + return current - since; + end + + def sleep(int seconds) void + sleepMs(seconds); + end + + def measure(func f) int? + int start = TIME.now(); + f(); + return TIME.elapsed(start); + end + + def timeout(func f, int maxSeconds) bool + return TIME.measure(f) <= maxSeconds; + end end + +class Duration + # Would be nice to support static methods + # to have a: from_nano, from_seconds ? + + def init(int milliseconds) void + if (milliseconds < 0) + this.milliseconds = 0; + return; + end + this.milliseconds = milliseconds; + end + + # accessors + def as_millis() int + return this.milliseconds; + end + + def as_seconds() int + return this.milliseconds / 1000; + end + + def as_minutes() int + return this.as_seconds() / 60; + end + + # OPERATIONS + # add a way to implement operators +,-,/,* between same class instances + + def add(obj other) obj + # need a true way to check wether obj is instanceof(Duraiton) + if (other.milliseconds) + return Duration(this.milliseconds + other.milliseconds); + else + return this; + end + end + + def sub(obj other) obj + if (other.milliseconds) + if (this.milliseconds - other.milliseconds < 0) + return Duration(0) + else + return Duration(this.milliseconds - other.milliseconds); + end + else + return this; + end + end + + def mul(int factor) obj + return Duration(this.milliseconds * factor); + end + + def div(int divisor) obj + return Duration(this.milliseconds / divisor); + end +end + From 092f2dbdfca6aa9292a9a46c91b04549631a1f8b Mon Sep 17 00:00:00 2001 From: Yanis Date: Tue, 27 May 2025 12:29:03 +0200 Subject: [PATCH 03/10] added struct Duration to time.gem --- stl/time.gem | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/stl/time.gem b/stl/time.gem index 00d2fc9..f841a8b 100644 --- a/stl/time.gem +++ b/stl/time.gem @@ -53,7 +53,7 @@ class Duration end # OPERATIONS - # add a way to implement operators +,-,/,* between same class instances + # add a way to implement arithmetic and comparaison operators between same class instances def add(obj other) obj # need a true way to check wether obj is instanceof(Duraiton) @@ -83,5 +83,23 @@ class Duration def div(int divisor) obj return Duration(this.milliseconds / divisor); end + + # Comparisons + def gt(obj other) bool + return this.milliseconds > other.milliseconds; + end + + def lt(obj other) bool + return this.milliseconds < other.milliseconds; + end + + def eq(obj other) bool + return this.milliseconds == other.milliseconds; + end + + def to_string() string + return this.as_seconds().to_string() + "s"; + end end + From ec37c410b0d81cd087df522e20f7a2e072e56d88 Mon Sep 17 00:00:00 2001 From: Yanis Date: Sat, 31 May 2025 00:43:45 +0200 Subject: [PATCH 04/10] identation spaces set to 2 + TIME module renamed to Time --- stl/time.gem | 182 +++++++++++++++++++++++++-------------------------- 1 file changed, 90 insertions(+), 92 deletions(-) diff --git a/stl/time.gem b/stl/time.gem index f841a8b..ca41ed2 100644 --- a/stl/time.gem +++ b/stl/time.gem @@ -1,105 +1,103 @@ -module TIME - def now() int - return epochClock(); - end - - def elapsed(int since) int? - int current = TIME.now(); - # should raise an error instead of returning int? - if (since > current) - return nil; - end - return current - since; - end - - def sleep(int seconds) void - sleepMs(seconds); - end - - def measure(func f) int? - int start = TIME.now(); - f(); - return TIME.elapsed(start); - end - - def timeout(func f, int maxSeconds) bool - return TIME.measure(f) <= maxSeconds; - end +module Time + def now() int + return epochClock(); + end + + def elapsed(int since) int? + int current = Time.now(); + # should raise an error instead of returning int? + if (since > current) + return nil; + end + return current - since; + end + + def sleep(int seconds) void + sleepMs(seconds); + end + + def measure(func f) int? + int start = Time.now(); + f(); + return Time.elapsed(start); + end + + def timeout(func f, int maxSeconds) bool + return Time.measure(f) <= maxSeconds; + end end class Duration - # Would be nice to support static methods - # to have a: from_nano, from_seconds ? - - def init(int milliseconds) void - if (milliseconds < 0) - this.milliseconds = 0; - return; - end - this.milliseconds = milliseconds; - end - - # accessors - def as_millis() int - return this.milliseconds; - end - - def as_seconds() int - return this.milliseconds / 1000; - end - - def as_minutes() int - return this.as_seconds() / 60; - end - - # OPERATIONS - # add a way to implement arithmetic and comparaison operators between same class instances + # Would be nice to support static methods + # to have a: from_nano, from_seconds ? - def add(obj other) obj - # need a true way to check wether obj is instanceof(Duraiton) - if (other.milliseconds) - return Duration(this.milliseconds + other.milliseconds); - else - return this; - end - end + def init(int milliseconds) void + if (milliseconds < 0) + this.milliseconds = 0; + return; + end + this.milliseconds = milliseconds; + end + + # accessors + def as_millis() int + return this.milliseconds; + end + + def as_seconds() int + return this.milliseconds / 1000; + end + + def as_minutes() int + return this.as_seconds() / 60; + end + + # OPERATIONS + # add a way to implement arithmetic and comparaison operators between same class instances + + def add(obj other) obj + # need a true way to check wether obj is instanceof(Duraiton) + if (other.milliseconds) + return Duration(this.milliseconds + other.milliseconds); + else + return this; + end + end - def sub(obj other) obj - if (other.milliseconds) - if (this.milliseconds - other.milliseconds < 0) - return Duration(0) - else - return Duration(this.milliseconds - other.milliseconds); - end + def sub(obj other) obj + if (other.milliseconds) + if (this.milliseconds - other.milliseconds < 0) + return Duration(0) else - return this; + return Duration(this.milliseconds - other.milliseconds); end - end - - def mul(int factor) obj - return Duration(this.milliseconds * factor); - end + else + return this; + end + end - def div(int divisor) obj - return Duration(this.milliseconds / divisor); - end + def mul(int factor) obj + return Duration(this.milliseconds * factor); + end - # Comparisons - def gt(obj other) bool - return this.milliseconds > other.milliseconds; - end + def div(int divisor) obj + return Duration(this.milliseconds / divisor); + end - def lt(obj other) bool - return this.milliseconds < other.milliseconds; - end + # Comparisons + def gt(obj other) bool + return this.milliseconds > other.milliseconds; + end - def eq(obj other) bool - return this.milliseconds == other.milliseconds; - end - - def to_string() string - return this.as_seconds().to_string() + "s"; - end -end + def lt(obj other) bool + return this.milliseconds < other.milliseconds; + end + def eq(obj other) bool + return this.milliseconds == other.milliseconds; + end + def to_string() string + return this.as_seconds().to_string() + "s"; + end +end \ No newline at end of file From 6b57ccf9690b1c71071ed4d31254e900d7501c7e Mon Sep 17 00:00:00 2001 From: Yanis Date: Sat, 31 May 2025 00:46:20 +0200 Subject: [PATCH 05/10] comments add for demonstration/questioning purposes removed --- stl/time.gem | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/stl/time.gem b/stl/time.gem index ca41ed2..5b7da8e 100644 --- a/stl/time.gem +++ b/stl/time.gem @@ -28,9 +28,6 @@ module Time end class Duration - # Would be nice to support static methods - # to have a: from_nano, from_seconds ? - def init(int milliseconds) void if (milliseconds < 0) this.milliseconds = 0; @@ -39,7 +36,7 @@ class Duration this.milliseconds = milliseconds; end - # accessors + # ACCESSORS def as_millis() int return this.milliseconds; end @@ -53,10 +50,7 @@ class Duration end # OPERATIONS - # add a way to implement arithmetic and comparaison operators between same class instances - def add(obj other) obj - # need a true way to check wether obj is instanceof(Duraiton) if (other.milliseconds) return Duration(this.milliseconds + other.milliseconds); else From b461025b76c8c914c5ce1b3f479e4e10db7db8a2 Mon Sep 17 00:00:00 2001 From: Yanis Date: Sat, 31 May 2025 00:59:26 +0200 Subject: [PATCH 06/10] include sleep functions moved outside of function scope --- src/vm.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/vm.c b/src/vm.c index c7ca02a..4448420 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1220,6 +1220,18 @@ static Value epochClockNative(int argCount, Value* args) { return NUMBER_VAL(now); } +//< For using sleep functions +#if _WIN32 +#include +// Windows Sleep takes milliseconds +#define sleep_ms(ms) Sleep((DWORD)(ms)); +#else +#include +// Unix usleep takes nanoseconds +#define sleep_ms(ms) usleep((useconds_t)ms * 1e3); +#endif +//> + // Set execution to sleep for a specified number of milliseconds. static Value sleepNative(int argCount, Value* args) { if (argCount != 1) { @@ -1232,27 +1244,19 @@ static Value sleepNative(int argCount, Value* args) { return NIL_VAL; } - const double seconds = AS_NUMBER(args[0]); + const double milliseconds = AS_NUMBER(args[0]); - if (seconds < 0) { + if (milliseconds < 0) { runtimeError("sleep() first argument must be a positive number"); return NIL_VAL; } - if (seconds > UINT_MAX) { + if (milliseconds > UINT_MAX) { runtimeError("sleep() first argument must not be bigger than 4294967295U"); return NIL_VAL; } -#if _WIN32 - #include - // Windows Sleep takes milliseconds - Sleep((DWORD)(seconds)); -#else - #include - // Unix usleep takes nanoseconds - usleep((useconds_t)seconds * 1e3); -#endif + sleep_ms(milliseconds); return NIL_VAL; } From 8638c603a82c6f3c794e55f7b430bcf2a3aa1a8e Mon Sep 17 00:00:00 2001 From: Yanis Date: Sat, 31 May 2025 01:19:12 +0200 Subject: [PATCH 07/10] newline add at end of file --- stl/time.gem | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/time.gem b/stl/time.gem index 5b7da8e..9bea2ee 100644 --- a/stl/time.gem +++ b/stl/time.gem @@ -94,4 +94,4 @@ class Duration def to_string() string return this.as_seconds().to_string() + "s"; end -end \ No newline at end of file +end From 70d39a241c5488af689f53f9edca072f91227173 Mon Sep 17 00:00:00 2001 From: Zebulun McNeill Date: Fri, 30 May 2025 18:44:21 -0500 Subject: [PATCH 08/10] update docs --- docs/standard-library.html | 273 +++++++++++-------------------------- nullable_test.gem | 1 + 2 files changed, 79 insertions(+), 195 deletions(-) create mode 100644 nullable_test.gem diff --git a/docs/standard-library.html b/docs/standard-library.html index 70fbcc2..2d94f8f 100644 --- a/docs/standard-library.html +++ b/docs/standard-library.html @@ -36,11 +36,10 @@

Standard Library

@@ -59,6 +58,7 @@

Available Modules

  • Math Module - Mathematical operations
  • HTTP Module - HTTP client functionality for web requests
  • +
  • Time Module - Time operations, measurements, and duration handling
  • Core Functions - Built-in functions available without imports
@@ -87,6 +87,34 @@

📦 Module System

+
+

Importing Modules

+

Learn how to import and use standard library modules in your Gem programs.

+ +

Basic Import Syntax

+
+
# Import math module
+require "math";
+
+# Import HTTP module
+require "http";
+
+# Use imported modules
+int sum = Math.add(10, 5);
+hash response = Http.get("https://api.example.com");
+
+ +

Module Availability

+
+

📦 Standard Library Modules

+
    +
  • "math" - Mathematical operations
  • +
  • "http" - HTTP client functionality
  • +
  • "time" - Time operations and duration handling
  • +
+
+
+

Math Module

The Math module provides mathematical operations and utility functions.

@@ -351,223 +379,78 @@

📡 HTTP Status Codes

-
-

Core Functions

-

These functions are available without any imports and provide essential I/O operations.

+
+

Time Module

+

The Time module provides basic time-related functionality including current time access, sleep operations, and function timing.

-

Output Functions

+

Importing the Time Module

-
# Print with newline
-puts "Hello, World!";                # Prints: Hello, World!\n
-puts 42;                             # Prints: 42
-puts true;                           # Prints: true
-puts false;                          # Prints: false
+
require "time";
-

Core Functions Reference

+

Current Time

-
# Complete core functions API
-puts(any value) void                 # Print value with newline
+
# Get current time as floating-point timestamp
+puts "Current timestamp: #{Time.now()}";
-
-
-

Importing Modules

-

Learn how to import and use standard library modules in your Gem programs.

- -

Basic Import Syntax

+

Sleep Operations

-
# Import math module
-require "math";
-
-# Import HTTP module
-require "http";
-
-# Use imported modules
-int sum = Math.add(10, 5);
-hash response = Http.get("https://api.example.com");
+
# Sleep for a specified number of milliseconds
+puts "Starting operation...";
+Time.sleep(2000);  # Sleep for 2 seconds (2000 milliseconds)
+puts "Operation completed after delay";
-

Multiple Imports

+

Function Timing

-
# Import multiple modules
-require "math";
-require "http";
-
-def fetchAndCalculate() void
-    # Make HTTP request
-    hash response = Http.get("https://api.example.com/numbers");
-    
-    if (response["success"] as bool)
-        # Use math operations on the response
-        int result = Math.multiply(10, 5);
-        puts "Calculation result: " + result;
-    end
-end
-
- -

Module Availability

-
-

📦 Standard Library Modules

-
    -
  • "math" - Mathematical operations
  • -
  • "http" - HTTP client functionality
  • -
-
-
- -
-

Usage Examples

-

Practical examples showing how to use the standard library.

- -

Mathematical Calculation Example

-
-
require "math";
-
-def calculateStats(int a, int b, int c) void
-    # Basic statistics
-    int sum = Math.add(Math.add(a, b), c);
-    int min = Math.min(Math.min(a, b), c);
-    int max = Math.max(Math.max(a, b), c);
-    
-    # Calculate average (integer division)
-    int average = Math.divide(sum, 3);
-    
-    # Calculate range
-    int range = Math.subtract(max, min);
-    
-    # Display results
-    puts "Sum: " + sum;
-    puts "Average: " + average;
-    puts "Min: " + min;
-    puts "Max: " + max;
-    puts "Range: " + range;
+                            
# Measure execution time of a function
+def slowOperation() void
+    Time.sleep(1000);  # Sleep for 1 second (1000 milliseconds)
+    puts "Slow operation completed";
 end
 
-# Usage
-calculateStats(10, 20, 30);
+# Measure function execution +Time.measure(slowOperation);
-

HTTP API Client Example

+

Timeout Operations

-
require "http";
-
-def fetchUserData(string userId) void
-    # Prepare headers
-    hash headers = {};
-    headers["Authorization"] = "Bearer your-api-token";
-    headers["Content-Type"] = "application/json";
-    
-    # Make GET request
-    string url = "https://api.example.com/users/" + userId;
-    hash response = Http.get(url, headers);
-    
-    # Handle response
-    bool success = response["success"] as bool;
-    int status = response["status"] as int;
-    
-    if (success)
-        puts "User data retrieved successfully!";
-        puts "Status: " + status;
-        puts "Response: " + (response["body"] as string);
-        puts "Response time: " + (response["response_time"] as int) + " microseconds";
-    else
-        puts "Failed to fetch user data";
-        puts "Status code: " + status;
-    end
+                            
# Attempt to run function with timeout
+def quickOperation() void
+    puts "Quick operation";
 end
 
-def createUser(string name, string email) void
-    # Prepare user data
-    hash userData = {};
-    userData["name"] = name;
-    userData["email"] = email;
-    userData["active"] = "true";
-    
-    # Prepare headers
-    hash headers = {};
-    headers["Content-Type"] = "application/json";
-    headers["Authorization"] = "Bearer your-api-token";
-    
-    # Make POST request
-    hash response = Http.post("https://api.example.com/users", userData, headers);
-    
-    # Handle response
-    if (response["success"] as bool)
-        puts "User created successfully!";
-        puts "Response: " + (response["body"] as string);
-    else
-        puts "Failed to create user";
-        puts "Status: " + (response["status"] as int);
-    end
+def slowOperation() void
+    Time.sleep(5000);  # Sleep for 5 seconds (5000 milliseconds)
+    puts "This takes too long";
 end
 
-# Usage examples
-fetchUserData("123");
-createUser("Alice Johnson", "alice@example.com");
+Time.timeout(quickOperation, 2); # 2 second timeout +Time.timeout(slowOperation, 2); # 2 second timeout
-

Combined Math and HTTP Example

+

Practical Time Examples

-
require "math";
-require "http";
-
-def calculateAndSubmit(int a, int b) void
-    # Perform calculations
-    int sum = Math.add(a, b);
-    int product = Math.multiply(a, b);
-    int power = Math.power(a, 2);
-    
-    puts "Calculations completed:";
-    puts "Sum: " + sum;
-    puts "Product: " + product;
-    puts "A squared: " + power;
-    
-    # Prepare data for submission
-    hash calculationData = {};
-    calculationData["input_a"] = "" + a;
-    calculationData["input_b"] = "" + b;
-    calculationData["sum"] = "" + sum;
-    calculationData["product"] = "" + product;
-    calculationData["power"] = "" + power;
-    
-    # Submit results via HTTP
-    hash headers = {};
-    headers["Content-Type"] = "application/json";
-    
-    hash response = Http.post("https://api.example.com/calculations", calculationData, headers);
-    
-    if (response["success"] as bool)
-        puts "Results submitted successfully!";
-        puts "Server response: " + (response["body"] as string);
-    else
-        puts "Failed to submit results";
-    end
+                            
# Simple timing and sleep operations
+def timedWork() void
+    puts "Starting work...";
+    Time.sleep(1500);  # 1.5 seconds of work
+    puts "Work completed";
 end
 
-# Usage
-calculateAndSubmit(10, 5);
-
- -

String Processing Example

-
-
# Basic string operations using built-in concatenation
-def createGreeting(string name) string
-    return "Hello, #{name}!";
-end
-
-def createBanner(string message) string
-    string border = "===================";
-    string spacedMessage = " #{message} ";
-    return "#{border}#{spacedMessage}#{border}";
-end
-
-# Usage
-string greeting = createGreeting("Alice");
-puts greeting;
-
-string banner = createBanner("WELCOME");
-puts banner;
+# Measure the work +puts "Timing work operation:"; +Time.measure(timedWork); + +# Multiple sleep operations +puts "Sleep sequence:"; +Time.sleep(500); # 0.5 seconds +puts "Step 1 complete"; +Time.sleep(1000); # 1.0 seconds +puts "Step 2 complete"; +Time.sleep(750); # 0.75 seconds +puts "All steps complete";
diff --git a/nullable_test.gem b/nullable_test.gem new file mode 100644 index 0000000..10b35fa --- /dev/null +++ b/nullable_test.gem @@ -0,0 +1 @@ +require "time"; int start = Time.now(); int? elapsed = Time.elapsed(start); puts elapsed; From d83909a9cd4b886248e629b5df76385e964f0a0a Mon Sep 17 00:00:00 2001 From: Zebulun McNeill Date: Fri, 30 May 2025 18:46:00 -0500 Subject: [PATCH 09/10] update version --- docs/changeblog.html | 2 +- docs/index.html | 6 +++--- version.conf | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/changeblog.html b/docs/changeblog.html index 4dc6dcf..dcc887f 100644 --- a/docs/changeblog.html +++ b/docs/changeblog.html @@ -270,7 +270,7 @@

What's Next?