Compare commits

..
45 Commits
Author SHA1 Message Date
retoor 5609a7558c fix: remove entire src/stdlib directory including README and all module source files
Build and Test / build (push) Successful in 7s
2025-11-25 02:13:03 +00:00
retoor e7fe6d2e56 feat: add scope-based memory management with push/pop and variable isolation
Introduce a new scope system that replaces manual stack frame management with structured scope_push/scope_pop calls. The implementation includes a Scope struct with dedicated memory and local symbol arrays, along with helper functions for variable lookup, memory access, and state synchronization. This change touches context.c, main.c, oop.c, parser.c, and async_stdlib.c to integrate scope initialization, cleanup, and frame transitions during function calls and destructor execution.
2025-11-25 01:27:25 +00:00
retoor 09e3339855 feat: add benchmarking stdlib with memory safety checks and example scripts
Implement a new `benchmark` standard library module providing high-precision nanosecond timing functions (`bench_start`, `bench_end`, `bench_format`, `bench_format_auto`, `bench_reset`). Add a dedicated `memory.c` module with runtime memory safety validation, including stack overflow detection, local variable limit enforcement, and high-water mark tracking. Integrate memory checks into the interpreter's variable declaration path in `src/interpreter.c` and initialize the memory subsystem in `src/main.c`. Update the build system (`Makefile`) to compile the new source files and include benchmark tests in the test suite. Extend `TUTORIAL.md` with comprehensive documentation for the benchmarking API and random number functions (`rand`, `srand`, `rand_range`, `time`). Add two example scripts (`examples/bench.rc` and `examples/benchmark_demo.rc`) demonstrating performance measurement of recursive Fibonacci, loop iterations, list operations, string concatenation, object creation, and sorting algorithms.
2025-11-25 00:25:16 +00:00
retoor 274c44e0f7 feat: add debug logging system and fix context snapshot bounds safety 2025-11-24 14:07:12 +00:00
retoor 2419b9b51e fix: add null checks for malloc failures and improve memory access validation in parser and async stdlib 2025-11-24 10:21:23 +00:00
retoor b5a0faac68 chore: add async I/O thread-safety notes and mutex protection details to TUTORIAL.md
- Inserted bullet point about thread-safe async I/O with mutex protection in coroutine section
- Added bullet point about memory_mutex usage for safe memory[] access in async operations
- Included full paragraph describing thread-safe async I/O operations (file read/write, socket recv/send) with mutex protection for shared memory access
2025-11-24 10:12:34 +00:00
retoor 8c59cfafa5 feat: add fine-grained mutexes for memory, locals, interpreter state, and rwlocks for funcs, native_funcs, classes, objects, and call_stack to enable concurrent access in async stdlib operations 2025-11-24 10:09:55 +00:00
retoor 7dd3a13732 fix: remove unused token_mutex and add str_pool_mutex locking to string and file stdlib functions 2025-11-24 09:42:54 +00:00
retoor 6be2eb28f0 chore: fix infinite loop condition in http_fileserver.rc and add file server documentation 2025-11-24 09:26:12 +00:00
retoor acc2ac6088 chore: remove trailing whitespace from README.md line 42 2025-11-24 09:21:23 +00:00
retoor 5ba90c77f3 chore: add socket and constants test suites, async file I/O tests, and strcmp tests 2025-11-24 09:00:49 +00:00
retoor b6a99ec2c8 chore: remove http stdlib module and its test suite from build and registration 2025-11-24 08:08:13 +00:00
retoor 8c0548960f chore: add http stdlib, thread-safe token rwlock, and dynamic array bounds in interpreter 2025-11-24 07:55:17 +00:00
retoor 2fc2e79e68 chore: expand async/await documentation with execution context isolation details 2025-11-24 04:51:03 +00:00
retoor 7e0a843afa chore: replace hardcoded buffer limits with MULTIPLIER scaling in src/types.h
Define a new MULTIPLIER constant set to 10 and apply it to all memory, identifier, function, class, include, and object limit macros, replacing the previous hardcoded values with their product by MULTIPLIER.
2025-11-24 02:47:07 +00:00
retoor 179ebc1892 feat: add ExecutionContext abstraction and runtime context management for async coroutine isolation 2025-11-24 02:37:40 +00:00
retoor c1fd0a22fe chore: add pthread mutex lock for thread-safe token access in native_eval 2025-11-24 01:56:47 +00:00
retoor 9b2a2c73d5 fix: add pthread mutex for thread-safe token access in coroutine execution 2025-11-24 01:19:24 +00:00
retoor 619c0467b0 fix: add try-catch block to handle network timeout in fetchUserData
Add robustness test suite covering array bounds, string operations, nested loops, and recursion protection for the custom runtime environment
2025-11-24 01:09:13 +00:00
retoor 5634bf5e11 fix: add bounds checks and robustness improvements to interpreter, parser, and tokenizer 2025-11-24 01:09:02 +00:00
retoor 655d33d789 feat: add call stack tracking and enhanced error reporting with file:line context
Implement a full call stack system that tracks function calls during interpretation, including native function boundaries. Add `push_call_frame`/`pop_call_frame` functions, a `CallStack` struct with depth-limited frames, and `print_stacktrace` for formatted error output. Introduce `error_with_context` to replace bare `error()` calls, and modify the tokenizer to track line/column/filename per token. Update the parser and interpreter to push/pop frames on function entry/exit, and add new Makefile targets (`demo-error-*`) plus test scripts (`test_error_stacktrace.rc`, `test_error_native.rc`, `test_error_undefined_var.rc`) and an `examples/error_handling_demo.rc` to demonstrate the new error reporting capabilities.
2025-11-24 00:59:54 +00:00
retoor 9a83539e33 feat: add eval stdlib module with dynamic expression evaluation and input validation tests 2025-11-24 00:46:47 +00:00
retoor e4bac92971 fix: add missing Le/Ge operators in relational parser and fix coroutine allocator off-by-one
- Add support for <= and >= relational operators in parser.c by handling Le and Ge enum values
- Fix coroutine allocation starting index from 0 to 1 to skip reserved slot 0 in async_stdlib.c
- Remove redundant coroutine deactivation in native_gather to prevent double-lock race condition
- Refactor native_fread to write directly to caller buffer instead of intermediate memory array
- Update feof test to perform read attempt before checking EOF flag for accurate state verification
- Correct replace() test expectation from single to all occurrences replacement behavior
2025-11-24 00:30:16 +00:00
retoor 6e6f522a55 feat: replace scattered legacy test files with unified pytest-based test suite for core modules 2025-11-23 23:50:23 +00:00
retoor 6bae549720 feat: add jest configuration and sample unit test for utils module 2025-11-23 22:46:02 +00:00
retoor 8c5de5ee9d feat: add object field mutation support with dot-assignment syntax and setter methods
Add parser support for direct field mutation via `obj.field = expr` syntax, enabling mutable object state after creation. Extend the OOP test suite with dedicated mutation tests (`oop_mutation_test.rc`) and advanced examples (`oop_advanced.rc`) covering BankAccount, Player, and Rectangle classes. Update `oop_working.rc` with mutation test cases and revise TUTORIAL.md documentation to describe field mutation, setter methods, and complete OOP examples with mutation. Add Makefile targets for running the new mutation and advanced tests.
2025-11-23 22:23:17 +00:00
retoor 0ea0f8e9eb chore: extract async, io, math, string, and constants into modular stdlib subdirectories 2025-11-23 22:03:19 +00:00
retoor 7b1ca4459c fix: replace unsafe strcpy/strcat with bounded strncpy/strncat and add null-termination in native_functions.c and string_utils.c
- Replace strcpy with strncpy + explicit null-termination in native_replace and concat_strings to prevent buffer overflows
- Add malloc failure checks for AsyncFileReadData and AsyncFileWriteData allocations in async_fread and async_fwrite
- Add null-pointer checks for async operation ID allocations with proper cleanup on failure
- Expand types.h with comprehensive system limits (MAX_IDENTIFIER_LEN, MAX_INCLUDE_DEPTH, MAX_PATH_LENGTH) and safety macros (ENSURE_TOKEN_VALID, ENSURE_MEMORY_VALID)
- Relocate class-related constants from Coroutine struct area to dedicated limits section for better organization
2025-11-23 21:14:15 +00:00
retoor a63dda5de3 docs: add OOP class system with object allocation, field/method lookup, and parser integration 2025-11-23 20:33:07 +00:00
retoor 3fab35d7cb feat: add async/await coroutine support with include system and file I/O to RC language
Add Python-like async/await functionality to the RC interpreter, enabling non-blocking coroutine execution via pthreads. Introduce `async` keyword for function declarations, `await()` for retrieving results, and `gather()` for parallel execution. Extend the tokenizer, parser, interpreter, and native functions with coroutine lifecycle management (init, alloc, thread execution, result retrieval). Also add include system with relative paths, nested includes up to 32 levels, and automatic circular include prevention. Document new features in TUTORIAL.md with examples (async_await_demo.rc) and tests (async_await_test.rc). Update types.h with Coroutine struct, MAX_COROUTINES constant, and new token types (Async, Await).
2025-11-23 19:30:37 +00:00
retoor 837e254ad6 chore: remove obsolete TODO2.md with outdated build tasks and notes 2025-11-23 14:59:21 +00:00
retoor 76b1da5351 chore: add initial CI workflow with build and test steps on push
Add .gitea/workflows/main.yml defining a Build and Test pipeline triggered on push events. The workflow checks out the repository, runs `make` to compile the project, and executes `make test` for automated testing on ubuntu-latest.
2025-11-23 14:45:39 +00:00
retoor 6a34114899 feat: implement include directive with path resolution and recursive rendering for modular template composition 2025-11-23 14:23:59 +00:00
retoor d669b1da3d feat: add return_flag and params_start for proper function return handling and parameter scanning
Implement a dedicated return_flag variable to replace the magic number -999 for return statements, enabling correct early exit from nested blocks. Add params_start field to Func struct to track parameter declaration positions, allowing the interpreter to properly map function arguments to local variables during function calls. Extend the test suite with new test files for arrays, comparison operators, logical operators, pointers, functions, trigonometry, and file seek operations, along with corresponding Makefile targets to execute them.
2025-11-23 14:07:19 +00:00
retoor 79a06a0be8 feat: add async I/O, double type, file I/O, and break/continue to RC interpreter
Implement multi-threaded async file and socket operations with pthread-based worker pool, add double floating-point type with conversion and arithmetic functions, introduce file I/O system with fopen/fclose/fread/fwrite/fgets/fputs, and add break/continue control flow statements. Update Makefile with new test and example targets, extend README and TUTORIAL.md with comprehensive documentation and usage examples, and include async_demo.rc example and async_io_test.rc test suite.
2025-11-23 13:30:46 +00:00
retoor 03bca46d88 fix: prevent infinite loop in session renewal by skipping block on break/continue/return 2025-11-23 12:53:36 +00:00
retoor 67c15f026d docs: add increment/decrement operator section with pre/post examples to TUTORIAL.md table of contents and body 2025-11-22 23:19:52 +00:00
retoor eb3d29ac8d chore: add increment/decrement operators and test suite to parser and tokenizer
Implement prefix and postfix ++/-- operators in the tokenizer (Inc/Dec token types) and parser (factor/unary functions) with correct pre/post semantics. Add increment_decrement_test.rc covering all four operator forms. Update Makefile to include the new test file in TESTS, add run-increment-decrement-test target, and fix .PHONY and TARGET dependency on dirs.
2025-11-22 23:17:01 +00:00
retoor 82722a757a feat: add comprehensive interactive tutorial with 15-section code walkthrough for RC language beginners 2025-11-22 22:28:28 +00:00
retoor b89a794817 chore: add bounds checks and null pointer guards across interpreter, parser, and native functions 2025-11-22 22:15:15 +00:00
retoor 5ceaf35d5a chore: rename example files from generic names to kebab-case http variants in Makefile and README 2025-11-22 21:30:55 +00:00
retoor b5b720d993 chore: remove binary tokens.txt file from repository tracking 2025-11-22 21:24:53 +00:00
retoor 1561e6a542 chore: remove all test source files and binary from repository 2025-11-22 21:24:34 +00:00
retoor dc91958deb chore: restructure project into src/tests/examples directories with modular build system 2025-11-22 21:22:43 +00:00
retoor 586a171d86 chore: add Makefile, main2.c interpreter, and sample HTTP server C files 2025-11-22 15:53:39 +00:00
21 changed files with 1975 additions and 316 deletions
+25 -12
View File
@@ -22,6 +22,8 @@ SOURCES = $(SRC_DIR)/main.c \
$(SRC_DIR)/error.c \
$(SRC_DIR)/context.c \
$(SRC_DIR)/debug.c \
$(SRC_DIR)/memory.c \
$(SRC_DIR)/scope.c \
$(SRC_DIR)/stdlib/stdlib.c \
$(SRC_DIR)/stdlib/string/string_stdlib.c \
$(SRC_DIR)/stdlib/math/math_stdlib.c \
@@ -30,6 +32,7 @@ SOURCES = $(SRC_DIR)/main.c \
$(SRC_DIR)/stdlib/async/async_stdlib.c \
$(SRC_DIR)/stdlib/constants/constants_stdlib.c \
$(SRC_DIR)/stdlib/eval/eval_stdlib.c \
$(SRC_DIR)/stdlib/benchmark/benchmark_stdlib.c \
OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
@@ -41,6 +44,7 @@ TESTS = $(TEST_DIR)/unittest.rc \
$(TEST_DIR)/test_stdlib_socket.rc \
$(TEST_DIR)/test_stdlib_async.rc \
$(TEST_DIR)/test_stdlib_constants.rc \
$(TEST_DIR)/test_stdlib_benchmark.rc \
$(TEST_DIR)/test_oop.rc \
$(TEST_DIR)/test_preprocessor.rc \
$(TEST_DIR)/test_eval.rc \
@@ -64,6 +68,7 @@ dirs:
@mkdir -p $(BUILD_DIR)/stdlib/async
@mkdir -p $(BUILD_DIR)/stdlib/constants
@mkdir -p $(BUILD_DIR)/stdlib/eval
@mkdir -p $(BUILD_DIR)/stdlib/benchmark
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
@mkdir -p $(dir $@)
@@ -80,37 +85,40 @@ test: $(TARGET)
@echo " RC LANGUAGE - COMPREHENSIVE TEST SUITE"
@echo "================================================================"
@echo ""
@echo "[1/11] Running Language Features Tests..."
@echo "[1/12] Running Language Features Tests..."
@$(TARGET) $(TEST_DIR)/test_language_features.rc
@echo ""
@echo "[2/11] Running String Standard Library Tests..."
@echo "[2/12] Running String Standard Library Tests..."
@$(TARGET) $(TEST_DIR)/test_stdlib_string.rc
@echo ""
@echo "[3/11] Running Math Standard Library Tests..."
@echo "[3/12] Running Math Standard Library Tests..."
@$(TARGET) $(TEST_DIR)/test_stdlib_math.rc
@echo ""
@echo "[4/11] Running I/O Standard Library Tests..."
@echo "[4/12] Running I/O Standard Library Tests..."
@$(TARGET) $(TEST_DIR)/test_stdlib_io.rc
@echo ""
@echo "[5/11] Running Socket Standard Library Tests..."
@echo "[5/12] Running Socket Standard Library Tests..."
@$(TARGET) $(TEST_DIR)/test_stdlib_socket.rc
@echo ""
@echo "[6/11] Running Async Standard Library Tests..."
@echo "[6/12] Running Async Standard Library Tests..."
@$(TARGET) $(TEST_DIR)/test_stdlib_async.rc
@echo ""
@echo "[7/11] Running Constants Standard Library Tests..."
@echo "[7/12] Running Constants Standard Library Tests..."
@$(TARGET) $(TEST_DIR)/test_stdlib_constants.rc
@echo ""
@echo "[8/11] Running OOP Features Tests..."
@echo "[8/12] Running Benchmark Standard Library Tests..."
@$(TARGET) $(TEST_DIR)/test_stdlib_benchmark.rc
@echo ""
@echo "[9/12] Running OOP Features Tests..."
@$(TARGET) $(TEST_DIR)/test_oop.rc
@echo ""
@echo "[9/11] Running Preprocessor Tests..."
@echo "[10/12] Running Preprocessor Tests..."
@$(TARGET) $(TEST_DIR)/test_preprocessor.rc
@echo ""
@echo "[10/11] Running Eval Function Tests..."
@echo "[11/12] Running Eval Function Tests..."
@$(TARGET) $(TEST_DIR)/test_eval.rc
@echo ""
@echo "[11/11] Running Input Validation Tests..."
@echo "[12/12] Running Input Validation Tests..."
@$(TARGET) $(TEST_DIR)/test_input_validation.rc
@echo ""
@echo "================================================================"
@@ -153,6 +161,9 @@ test-validation: $(TARGET)
test-constants: $(TARGET)
$(TARGET) $(TEST_DIR)/test_stdlib_constants.rc
test-benchmark: $(TARGET)
$(TARGET) $(TEST_DIR)/test_stdlib_benchmark.rc
test-robustness: $(TARGET)
$(TARGET) $(TEST_DIR)/test_robustness.rc
@@ -187,7 +198,7 @@ help:
@echo ""
@echo "Usage:"
@echo " make - Build the interpreter"
@echo " make test - Run ALL comprehensive tests (250+ tests)"
@echo " make test - Run ALL comprehensive tests (300+ tests)"
@echo " make test-smoke - Run quick smoke test (31 tests)"
@echo " make clean - Remove all build artifacts"
@echo ""
@@ -199,6 +210,7 @@ help:
@echo " make test-socket - Socket stdlib (19 tests)"
@echo " make test-async - Async stdlib (37 tests)"
@echo " make test-constants - Constants stdlib (13 tests)"
@echo " make test-benchmark - Benchmark stdlib (50 tests)"
@echo " make test-oop - OOP features (31 tests)"
@echo " make test-preprocessor - Preprocessor (2 tests)"
@echo " make test-eval - Eval function (30 tests)"
@@ -224,6 +236,7 @@ help:
@echo " stdlib/io/ - I/O functions"
@echo " stdlib/async/ - Async functions"
@echo " stdlib/eval/ - Eval function"
@echo " stdlib/benchmark/ - Benchmarking functions"
@echo " tests/ - Test programs (unittest framework)"
@echo " examples/ - Example programs (.rc files)"
@echo " build/ - Compiled object files"
+330 -1
View File
@@ -31,6 +31,7 @@ Complete guide to the RC (Retoor's C) programming language - a C-like interprete
20. [Async/Await (Python-like)](#asyncawait-python-like)
21. [Object-Oriented Programming](#object-oriented-programming)
22. [Async I/O (Low-level)](#async-io-low-level)
23. [Benchmarking](#benchmarking)
---
@@ -622,6 +623,33 @@ int main() {
}
```
### Random Number Functions
**rand()** - Returns a pseudo-random integer.
**srand(seed)** - Seeds the random number generator.
**rand_range(min, max)** - Returns a random integer in the range [min, max].
**time()** - Returns current Unix timestamp (seconds since epoch).
```c
int main() {
srand(time());
printf("Random: %d\n", rand());
printf("Random: %d\n", rand());
printf("Random: %d\n", rand());
printf("Range [1-10]: %d\n", rand_range(1, 10));
printf("Range [1-10]: %d\n", rand_range(1, 10));
printf("Timestamp: %d\n", time());
return 0;
}
```
## String Manipulation Functions
### strpos(haystack, needle) - Find Substring
@@ -1376,7 +1404,7 @@ Assignment: =
**String:** strlen, strpos, substr, upper, lower, strip, replace, startswith, endswith
**Math:** sqrt, pow, sin, cos, tan, abs, floor, ceil
**Math:** sqrt, pow, sin, cos, tan, abs, floor, ceil, rand, srand, rand_range, time
**File I/O:** fopen, fclose, fread, fwrite, fgets, fputs, fseek, ftell, feof, fremove, frename
@@ -1390,6 +1418,8 @@ Assignment: =
**Constants:** AF_INET, SOCK_STREAM, SEEK_SET, SEEK_CUR, SEEK_END
**Benchmarking:** bench_start, bench_end, bench_format, bench_format_auto, bench_reset
## File I/O
### Writing to a File
@@ -2094,3 +2124,302 @@ int main() {
}
```
---
## Benchmarking
RC provides a high-precision benchmarking system for measuring code execution time with nanosecond accuracy.
### Starting a Timer
Use `bench_start()` to begin timing code execution:
```c
int main() {
int timer = bench_start();
int i = 0;
int sum = 0;
while (i < 1000) {
sum = sum + i;
i = i + 1;
}
int elapsed = bench_end(timer);
printf("Elapsed: %d nanoseconds\n", elapsed);
return 0;
}
```
### Formatting Time
Use `bench_format()` to format time in specific units:
```c
int main() {
int timer = bench_start();
int result = fibonacci(20);
int elapsed = bench_end(timer);
printf("Nanoseconds: %s\n", bench_format(elapsed, 0));
printf("Microseconds: %s\n", bench_format(elapsed, 1));
printf("Milliseconds: %s\n", bench_format(elapsed, 2));
printf("Seconds: %s\n", bench_format(elapsed, 3));
printf("Minutes: %s\n", bench_format(elapsed, 4));
printf("Hours: %s\n", bench_format(elapsed, 5));
return 0;
}
```
**Time Units:**
- 0 = nanoseconds (ns)
- 1 = microseconds (us)
- 2 = milliseconds (ms)
- 3 = seconds (s)
- 4 = minutes (min)
- 5 = hours (h)
### Automatic Formatting
Use `bench_format_auto()` to automatically select the best time unit:
```c
int main() {
int timer = bench_start();
int i = 0;
while (i < 10000) {
i = i + 1;
}
int elapsed = bench_end(timer);
char* formatted = bench_format_auto(elapsed);
printf("Time: %s\n", formatted);
return 0;
}
```
### Multiple Timers
You can use multiple timers concurrently:
```c
int main() {
int timer1 = bench_start();
int timer2 = bench_start();
int timer3 = bench_start();
int result1 = compute_task1();
int elapsed1 = bench_end(timer1);
int result2 = compute_task2();
int elapsed2 = bench_end(timer2);
int result3 = compute_task3();
int elapsed3 = bench_end(timer3);
printf("Task 1: %s\n", bench_format_auto(elapsed1));
printf("Task 2: %s\n", bench_format_auto(elapsed2));
printf("Task 3: %s\n", bench_format_auto(elapsed3));
return 0;
}
```
### Nested Timing
Timers can be nested to measure sub-operations:
```c
int main() {
int outer_timer = bench_start();
printf("Starting outer operation\n");
int inner_timer = bench_start();
int partial_result = compute_part1();
int inner_elapsed = bench_end(inner_timer);
printf("Part 1: %s\n", bench_format_auto(inner_elapsed));
int final_result = compute_part2(partial_result);
int outer_elapsed = bench_end(outer_timer);
printf("Total: %s\n", bench_format_auto(outer_elapsed));
return 0;
}
```
### Comparing Algorithms
Benchmark different approaches:
```c
int approach1(int n) {
int sum = 0;
int i = 0;
while (i < n) {
sum = sum + i;
i = i + 1;
}
return sum;
}
int approach2(int n) {
return (n * (n - 1)) / 2;
}
int main() {
int runs = 100;
int timer1 = bench_start();
int i = 0;
while (i < runs) {
int result = approach1(1000);
i = i + 1;
}
int time1 = bench_end(timer1);
int timer2 = bench_start();
i = 0;
while (i < runs) {
int result = approach2(1000);
i = i + 1;
}
int time2 = bench_end(timer2);
printf("Approach 1: %s\n", bench_format_auto(time1));
printf("Approach 2: %s\n", bench_format_auto(time2));
int ratio = time1 / time2;
printf("Approach 2 is %dx faster\n", ratio);
return 0;
}
```
### Resetting Timers
Use `bench_reset()` to reset all timers:
```c
int main() {
int timer1 = bench_start();
int timer2 = bench_start();
bench_reset();
int timer3 = bench_start();
return 0;
}
```
### Performance Profiling
Create a performance profile of your application:
```c
int main() {
printf("=== Performance Profile ===\n");
int init_timer = bench_start();
initialize_data();
int init_time = bench_end(init_timer);
printf("Initialization: %s\n", bench_format_auto(init_time));
int process_timer = bench_start();
process_data();
int process_time = bench_end(process_timer);
printf("Processing: %s\n", bench_format_auto(process_time));
int save_timer = bench_start();
save_results();
int save_time = bench_end(save_timer);
printf("Saving: %s\n", bench_format_auto(save_time));
int total = init_time + process_time + save_time;
printf("Total: %s\n", bench_format_auto(total));
return 0;
}
```
### Best Practices
1. **Run multiple iterations** for more accurate measurements
2. **Warm up** before benchmarking to account for caching effects
3. **Use nested timers** to identify bottlenecks
4. **Compare relative performance** rather than absolute numbers
5. **Document benchmark conditions** (input size, hardware, etc.)
### Limitations
- Maximum 1000 concurrent timers
- Nanosecond precision depends on system clock
- Timers use CLOCK_MONOTONIC for accuracy
- Thread-safe timer management
### Complete Example
```c
int fibonacci(int n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
int compute_sum_of_squares(int limit) {
int sum = 0;
int i = 1;
while (i <= limit) {
sum = sum + (i * i);
i = i + 1;
}
return sum;
}
int main() {
printf("=== Benchmarking Demo ===\n\n");
printf("Test 1: Simple Loop\n");
int timer1 = bench_start();
int i = 0;
int sum = 0;
while (i < 1000) {
sum = sum + i;
i = i + 1;
}
int elapsed1 = bench_end(timer1);
printf("Result: %d\n", sum);
printf("Time: %s\n\n", bench_format_auto(elapsed1));
printf("Test 2: Fibonacci\n");
int timer2 = bench_start();
int fib_result = fibonacci(20);
int elapsed2 = bench_end(timer2);
printf("Result: %d\n", fib_result);
printf("Time: %s\n\n", bench_format_auto(elapsed2));
printf("Test 3: Sum of Squares\n");
int timer3 = bench_start();
int sum_result = compute_sum_of_squares(100);
int elapsed3 = bench_end(timer3);
printf("Result: %d\n", sum_result);
printf("Time: %s\n\n", bench_format_auto(elapsed3));
printf("=== Complete ===\n");
return 0;
}
```
For more benchmarking examples, see `examples/benchmark_demo.rc`.
+233
View File
@@ -0,0 +1,233 @@
int modulo(int a, int b) {
return a - (a / b) * b;
}
int fibonacci_recursive(int n) {
if (n <= 1) { return n; }
return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2);
}
int loop_iteration(int iterations) {
int total = 0;
int i = 0;
while (i < iterations) {
total = total + i * 2;
i = i + 1;
}
return total;
}
int list_operations(int iterations) {
int sum = 0;
int i = 0;
int val = 0;
while (i < iterations) {
if (modulo(i, 3) == 0) {
val = i * 2;
sum = sum + val;
}
i = i + 1;
}
return sum;
}
int string_concatenation(int iterations) {
char *result = "";
int i = 0;
while (i < iterations) {
result = result + "x";
i = i + 1;
}
return strlen(result);
}
class BenchItem {
int id = 0;
int value = 0;
int active = 0;
}
int dict_creation(int iterations) {
int sum = 0;
int i = 0;
int obj = 0;
while (i < iterations) {
obj = new BenchItem();
obj.id = i;
obj.value = i * 2;
obj.active = modulo(i, 2);
sum = sum + obj.value;
i = i + 1;
}
return sum;
}
int sorting_algorithm(int iterations) {
int arr[1000];
int i = 0;
int j = 0;
int limit = 0;
int left = 0;
int right = 0;
if (iterations > 1000) { iterations = 1000; }
while (i < iterations) {
arr[i] = rand_range(0, 1000);
i = i + 1;
}
i = 0;
while (i < iterations - 1) {
j = 0;
limit = iterations - i - 1;
while (j < limit) {
left = arr[j];
right = arr[j + 1];
if (left > right) {
arr[j] = right;
arr[j + 1] = left;
}
j = j + 1;
}
i = i + 1;
}
return arr[0];
}
int math_operations(int iterations) {
int total = 0;
int i = 0;
int sq = 0;
int s = 0;
int c = 0;
while (i < iterations) {
sq = sqrt(i);
s = sin(i);
c = cos(i);
total = total + sq * s / 1000000 * c / 1000000;
i = i + 1;
}
return total;
}
int main() {
srand(time());
int iterations = 1000;
int fib_n = 20;
int timer = 0;
int t = 0;
int r = 0;
int total_time = 0;
int durations[7];
char *names[7];
int min_idx = 0;
int max_idx = 0;
int min_val = 0;
int max_val = 0;
int i = 0;
int d = 0;
names[0] = "Fibonacci Recursive";
names[1] = "Loop Iteration";
names[2] = "List Operations";
names[3] = "String Concatenation";
names[4] = "Dict Creation";
names[5] = "Sorting Algorithm";
names[6] = "Math Operations";
printf("\n======================================================================\n");
printf("RC PERFORMANCE BENCHMARK\n");
printf("======================================================================\n");
printf("Iterations: %d | Fibonacci N: %d\n", iterations, fib_n);
printf("======================================================================\n\n");
printf("Running: Fibonacci Recursive... ");
timer = bench_start();
r = fibonacci_recursive(fib_n);
t = bench_end(timer);
durations[0] = t;
total_time = total_time + t;
printf("%s\n", bench_format(t, 2));
printf(" Result: %d\n\n", r);
printf("Running: Loop Iteration... ");
timer = bench_start();
r = loop_iteration(iterations);
t = bench_end(timer);
durations[1] = t;
total_time = total_time + t;
printf("%s\n", bench_format(t, 2));
printf(" Result: %d\n\n", r);
printf("Running: List Operations... ");
timer = bench_start();
r = list_operations(iterations);
t = bench_end(timer);
durations[2] = t;
total_time = total_time + t;
printf("%s\n", bench_format(t, 2));
printf(" Result: %d\n\n", r);
printf("Running: String Concatenation... ");
timer = bench_start();
r = string_concatenation(iterations);
t = bench_end(timer);
durations[3] = t;
total_time = total_time + t;
printf("%s\n", bench_format(t, 2));
printf(" Result: %d\n\n", r);
printf("Running: Dict Creation... ");
timer = bench_start();
r = dict_creation(iterations);
t = bench_end(timer);
durations[4] = t;
total_time = total_time + t;
printf("%s\n", bench_format(t, 2));
printf(" Result: %d\n\n", r);
printf("Running: Sorting Algorithm... ");
timer = bench_start();
r = sorting_algorithm(iterations);
t = bench_end(timer);
durations[5] = t;
total_time = total_time + t;
printf("%s\n", bench_format(t, 2));
printf(" Result: %d\n\n", r);
printf("Running: Math Operations... ");
timer = bench_start();
r = math_operations(iterations);
t = bench_end(timer);
durations[6] = t;
total_time = total_time + t;
printf("%s\n", bench_format(t, 2));
printf(" Result: %d\n\n", r);
min_idx = 0;
max_idx = 0;
min_val = durations[0];
max_val = durations[0];
i = 1;
while (i < 7) {
d = durations[i];
if (d < min_val) {
min_val = d;
min_idx = i;
}
if (d > max_val) {
max_val = d;
max_idx = i;
}
i = i + 1;
}
printf("======================================================================\n");
printf("SUMMARY\n");
printf("======================================================================\n");
printf("Total Time: %s\n", bench_format(total_time, 2));
printf("Fastest Test: %s (%s)\n", names[min_idx], bench_format(durations[min_idx], 2));
printf("Slowest Test: %s (%s)\n", names[max_idx], bench_format(durations[max_idx], 2));
printf("======================================================================\n\n");
return 0;
}
+144
View File
@@ -0,0 +1,144 @@
int fibonacci(int n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
int compute_sum_of_squares(int limit) {
int sum = 0;
int i = 1;
while (i <= limit) {
sum = sum + (i * i);
i = i + 1;
}
return sum;
}
int main() {
printf("=================================================\n");
printf(" RC Language - Benchmarking Demo\n");
printf("=================================================\n\n");
printf("This demo shows how to use the benchmarking stdlib\n");
printf("to measure execution time of different operations.\n\n");
printf("=================================================\n");
printf("Test 1: Simple Loop (1000 iterations)\n");
printf("=================================================\n");
int timer1 = bench_start();
int i = 0;
int sum = 0;
while (i < 1000) {
sum = sum + i;
i = i + 1;
}
int elapsed1 = bench_end(timer1);
char* formatted1 = bench_format_auto(elapsed1);
printf("Result: sum = %d\n", sum);
printf("Time: %s (%d nanoseconds)\n\n", formatted1, elapsed1);
printf("=================================================\n");
printf("Test 2: Fibonacci Calculation (fib(20))\n");
printf("=================================================\n");
int timer2 = bench_start();
int fib_result = fibonacci(20);
int elapsed2 = bench_end(timer2);
char* formatted2 = bench_format_auto(elapsed2);
printf("Result: fibonacci(20) = %d\n", fib_result);
printf("Time: %s (%d nanoseconds)\n\n", formatted2, elapsed2);
printf("=================================================\n");
printf("Test 3: Sum of Squares (1 to 100)\n");
printf("=================================================\n");
int timer3 = bench_start();
int sum_result = compute_sum_of_squares(100);
int elapsed3 = bench_end(timer3);
char* formatted3 = bench_format_auto(elapsed3);
printf("Result: Sum of squares = %d\n", sum_result);
printf("Time: %s (%d nanoseconds)\n\n", formatted3, elapsed3);
printf("=================================================\n");
printf("Test 4: Nested Timers\n");
printf("=================================================\n");
int outer_timer = bench_start();
printf("Outer operation started...\n");
int inner_timer = bench_start();
int k = 0;
int product = 1;
while (k < 50) {
product = product + k;
k = k + 1;
}
int inner_elapsed = bench_end(inner_timer);
printf("Inner operation completed in: %s\n", bench_format_auto(inner_elapsed));
int m = 0;
while (m < 100) {
m = m + 1;
}
int outer_elapsed = bench_end(outer_timer);
printf("Outer operation completed in: %s\n\n", bench_format_auto(outer_elapsed));
printf("=================================================\n");
printf("Test 5: Manual Format with Different Units\n");
printf("=================================================\n");
int sample_time = 1234567890;
printf("Time: %d nanoseconds\n", sample_time);
printf(" As nanoseconds: %s\n", bench_format(sample_time, 0));
printf(" As microseconds: %s\n", bench_format(sample_time, 1));
printf(" As milliseconds: %s\n", bench_format(sample_time, 2));
printf(" As seconds: %s\n", bench_format(sample_time, 3));
printf(" As minutes: %s\n", bench_format(sample_time, 4));
printf(" As hours: %s\n", bench_format(sample_time, 5));
printf(" Auto format: %s\n\n", bench_format_auto(sample_time));
printf("=================================================\n");
printf("Test 6: Comparison of Algorithms\n");
printf("=================================================\n");
printf("Computing fibonacci(15) multiple times...\n");
int timer_a = bench_start();
int result_a = fibonacci(15);
int elapsed_a = bench_end(timer_a);
int timer_b = bench_start();
int result_b = fibonacci(15);
int elapsed_b = bench_end(timer_b);
int timer_c = bench_start();
int result_c = fibonacci(15);
int elapsed_c = bench_end(timer_c);
printf("Run 1: %s (result: %d)\n", bench_format_auto(elapsed_a), result_a);
printf("Run 2: %s (result: %d)\n", bench_format_auto(elapsed_b), result_b);
printf("Run 3: %s (result: %d)\n", bench_format_auto(elapsed_c), result_c);
int avg_time = (elapsed_a + elapsed_b + elapsed_c) / 3;
printf("Average time: %s\n\n", bench_format_auto(avg_time));
printf("=================================================\n");
printf("Benchmarking Complete!\n");
printf("=================================================\n");
return 0;
}
+1
View File
@@ -1,6 +1,7 @@
#include <stdlib.h>
#include <string.h>
#include "types.h"
#include "scope.h"
extern long memory[MEM_SIZE];
extern int sp;
+31 -19
View File
@@ -7,6 +7,7 @@
#include "parser.h"
#include "error.h"
#include "debug.h"
#include "memory.h"
void error(char *msg) {
error_with_context("ParseError", msg, NULL);
@@ -111,29 +112,38 @@ void statement() {
Token *t = &tokens[pc];
match(Id);
if (loc_cnt >= VAR_MAX) {
DEBUG_LOG("ERROR: Too many local variables: loc_cnt=%d (max=%d)", loc_cnt, VAR_MAX);
char var_name[64];
int name_len = t->val;
if (name_len < 0) name_len = 0;
if (name_len > 31) name_len = 31;
strncpy(var_name, t->text, name_len);
var_name[name_len] = 0;
char alloc_ctx[128];
snprintf(alloc_ctx, sizeof(alloc_ctx), "declare variable '%s'", var_name);
if (!mem_check_loc_cnt(loc_cnt + 1, alloc_ctx)) {
error("Too many local variables");
}
if (sp >= MEM_SIZE) {
DEBUG_LOG("ERROR: Stack overflow: sp=%d (max=%d)", sp, MEM_SIZE);
DEBUG_STACK_STATE();
if (!mem_check_sp(sp + 1, alloc_ctx)) {
error("Stack overflow");
}
int addr = sp;
Symbol *s = &locals[loc_cnt++];
if (!t->text) {
error("Invalid token text");
}
int name_len = t->val;
if (name_len < 0) name_len = 0;
if (name_len > 31) name_len = 31;
strncpy(s->name, t->text, name_len);
s->name[name_len] = 0;
strncpy(s->name, var_name, 32);
s->name[31] = 0;
s->type = var_type;
s->addr = addr;
s->is_array = 0;
if (loc_cnt > mem_stats.loc_cnt_high_water) {
mem_stats.loc_cnt_high_water = loc_cnt;
}
if (pc < MAX_TOK && pc < tk_idx && tokens[pc].type == '[') {
pc++;
long size_val = expression();
@@ -148,27 +158,29 @@ void statement() {
int size = (int)size_val;
s->is_array = 1;
if (sp + size >= MEM_SIZE) {
snprintf(alloc_ctx, sizeof(alloc_ctx), "allocate array '%s[%d]'", var_name, size);
if (!mem_check_sp(sp + size, alloc_ctx)) {
error("Stack overflow during array allocation");
}
if (sp + size < sp) {
error("Integer overflow in array allocation");
}
sp += size;
mem_stats.total_allocations += size;
} else {
if (sp + 1 >= MEM_SIZE) {
error("Stack overflow during variable allocation");
}
sp++;
mem_stats.total_allocations++;
}
if (sp > mem_stats.sp_high_water) {
mem_stats.sp_high_water = sp;
}
if (pc < MAX_TOK && pc < tk_idx && tokens[pc].type == '=') {
pc++;
if (addr >= 0 && addr < MEM_SIZE) {
memory[addr] = expression();
} else {
error("Memory access out of bounds");
}
snprintf(alloc_ctx, sizeof(alloc_ctx), "initialize '%s'", var_name);
mem_write(addr, expression(), alloc_ctx);
}
if (pc < MAX_TOK && pc < tk_idx && tokens[pc].type == ',') pc++;
}
+5
View File
@@ -10,10 +10,14 @@
#include "preprocessor.h"
#include "oop.h"
#include "debug.h"
#include "memory.h"
#include "scope.h"
int main(int argc, char **argv) {
DEBUG_INIT();
DEBUG_LOG("RC Interpreter starting");
mem_init();
scope_init();
pthread_rwlock_init(&tokens_rwlock, NULL);
pthread_mutex_init(&str_pool_mutex, NULL);
@@ -74,6 +78,7 @@ int main(int argc, char **argv) {
statement();
free(src_code);
scope_cleanup();
pthread_rwlock_destroy(&tokens_rwlock);
pthread_mutex_destroy(&str_pool_mutex);
pthread_mutex_destroy(&memory_mutex);
+267
View File
@@ -0,0 +1,267 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "memory.h"
#include "types.h"
#include "error.h"
MemoryStats mem_stats = {0, 0, 0, 0, 0, 0};
extern long memory[];
extern int sp;
extern int bp;
extern Symbol locals[];
extern int loc_cnt;
extern int pc;
extern long ax;
static void mem_error(const char *msg, const char *context) {
fprintf(stderr, "\n");
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
fprintf(stderr, "║ MEMORY ERROR DETECTED ║\n");
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
fprintf(stderr, "\n Error: %s\n", msg);
fprintf(stderr, " Context: %s\n", context);
fprintf(stderr, "\n");
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
fprintf(stderr, "║ MEMORY STATE ║\n");
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
fprintf(stderr, " Stack Pointer (sp): %d\n", sp);
fprintf(stderr, " Base Pointer (bp): %d\n", bp);
fprintf(stderr, " Program Counter (pc): %d\n", pc);
fprintf(stderr, " Local Count (loc_cnt): %d\n", loc_cnt);
fprintf(stderr, " Accumulator (ax): %ld\n", ax);
fprintf(stderr, "\n");
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
fprintf(stderr, "║ MEMORY LIMITS ║\n");
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
fprintf(stderr, " MEM_SIZE: %d\n", MEM_SIZE);
fprintf(stderr, " VAR_MAX: %d\n", VAR_MAX);
fprintf(stderr, " MAX_TOK: %d\n", MAX_TOK);
fprintf(stderr, "\n");
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
fprintf(stderr, "║ MEMORY STATISTICS ║\n");
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
fprintf(stderr, " SP High Water Mark: %d\n", mem_stats.sp_high_water);
fprintf(stderr, " LOC_CNT High Water: %d\n", mem_stats.loc_cnt_high_water);
fprintf(stderr, " Current Call Depth: %d\n", mem_stats.call_depth);
fprintf(stderr, " Max Call Depth: %d\n", mem_stats.max_call_depth);
fprintf(stderr, " Total Allocations: %d\n", mem_stats.total_allocations);
fprintf(stderr, " Total Deallocations: %d\n", mem_stats.total_deallocations);
fprintf(stderr, "\n");
if (loc_cnt > 0 && loc_cnt <= VAR_MAX) {
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
fprintf(stderr, "║ LOCAL VARIABLES ║\n");
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
int show_count = loc_cnt < 20 ? loc_cnt : 20;
for (int i = 0; i < show_count; i++) {
fprintf(stderr, " [%3d] %-20s addr=%5d is_array=%d",
i, locals[i].name, locals[i].addr, locals[i].is_array);
if (locals[i].addr >= 0 && locals[i].addr < MEM_SIZE) {
fprintf(stderr, " value=%ld", memory[locals[i].addr]);
}
fprintf(stderr, "\n");
}
if (loc_cnt > 20) {
fprintf(stderr, " ... and %d more variables\n", loc_cnt - 20);
}
fprintf(stderr, "\n");
}
if (sp > 0 && sp <= MEM_SIZE) {
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
fprintf(stderr, "║ STACK CONTENTS ║\n");
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
int start = sp > 20 ? sp - 20 : 0;
for (int i = start; i < sp && i < MEM_SIZE; i++) {
fprintf(stderr, " memory[%5d] = %ld", i, memory[i]);
if (i == bp) fprintf(stderr, " <-- bp");
if (i == sp - 1) fprintf(stderr, " <-- sp-1 (top)");
fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
}
exit(1);
}
void mem_init(void) {
mem_stats.sp_high_water = 0;
mem_stats.loc_cnt_high_water = 0;
mem_stats.call_depth = 0;
mem_stats.max_call_depth = 0;
mem_stats.total_allocations = 0;
mem_stats.total_deallocations = 0;
}
void mem_dump_stats(void) {
fprintf(stderr, "\n=== Memory Statistics ===\n");
fprintf(stderr, "SP High Water: %d / %d (%.1f%%)\n",
mem_stats.sp_high_water, MEM_SIZE,
100.0 * mem_stats.sp_high_water / MEM_SIZE);
fprintf(stderr, "LOC_CNT High: %d / %d (%.1f%%)\n",
mem_stats.loc_cnt_high_water, VAR_MAX,
100.0 * mem_stats.loc_cnt_high_water / VAR_MAX);
fprintf(stderr, "Max Call Depth: %d\n", mem_stats.max_call_depth);
fprintf(stderr, "Allocations: %d\n", mem_stats.total_allocations);
fprintf(stderr, "Deallocations: %d\n", mem_stats.total_deallocations);
fprintf(stderr, "=========================\n");
}
int mem_check_sp(int required_sp, const char *context) {
if (required_sp < 0) {
char msg[256];
snprintf(msg, sizeof(msg), "Stack underflow: required sp=%d", required_sp);
mem_error(msg, context);
return 0;
}
if (required_sp >= MEM_SIZE) {
char msg[256];
snprintf(msg, sizeof(msg), "Stack overflow: required sp=%d >= MEM_SIZE=%d",
required_sp, MEM_SIZE);
mem_error(msg, context);
return 0;
}
return 1;
}
int mem_check_addr(int addr, const char *context) {
if (addr < 0) {
char msg[256];
snprintf(msg, sizeof(msg), "Negative memory address: addr=%d", addr);
mem_error(msg, context);
return 0;
}
if (addr >= MEM_SIZE) {
char msg[256];
snprintf(msg, sizeof(msg), "Memory address out of bounds: addr=%d >= MEM_SIZE=%d",
addr, MEM_SIZE);
mem_error(msg, context);
return 0;
}
return 1;
}
int mem_check_loc_cnt(int required_cnt, const char *context) {
if (required_cnt < 0) {
char msg[256];
snprintf(msg, sizeof(msg), "Negative local count: loc_cnt=%d", required_cnt);
mem_error(msg, context);
return 0;
}
if (required_cnt >= VAR_MAX) {
char msg[256];
snprintf(msg, sizeof(msg), "Too many local variables: loc_cnt=%d >= VAR_MAX=%d",
required_cnt, VAR_MAX);
mem_error(msg, context);
return 0;
}
return 1;
}
long mem_read(int addr, const char *context) {
if (!mem_check_addr(addr, context)) {
return 0;
}
return memory[addr];
}
void mem_write(int addr, long value, const char *context) {
if (!mem_check_addr(addr, context)) {
return;
}
memory[addr] = value;
}
int mem_push(long value, const char *context) {
if (!mem_check_sp(sp + 1, context)) {
return 0;
}
memory[sp++] = value;
mem_stats.total_allocations++;
if (sp > mem_stats.sp_high_water) {
mem_stats.sp_high_water = sp;
}
return 1;
}
long mem_pop(const char *context) {
if (sp <= 0) {
mem_error("Stack underflow on pop", context);
return 0;
}
mem_stats.total_deallocations++;
return memory[--sp];
}
void mem_alloc_var(int size, const char *var_name) {
char context[256];
snprintf(context, sizeof(context), "Allocating variable '%s' (size=%d)", var_name, size);
if (!mem_check_sp(sp + size, context)) {
return;
}
if (!mem_check_loc_cnt(loc_cnt + 1, context)) {
return;
}
sp += size;
mem_stats.total_allocations += size;
if (sp > mem_stats.sp_high_water) {
mem_stats.sp_high_water = sp;
}
if (loc_cnt > mem_stats.loc_cnt_high_water) {
mem_stats.loc_cnt_high_water = loc_cnt;
}
}
void mem_free_frame(int old_sp, int old_loc_cnt, const char *context) {
if (old_sp < 0 || old_sp > sp) {
char msg[256];
snprintf(msg, sizeof(msg), "Invalid frame restoration: old_sp=%d, current sp=%d",
old_sp, sp);
mem_error(msg, context);
return;
}
if (old_loc_cnt < 0 || old_loc_cnt > loc_cnt) {
char msg[256];
snprintf(msg, sizeof(msg), "Invalid loc_cnt restoration: old_loc_cnt=%d, current loc_cnt=%d",
old_loc_cnt, loc_cnt);
mem_error(msg, context);
return;
}
mem_stats.total_deallocations += (sp - old_sp);
sp = old_sp;
loc_cnt = old_loc_cnt;
}
void mem_enter_call(const char *func_name) {
mem_stats.call_depth++;
if (mem_stats.call_depth > mem_stats.max_call_depth) {
mem_stats.max_call_depth = mem_stats.call_depth;
}
if (mem_stats.call_depth > MAX_CALL_STACK) {
char msg[256];
snprintf(msg, sizeof(msg), "Call stack overflow entering '%s'", func_name);
mem_error(msg, "mem_enter_call");
}
}
void mem_exit_call(const char *func_name) {
if (mem_stats.call_depth <= 0) {
char msg[256];
snprintf(msg, sizeof(msg), "Call stack underflow exiting '%s'", func_name);
mem_error(msg, "mem_exit_call");
return;
}
mem_stats.call_depth--;
}
void mem_trace_state(const char *label) {
fprintf(stderr, "[TRACE:%s] sp=%d bp=%d loc_cnt=%d call_depth=%d\n",
label, sp, bp, loc_cnt, mem_stats.call_depth);
}
+38
View File
@@ -0,0 +1,38 @@
#ifndef MEMORY_H
#define MEMORY_H
#include "types.h"
typedef struct {
int sp_high_water;
int loc_cnt_high_water;
int call_depth;
int max_call_depth;
int total_allocations;
int total_deallocations;
} MemoryStats;
extern MemoryStats mem_stats;
void mem_init(void);
void mem_dump_stats(void);
int mem_check_sp(int required_sp, const char *context);
int mem_check_addr(int addr, const char *context);
int mem_check_loc_cnt(int required_cnt, const char *context);
long mem_read(int addr, const char *context);
void mem_write(int addr, long value, const char *context);
int mem_push(long value, const char *context);
long mem_pop(const char *context);
void mem_alloc_var(int size, const char *var_name);
void mem_free_frame(int old_sp, int old_loc_cnt, const char *context);
void mem_enter_call(const char *func_name);
void mem_exit_call(const char *func_name);
void mem_trace_state(const char *label);
#endif
+9 -17
View File
@@ -3,6 +3,7 @@
#include <string.h>
#include "types.h"
#include "interpreter.h"
#include "scope.h"
int find_class(char *name, int len) {
if (!name || len <= 0 || len > 31) return -1;
@@ -113,23 +114,15 @@ void call_destructor(long obj_ptr) {
int func_idx = cls->methods[i].func_idx;
if (func_idx >= 0 && func_idx < func_cnt) {
int saved_pc = pc;
int saved_sp = sp;
int saved_bp = bp;
int saved_loc_cnt = loc_cnt;
long saved_ax = ax;
int saved_return_flag = return_flag;
if (sp >= MEM_SIZE || bp < 0 || bp >= MEM_SIZE) return;
scope_push();
memory[sp] = bp;
bp = sp++;
if (sp >= MEM_SIZE) return;
memory[sp++] = 0;
memory[sp++] = saved_loc_cnt;
if (sp >= MEM_SIZE) return;
memory[sp++] = obj_ptr;
int param_base = sp;
if (sp < MEM_SIZE) {
memory[sp++] = obj_ptr;
}
int scan_pc = funcs[func_idx].params_start;
if (scan_pc < MAX_TOK && scan_pc < tk_idx && tokens[scan_pc].type != ')') {
@@ -142,7 +135,7 @@ void call_destructor(long obj_ptr) {
strncpy(sym->name, param_name->text, name_len);
sym->name[name_len] = 0;
sym->type = Int;
sym->addr = sp - 1;
sym->addr = param_base;
sym->is_array = 0;
}
}
@@ -154,10 +147,9 @@ void call_destructor(long obj_ptr) {
statement();
scope_pop();
pc = saved_pc;
sp = saved_sp;
bp = saved_bp;
loc_cnt = saved_loc_cnt;
ax = saved_ax;
return_flag = saved_return_flag;
}
+38 -44
View File
@@ -7,6 +7,8 @@
#include "string_utils.h"
#include "error.h"
#include "debug.h"
#include "memory.h"
#include "scope.h"
extern Token tokens[];
extern int pc;
@@ -176,20 +178,20 @@ long factor() {
}
int ret_pc = pc;
int old_loc_cnt = loc_cnt;
int old_bp = bp;
int saved_return_flag = return_flag;
if (sp >= MEM_SIZE) return 0;
if (bp < 0 || bp >= MEM_SIZE) return 0;
memory[sp] = bp; bp = sp++;
if (sp >= MEM_SIZE) return 0;
memory[sp++] = ret_pc;
memory[sp++] = old_loc_cnt;
mem_enter_call(funcs[f_idx].name);
scope_push();
char call_ctx[128];
snprintf(call_ctx, sizeof(call_ctx), "call %s: push frame", funcs[f_idx].name);
int param_base = sp;
for(int i=0; i<argc; i++) {
if (sp >= MEM_SIZE) break;
memory[sp++] = args[i];
if (sp < MEM_SIZE) {
memory[sp++] = args[i];
}
}
int scan_pc = funcs[f_idx].params_start;
@@ -198,17 +200,19 @@ long factor() {
if (tokens[scan_pc].type == Int || tokens[scan_pc].type == Char || tokens[scan_pc].type == Double) {
scan_pc++;
while (scan_pc < MAX_TOK && tokens[scan_pc].type == '*') scan_pc++;
if (scan_pc < MAX_TOK && tokens[scan_pc].type == Id && param_idx < argc && loc_cnt < VAR_MAX) {
Token *param_name = &tokens[scan_pc];
Symbol *sym = &locals[loc_cnt++];
int name_len = param_name->val;
if (name_len > 31) name_len = 31;
strncpy(sym->name, param_name->text, name_len);
sym->name[name_len] = 0;
sym->type = Int;
sym->addr = param_base + param_idx;
sym->is_array = 0;
param_idx++;
if (scan_pc < MAX_TOK && tokens[scan_pc].type == Id && param_idx < argc) {
if (loc_cnt < VAR_MAX) {
Token *param_name = &tokens[scan_pc];
Symbol *sym = &locals[loc_cnt++];
int name_len = param_name->val;
if (name_len > 31) name_len = 31;
strncpy(sym->name, param_name->text, name_len);
sym->name[name_len] = 0;
sym->type = Int;
sym->addr = param_base + param_idx;
sym->is_array = 0;
param_idx++;
}
}
}
scan_pc++;
@@ -231,14 +235,12 @@ long factor() {
return_flag = 0;
pop_call_frame();
mem_exit_call(funcs[f_idx].name);
scope_pop();
loc_cnt = old_loc_cnt;
if (bp < 0 || bp >= MEM_SIZE) return 0;
sp = bp;
if (sp < 0 || sp >= MEM_SIZE) return 0;
bp = memory[sp];
if (sp + 1 < MEM_SIZE) ret_pc = memory[sp + 1];
pc = ret_pc;
return_flag = saved_return_flag;
return val;
}
else {
@@ -427,22 +429,15 @@ long unary() {
match(')');
int saved_pc = pc;
int saved_sp = sp;
int saved_bp = bp;
int saved_loc_cnt = loc_cnt;
int saved_return_flag = return_flag;
if (sp >= MEM_SIZE || bp < 0 || bp >= MEM_SIZE) return 0;
memory[sp] = bp;
bp = sp++;
if (sp >= MEM_SIZE) return 0;
memory[sp++] = 0;
memory[sp++] = saved_loc_cnt;
scope_push();
int param_base = sp;
for (int i = 0; i < argc && i < 10; i++) {
if (sp >= MEM_SIZE) break;
memory[sp++] = args[i];
if (sp < MEM_SIZE) {
memory[sp++] = args[i];
}
}
int scan_pc = funcs[func_idx].params_start;
@@ -460,7 +455,7 @@ long unary() {
strncpy(sym->name, param_name->text, name_len);
sym->name[name_len] = 0;
sym->type = Int;
sym->addr = saved_sp + 3 + param_idx;
sym->addr = param_base + param_idx;
sym->is_array = 0;
param_idx++;
}
@@ -476,10 +471,9 @@ long unary() {
ax = 0;
return_flag = 0;
scope_pop();
pc = saved_pc;
sp = saved_sp;
bp = saved_bp;
loc_cnt = saved_loc_cnt;
return_flag = saved_return_flag;
} else {
extern int find_class_field(int class_idx, char *name, int len);
int field_idx = find_class_field(class_idx, member->text, member->val);
+363
View File
@@ -0,0 +1,363 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "scope.h"
#include "types.h"
#include "interpreter.h"
Scope *current_scope = NULL;
Scope *root_scope = NULL;
Scope* scope_create(Scope *parent) {
Scope *scope = (Scope*)calloc(1, sizeof(Scope));
if (!scope) return NULL;
scope->parent = parent;
scope->mem_size = SCOPE_MEM_SIZE;
scope->loc_capacity = SCOPE_VAR_MAX;
scope->memory = (long*)calloc(SCOPE_MEM_SIZE, sizeof(long));
if (!scope->memory) {
free(scope);
return NULL;
}
scope->locals = (Symbol*)calloc(SCOPE_VAR_MAX, sizeof(Symbol));
if (!scope->locals) {
free(scope->memory);
free(scope);
return NULL;
}
scope->sp = 0;
scope->bp = 0;
scope->loc_cnt = 0;
scope->depth = parent ? parent->depth + 1 : 0;
return scope;
}
void scope_destroy(Scope *scope) {
if (!scope) return;
if (scope->memory) free(scope->memory);
if (scope->locals) free(scope->locals);
free(scope);
}
Scope* scope_push(void) {
extern long memory[];
extern Symbol locals[];
extern int sp, bp, loc_cnt;
if (current_scope) {
int copy_sp = sp < current_scope->mem_size ? sp : current_scope->mem_size;
for (int i = 0; i < copy_sp; i++) {
current_scope->memory[i] = memory[i];
}
int copy_loc = loc_cnt < current_scope->loc_capacity ? loc_cnt : current_scope->loc_capacity;
for (int i = 0; i < copy_loc; i++) {
current_scope->locals[i] = locals[i];
}
current_scope->sp = sp;
current_scope->bp = bp;
current_scope->loc_cnt = loc_cnt;
}
Scope *new_scope = scope_create(current_scope);
if (!new_scope) {
error("Failed to create new scope");
return NULL;
}
current_scope = new_scope;
sp = 0;
bp = 0;
loc_cnt = 0;
return new_scope;
}
Scope* scope_pop(void) {
extern long memory[];
extern Symbol locals[];
extern int sp, bp, loc_cnt;
if (!current_scope || !current_scope->parent) {
return current_scope;
}
Scope *old = current_scope;
current_scope = current_scope->parent;
scope_destroy(old);
if (current_scope) {
int copy_sp = current_scope->sp < MEM_SIZE ? current_scope->sp : MEM_SIZE;
for (int i = 0; i < copy_sp; i++) {
memory[i] = current_scope->memory[i];
}
int copy_loc = current_scope->loc_cnt < VAR_MAX ? current_scope->loc_cnt : VAR_MAX;
for (int i = 0; i < copy_loc; i++) {
locals[i] = current_scope->locals[i];
}
sp = current_scope->sp;
bp = current_scope->bp;
loc_cnt = current_scope->loc_cnt;
}
return current_scope;
}
int scope_find_local_current_only(const char *name, int len) {
if (!current_scope || !name || len <= 0 || len >= 32) return -1;
Scope *scope = current_scope;
for (int i = scope->loc_cnt - 1; i >= 0; i--) {
if (!strncmp(scope->locals[i].name, name, len) && scope->locals[i].name[len] == 0) {
return i;
}
}
return -1;
}
static int scope_find_local_in_scope(Scope *scope, const char *name, int len) {
if (!scope || !name || len <= 0 || len >= 32) return -1;
for (int i = scope->loc_cnt - 1; i >= 0; i--) {
if (!strncmp(scope->locals[i].name, name, len) && scope->locals[i].name[len] == 0) {
return i;
}
}
return -1;
}
int scope_find_local(const char *name, int len) {
if (!name || len <= 0 || len >= 32) return -1;
Scope *scope = current_scope;
while (scope) {
int idx = scope_find_local_in_scope(scope, name, len);
if (idx >= 0) {
return idx;
}
scope = scope->parent;
}
return -1;
}
Symbol* scope_get_local(int idx) {
if (!current_scope || idx < 0) return NULL;
Scope *scope = current_scope;
while (scope) {
if (idx < scope->loc_cnt) {
return &scope->locals[idx];
}
scope = scope->parent;
}
return NULL;
}
Symbol* scope_add_local(const char *name, int len, int type, int addr, int is_array) {
if (!current_scope) return NULL;
if (current_scope->loc_cnt >= current_scope->loc_capacity) {
error("Too many local variables in scope");
return NULL;
}
Symbol *sym = &current_scope->locals[current_scope->loc_cnt++];
int name_len = len > 31 ? 31 : len;
strncpy(sym->name, name, name_len);
sym->name[name_len] = 0;
sym->type = type;
sym->addr = addr;
sym->is_array = is_array;
return sym;
}
long scope_mem_read(int addr) {
if (!current_scope) return 0;
if (addr < 0) return 0;
Scope *scope = current_scope;
while (scope) {
if (addr < scope->mem_size && addr < scope->sp) {
return scope->memory[addr];
}
if (addr < scope->mem_size) {
return scope->memory[addr];
}
scope = scope->parent;
}
if (addr < current_scope->mem_size) {
return current_scope->memory[addr];
}
return 0;
}
void scope_mem_write(int addr, long value) {
if (!current_scope) return;
if (addr < 0 || addr >= current_scope->mem_size) return;
current_scope->memory[addr] = value;
}
int scope_mem_alloc(int size) {
if (!current_scope || size <= 0) return -1;
if (current_scope->sp + size > current_scope->mem_size) {
error("Scope stack overflow");
return -1;
}
int addr = current_scope->sp;
current_scope->sp += size;
return addr;
}
int scope_check_sp(int required) {
if (!current_scope) return 0;
return required >= 0 && required < current_scope->mem_size;
}
int scope_check_addr(int addr) {
if (!current_scope) return 0;
return addr >= 0 && addr < current_scope->mem_size;
}
void scope_init(void) {
extern int sp, bp, loc_cnt;
if (root_scope) {
scope_cleanup();
}
root_scope = scope_create(NULL);
current_scope = root_scope;
sp = 0;
bp = 0;
loc_cnt = 0;
}
void scope_cleanup(void) {
while (current_scope && current_scope != root_scope) {
scope_pop();
}
if (root_scope) {
scope_destroy(root_scope);
root_scope = NULL;
current_scope = NULL;
}
}
void scope_save_state(int *out_sp, int *out_bp, int *out_loc_cnt) {
if (!current_scope) {
*out_sp = 0;
*out_bp = 0;
*out_loc_cnt = 0;
return;
}
*out_sp = current_scope->sp;
*out_bp = current_scope->bp;
*out_loc_cnt = current_scope->loc_cnt;
}
void scope_restore_state(int saved_sp, int saved_bp, int saved_loc_cnt) {
if (!current_scope) return;
current_scope->sp = saved_sp;
current_scope->bp = saved_bp;
current_scope->loc_cnt = saved_loc_cnt;
}
void scope_sync_to_globals(void) {
if (!current_scope) return;
extern long memory[];
extern Symbol locals[];
extern int sp, bp, loc_cnt;
int copy_sp = current_scope->sp < MEM_SIZE ? current_scope->sp : MEM_SIZE;
for (int i = 0; i < copy_sp; i++) {
memory[i] = current_scope->memory[i];
}
int copy_loc = current_scope->loc_cnt < VAR_MAX ? current_scope->loc_cnt : VAR_MAX;
for (int i = 0; i < copy_loc; i++) {
locals[i] = current_scope->locals[i];
}
sp = current_scope->sp;
bp = current_scope->bp;
loc_cnt = current_scope->loc_cnt;
}
void scope_sync_from_globals(void) {
if (!current_scope) return;
extern long memory[];
extern Symbol locals[];
extern int sp, bp, loc_cnt;
int copy_sp = sp < current_scope->mem_size ? sp : current_scope->mem_size;
for (int i = 0; i < copy_sp; i++) {
current_scope->memory[i] = memory[i];
}
int copy_loc = loc_cnt < current_scope->loc_capacity ? loc_cnt : current_scope->loc_capacity;
for (int i = 0; i < copy_loc; i++) {
current_scope->locals[i] = locals[i];
}
current_scope->sp = sp;
current_scope->bp = bp;
current_scope->loc_cnt = loc_cnt;
}
Symbol* scope_find_local_symbol(const char *name, int len) {
if (!name || len <= 0 || len >= 32) return NULL;
Scope *scope = current_scope;
while (scope) {
for (int i = scope->loc_cnt - 1; i >= 0; i--) {
if (!strncmp(scope->locals[i].name, name, len) && scope->locals[i].name[len] == 0) {
return &scope->locals[i];
}
}
scope = scope->parent;
}
return NULL;
}
int scope_find_local_with_scope(const char *name, int len, Scope **out_scope) {
if (!name || len <= 0 || len >= 32) return -1;
Scope *scope = current_scope;
while (scope) {
for (int i = scope->loc_cnt - 1; i >= 0; i--) {
if (!strncmp(scope->locals[i].name, name, len) && scope->locals[i].name[len] == 0) {
if (out_scope) *out_scope = scope;
return i;
}
}
scope = scope->parent;
}
return -1;
}
long scope_read_var(const char *name, int len) {
Scope *scope = NULL;
int idx = scope_find_local_with_scope(name, len, &scope);
if (idx < 0 || !scope) return 0;
Symbol *sym = &scope->locals[idx];
if (sym->addr < 0 || sym->addr >= scope->mem_size) return 0;
return scope->memory[sym->addr];
}
void scope_write_var(const char *name, int len, long value) {
Scope *scope = NULL;
int idx = scope_find_local_with_scope(name, len, &scope);
if (idx < 0 || !scope) return;
Symbol *sym = &scope->locals[idx];
if (sym->addr < 0 || sym->addr >= scope->mem_size) return;
scope->memory[sym->addr] = value;
}
+54
View File
@@ -0,0 +1,54 @@
#ifndef SCOPE_H
#define SCOPE_H
#include "types.h"
#define SCOPE_MEM_SIZE 10000
#define SCOPE_VAR_MAX 500
typedef struct Scope {
struct Scope *parent;
long *memory;
int mem_size;
Symbol *locals;
int loc_capacity;
int sp;
int bp;
int loc_cnt;
int depth;
} Scope;
extern Scope *current_scope;
extern Scope *root_scope;
Scope* scope_create(Scope *parent);
void scope_destroy(Scope *scope);
Scope* scope_push(void);
Scope* scope_pop(void);
int scope_find_local(const char *name, int len);
int scope_find_local_current_only(const char *name, int len);
Symbol* scope_get_local(int idx);
Symbol* scope_add_local(const char *name, int len, int type, int addr, int is_array);
long scope_mem_read(int addr);
void scope_mem_write(int addr, long value);
int scope_mem_alloc(int size);
int scope_check_sp(int required);
int scope_check_addr(int addr);
void scope_init(void);
void scope_cleanup(void);
void scope_save_state(int *out_sp, int *out_bp, int *out_loc_cnt);
void scope_restore_state(int saved_sp, int saved_bp, int saved_loc_cnt);
void scope_sync_to_globals(void);
void scope_sync_from_globals(void);
Symbol* scope_find_local_symbol(const char *name, int len);
int scope_find_local_with_scope(const char *name, int len, Scope **out_scope);
long scope_read_var(const char *name, int len);
void scope_write_var(const char *name, int len, long value);
#endif
-206
View File
@@ -1,206 +0,0 @@
# RC Standard Library
The RC language standard library is organized into modular categories for easy maintenance and extensibility.
## Structure
```
src/stdlib/
├── stdlib.h # Main registration header
├── stdlib.c # Registers all stdlib modules
├── string/ # String manipulation functions
│ ├── string_stdlib.h
│ └── string_stdlib.c
├── math/ # Mathematical functions
│ ├── math_stdlib.h
│ └── math_stdlib.c
├── io/ # Input/Output operations
│ ├── file_stdlib.h
│ ├── file_stdlib.c # File I/O functions
│ ├── socket_stdlib.h
│ └── socket_stdlib.c # Network socket functions
├── async/ # Asynchronous operations
│ ├── async_stdlib.h
│ └── async_stdlib.c # Async I/O and coroutines
└── constants/ # System constants
├── constants_stdlib.h
└── constants_stdlib.c
```
## Available Functions
### String Functions (string/)
- `strlen(str)` - Get string length
- `strpos(haystack, needle)` - Find substring position
- `substr(str, start, length)` - Extract substring
- `upper(str)` - Convert to uppercase
- `lower(str)` - Convert to lowercase
- `strip(str)` - Trim whitespace
- `replace(str, old, new)` - Replace substring
- `startswith(str, prefix)` - Check if starts with prefix
- `endswith(str, suffix)` - Check if ends with suffix
### Math Functions (math/)
- `sqrt(x)` - Square root
- `pow(base, exp)` - Power function
- `sin(x)`, `cos(x)`, `tan(x)` - Trigonometric functions
- `abs(x)` - Absolute value
- `floor(x)`, `ceil(x)` - Rounding functions
- `int_to_double(x)` - Convert int to double
- `double_to_int(x)` - Convert double to int
- `double_add(a, b)`, `double_sub(a, b)`, `double_mul(a, b)`, `double_div(a, b)` - Double arithmetic
### File I/O Functions (io/file_stdlib.c)
- `fopen(filename, mode)` - Open file
- `fclose(file)` - Close file
- `fread(file, addr, size)` - Read from file
- `fwrite(file, buf, size)` - Write to file
- `fgets(file, max_size)` - Read line from file
- `fputs(file, str)` - Write string to file
- `feof(file)` - Check end of file
- `ftell(file)` - Get file position
- `fseek(file, offset, whence)` - Seek in file
- `fremove(filename)` - Delete file
- `frename(oldname, newname)` - Rename file
### Socket Functions (io/socket_stdlib.c)
- `socket(domain, type, protocol)` - Create socket
- `bind(sockfd, port)` - Bind socket to port
- `listen(sockfd, backlog)` - Listen for connections
- `accept(sockfd)` - Accept connection
- `recv(sockfd, addr, len, flags)` - Receive data
- `send(sockfd, buf, len, flags)` - Send data
- `close(fd)` - Close file descriptor
### Async Functions (async/)
- `async_fread(file, addr, size)` - Async file read
- `async_fwrite(file, buf, size)` - Async file write
- `async_recv(sockfd, addr, len, flags)` - Async socket receive
- `async_send(sockfd, buf, len, flags)` - Async socket send
- `async_wait(id)` - Wait for async operation
- `async_poll(id)` - Poll async operation status
- `async_result(id)` - Get async operation result
- `await(coroutine_id)` - Await coroutine completion
- `gather(coro1, coro2, ...)` - Wait for multiple coroutines
### Constants (constants/)
- `AF_INET()` - IPv4 address family
- `SOCK_STREAM()` - TCP socket type
- `SEEK_SET()` - Seek from beginning
- `SEEK_CUR()` - Seek from current position
- `SEEK_END()` - Seek from end
## Adding New Functions
### 1. Add to Existing Category
To add a new function to an existing category (e.g., a new math function):
1. Open the appropriate category file: `src/stdlib/math/math_stdlib.c`
2. Add your function implementation:
```c
long native_my_function(long *args, int argc) {
if (!args || argc < 1) {
return 0;
}
// Your implementation here
return result;
}
```
3. Register it in the category's registration function:
```c
void register_math_stdlib() {
// ... existing registrations ...
register_native_func("my_function", native_my_function);
}
```
4. Rebuild: `make clean && make`
### 2. Add New Category
To add a new category (e.g., `datetime`):
1. Create directory: `mkdir -p src/stdlib/datetime`
2. Create header file: `src/stdlib/datetime/datetime_stdlib.h`
```c
#ifndef DATETIME_STDLIB_H
#define DATETIME_STDLIB_H
void register_datetime_stdlib();
#endif
```
3. Create implementation: `src/stdlib/datetime/datetime_stdlib.c`
```c
#include <time.h>
#include "../../types.h"
#include "datetime_stdlib.h"
extern void register_native_func(char *name, NativeFunc func);
long native_time(long *args, int argc) {
return (long)time(NULL);
}
void register_datetime_stdlib() {
register_native_func("time", native_time);
}
```
4. Add to `src/stdlib/stdlib.c`:
```c
#include "datetime/datetime_stdlib.h"
void register_stdlib() {
// ... existing registrations ...
register_datetime_stdlib();
}
```
5. Add to Makefile SOURCES:
```makefile
SOURCES = ... \
$(SRC_DIR)/stdlib/datetime/datetime_stdlib.c
```
6. Create build directory in Makefile dirs target:
```makefile
dirs:
# ... existing directories ...
@mkdir -p $(BUILD_DIR)/stdlib/datetime
```
7. Rebuild: `make clean && make`
## Function Signature
All native functions must follow this signature:
```c
long native_function_name(long *args, int argc)
```
Where:
- `args` - Array of long arguments passed from RC code
- `argc` - Number of arguments
- Returns a `long` value
## Best Practices
1. Always validate arguments (check argc and args pointer)
2. Use appropriate error handling
3. Document your functions with comments
4. Test your functions thoroughly
5. Keep functions focused and single-purpose
6. Use the string pool for string allocations (str_pool)
7. Check bounds when accessing memory array
8. Follow the existing code style
## Testing
After making changes:
1. Build: `make clean && make`
2. Run all tests: `make test`
3. Run specific category tests (e.g., `make run-math-test`)
## Architecture Notes
- All stdlib modules are automatically registered when `register_stdlib()` is called from `src/native_functions.c`
- The registration happens during interpreter initialization in `src/main.c`
- Native functions are stored in the global `native_funcs` array
- Function lookup is performed by name during parsing
+6 -17
View File
@@ -5,6 +5,7 @@
#include <sys/socket.h>
#include "../../types.h"
#include "../../interpreter.h"
#include "../../scope.h"
#include "async_stdlib.h"
#include "../../debug.h"
@@ -564,23 +565,11 @@ static long execute_function_sync(int func_idx, long *args, int argc) {
return 0;
}
int saved_sp = sp;
int saved_bp = bp;
int saved_pc = pc;
int saved_loc_cnt = loc_cnt;
long saved_ax = ax;
int saved_return_flag = return_flag;
if (sp >= MEM_SIZE) return 0;
memory[sp] = bp;
bp = sp++;
if (sp >= MEM_SIZE - 1) {
sp = saved_sp;
bp = saved_bp;
return 0;
}
memory[sp++] = 0;
memory[sp++] = 0;
scope_push();
int param_base = sp;
for (int i = 0; i < argc && i < 10 && sp < MEM_SIZE; i++) {
@@ -621,11 +610,11 @@ static long execute_function_sync(int func_idx, long *args, int argc) {
long result = ax;
sp = saved_sp;
bp = saved_bp;
scope_pop();
pc = saved_pc;
loc_cnt = saved_loc_cnt;
ax = saved_ax;
return_flag = saved_return_flag;
return result;
}
+192
View File
@@ -0,0 +1,192 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <pthread.h>
#include "../../types.h"
#include "benchmark_stdlib.h"
extern void register_native_func(char *name, NativeFunc func);
extern pthread_mutex_t str_pool_mutex;
extern char str_pool[STR_POOL_SIZE];
extern int str_pool_idx;
#define MAX_TIMERS 1000
typedef struct {
struct timespec start_time;
int active;
} BenchTimer;
static BenchTimer timers[MAX_TIMERS];
static pthread_mutex_t timers_mutex = PTHREAD_MUTEX_INITIALIZER;
static int next_timer_id = 0;
long native_bench_start(long *args, int argc) {
pthread_mutex_lock(&timers_mutex);
int timer_id = -1;
for (int i = 0; i < MAX_TIMERS; i++) {
if (!timers[i].active) {
timer_id = i;
break;
}
}
if (timer_id == -1) {
if (next_timer_id < MAX_TIMERS) {
timer_id = next_timer_id++;
} else {
pthread_mutex_unlock(&timers_mutex);
return -1;
}
}
timers[timer_id].active = 1;
clock_gettime(CLOCK_MONOTONIC, &timers[timer_id].start_time);
pthread_mutex_unlock(&timers_mutex);
return timer_id;
}
long native_bench_end(long *args, int argc) {
if (!args || argc < 1) {
return -1;
}
int timer_id = (int)args[0];
if (timer_id < 0 || timer_id >= MAX_TIMERS) {
return -1;
}
struct timespec end_time;
clock_gettime(CLOCK_MONOTONIC, &end_time);
pthread_mutex_lock(&timers_mutex);
if (!timers[timer_id].active) {
pthread_mutex_unlock(&timers_mutex);
return -1;
}
long elapsed_ns = (end_time.tv_sec - timers[timer_id].start_time.tv_sec) * 1000000000L +
(end_time.tv_nsec - timers[timer_id].start_time.tv_nsec);
timers[timer_id].active = 0;
pthread_mutex_unlock(&timers_mutex);
return elapsed_ns;
}
long native_bench_format(long *args, int argc) {
static char empty_str[] = "";
if (!args || argc < 2) {
return (long)empty_str;
}
long nanoseconds = args[0];
int unit = (int)args[1];
pthread_mutex_lock(&str_pool_mutex);
if (str_pool_idx >= STR_POOL_SIZE - 256) {
pthread_mutex_unlock(&str_pool_mutex);
return (long)empty_str;
}
char *result = &str_pool[str_pool_idx];
int len = 0;
switch (unit) {
case 0:
len = snprintf(result, 256, "%ld ns", nanoseconds);
break;
case 1: {
double microseconds = nanoseconds / 1000.0;
len = snprintf(result, 256, "%.3f us", microseconds);
break;
}
case 2: {
double milliseconds = nanoseconds / 1000000.0;
len = snprintf(result, 256, "%.3f ms", milliseconds);
break;
}
case 3: {
double seconds = nanoseconds / 1000000000.0;
len = snprintf(result, 256, "%.6f s", seconds);
break;
}
case 4: {
double minutes = nanoseconds / 60000000000.0;
len = snprintf(result, 256, "%.6f min", minutes);
break;
}
case 5: {
double hours = nanoseconds / 3600000000000.0;
len = snprintf(result, 256, "%.6f h", hours);
break;
}
default:
len = snprintf(result, 256, "%ld ns", nanoseconds);
break;
}
if (len > 0 && len < 256) {
str_pool_idx += len + 1;
}
pthread_mutex_unlock(&str_pool_mutex);
return (long)result;
}
long native_bench_format_auto(long *args, int argc) {
if (!args || argc < 1) {
static char empty_str[] = "";
return (long)empty_str;
}
long nanoseconds = args[0];
int unit = 0;
if (nanoseconds >= 3600000000000L) {
unit = 5;
} else if (nanoseconds >= 60000000000L) {
unit = 4;
} else if (nanoseconds >= 1000000000L) {
unit = 3;
} else if (nanoseconds >= 1000000L) {
unit = 2;
} else if (nanoseconds >= 1000L) {
unit = 1;
} else {
unit = 0;
}
long format_args[2] = {nanoseconds, unit};
return native_bench_format(format_args, 2);
}
long native_bench_reset(long *args, int argc) {
pthread_mutex_lock(&timers_mutex);
for (int i = 0; i < MAX_TIMERS; i++) {
timers[i].active = 0;
}
next_timer_id = 0;
pthread_mutex_unlock(&timers_mutex);
return 0;
}
void register_benchmark_stdlib() {
register_native_func("bench_start", native_bench_start);
register_native_func("bench_end", native_bench_end);
register_native_func("bench_format", native_bench_format);
register_native_func("bench_format_auto", native_bench_format_auto);
register_native_func("bench_reset", native_bench_reset);
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef BENCHMARK_STDLIB_H
#define BENCHMARK_STDLIB_H
void register_benchmark_stdlib();
#endif
+34
View File
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "../../types.h"
#include "math_stdlib.h"
@@ -128,6 +129,35 @@ long native_double_div(long *args, int argc) {
return result.l;
}
long native_rand(long *args, int argc) {
return (long)rand();
}
long native_srand(long *args, int argc) {
if (!args || argc < 1) {
srand(1);
} else {
srand((unsigned int)args[0]);
}
return 0;
}
long native_rand_range(long *args, int argc) {
if (!args || argc < 2) {
return 0;
}
long min = args[0];
long max = args[1];
if (max <= min) {
return min;
}
return min + (rand() % (max - min + 1));
}
long native_time(long *args, int argc) {
return (long)time(NULL);
}
void register_math_stdlib() {
register_native_func("sqrt", native_sqrt);
register_native_func("pow", native_pow);
@@ -143,4 +173,8 @@ void register_math_stdlib() {
register_native_func("double_sub", native_double_sub);
register_native_func("double_mul", native_double_mul);
register_native_func("double_div", native_double_div);
register_native_func("rand", native_rand);
register_native_func("srand", native_srand);
register_native_func("rand_range", native_rand_range);
register_native_func("time", native_time);
}
+2
View File
@@ -6,6 +6,7 @@
#include "async/async_stdlib.h"
#include "constants/constants_stdlib.h"
#include "eval/eval_stdlib.h"
#include "benchmark/benchmark_stdlib.h"
void register_stdlib() {
register_string_stdlib();
@@ -15,4 +16,5 @@ void register_stdlib() {
register_async_stdlib();
register_constants_stdlib();
register_eval_stdlib();
register_benchmark_stdlib();
}
+168
View File
@@ -0,0 +1,168 @@
#include "unittest.rc"
int main() {
int tc = new TestCase();
tc.init("Benchmark Standard Library");
printf("\n=== Testing bench_start/bench_end ===\n");
int timer1 = bench_start();
tc.assertGreaterEqual(timer1, 0, "bench_start returns valid timer ID");
int i = 0;
int sum = 0;
while (i < 1000) {
sum = sum + i;
i = i + 1;
}
int elapsed1 = bench_end(timer1);
tc.assertGreater(elapsed1, 0, "bench_end returns positive elapsed time");
tc.assertLess(elapsed1, 1000000000, "elapsed time is less than 1 second");
printf("\n=== Testing multiple timers ===\n");
int timer2 = bench_start();
int timer3 = bench_start();
tc.assertNotEqual(timer2, timer3, "Multiple timers have different IDs");
int elapsed2 = bench_end(timer2);
int elapsed3 = bench_end(timer3);
tc.assertGreater(elapsed2, 0, "Timer 2 has positive elapsed time");
tc.assertGreater(elapsed3, 0, "Timer 3 has positive elapsed time");
printf("\n=== Testing bench_format with nanoseconds ===\n");
char* formatted_ns = bench_format(12345, 0);
tc.assertNotNull(formatted_ns, "bench_format(ns) returns non-null");
tc.assertStringContains(formatted_ns, "ns", "Formatted string contains 'ns'");
tc.assertStringContains(formatted_ns, "12345", "Formatted string contains value");
printf("\n=== Testing bench_format with microseconds ===\n");
char* formatted_us = bench_format(123456, 1);
tc.assertNotNull(formatted_us, "bench_format(us) returns non-null");
tc.assertStringContains(formatted_us, "us", "Formatted string contains 'us'");
printf("\n=== Testing bench_format with milliseconds ===\n");
char* formatted_ms = bench_format(1234567, 2);
tc.assertNotNull(formatted_ms, "bench_format(ms) returns non-null");
tc.assertStringContains(formatted_ms, "ms", "Formatted string contains 'ms'");
printf("\n=== Testing bench_format with seconds ===\n");
char* formatted_s = bench_format(1234567890, 3);
tc.assertNotNull(formatted_s, "bench_format(s) returns non-null");
tc.assertStringContains(formatted_s, "s", "Formatted string contains 's'");
printf("\n=== Testing bench_format with minutes ===\n");
char* formatted_min = bench_format(123456789000, 4);
tc.assertNotNull(formatted_min, "bench_format(min) returns non-null");
tc.assertStringContains(formatted_min, "min", "Formatted string contains 'min'");
printf("\n=== Testing bench_format with hours ===\n");
char* formatted_h = bench_format(7412345678900, 5);
tc.assertNotNull(formatted_h, "bench_format(h) returns non-null");
tc.assertStringContains(formatted_h, "h", "Formatted string contains 'h'");
printf("\n=== Testing bench_format_auto with nanoseconds ===\n");
char* auto_ns = bench_format_auto(500);
tc.assertNotNull(auto_ns, "bench_format_auto(500ns) returns non-null");
tc.assertStringContains(auto_ns, "ns", "Auto-format chooses nanoseconds for small values");
printf("\n=== Testing bench_format_auto with microseconds ===\n");
char* auto_us = bench_format_auto(5000);
tc.assertNotNull(auto_us, "bench_format_auto(5000ns) returns non-null");
tc.assertStringContains(auto_us, "us", "Auto-format chooses microseconds");
printf("\n=== Testing bench_format_auto with milliseconds ===\n");
char* auto_ms = bench_format_auto(5000000);
tc.assertNotNull(auto_ms, "bench_format_auto(5ms) returns non-null");
tc.assertStringContains(auto_ms, "ms", "Auto-format chooses milliseconds");
printf("\n=== Testing bench_format_auto with seconds ===\n");
char* auto_s = bench_format_auto(5000000000);
tc.assertNotNull(auto_s, "bench_format_auto(5s) returns non-null");
tc.assertStringContains(auto_s, "s", "Auto-format chooses seconds");
printf("\n=== Testing bench_format_auto with minutes ===\n");
char* auto_min = bench_format_auto(300000000000);
tc.assertNotNull(auto_min, "bench_format_auto(5min) returns non-null");
tc.assertStringContains(auto_min, "min", "Auto-format chooses minutes");
printf("\n=== Testing bench_format_auto with hours ===\n");
char* auto_h = bench_format_auto(18000000000000);
tc.assertNotNull(auto_h, "bench_format_auto(5h) returns non-null");
tc.assertStringContains(auto_h, "h", "Auto-format chooses hours");
printf("\n=== Testing practical benchmarking scenario ===\n");
int timer_practical = bench_start();
int j = 0;
int factorial = 1;
while (j < 100) {
factorial = factorial + j;
j = j + 1;
}
int elapsed_practical = bench_end(timer_practical);
char* practical_formatted = bench_format_auto(elapsed_practical);
tc.assertGreater(elapsed_practical, 0, "Practical benchmark measured time");
tc.assertNotNull(practical_formatted, "Practical benchmark formatted correctly");
printf(" Practical test took: %s\n", practical_formatted);
printf("\n=== Testing bench_reset ===\n");
int reset_result = bench_reset();
tc.assertEqual(reset_result, 0, "bench_reset returns 0");
int timer_after_reset = bench_start();
tc.assertGreaterEqual(timer_after_reset, 0, "Timer ID valid after reset");
printf("\n=== Testing error cases ===\n");
int invalid_end = bench_end(9999);
tc.assertEqual(invalid_end, -1, "bench_end with invalid timer returns -1");
int negative_timer = bench_end(-1);
tc.assertEqual(negative_timer, -1, "bench_end with negative timer returns -1");
printf("\n=== Testing nested timing ===\n");
int outer_timer = bench_start();
int inner_timer = bench_start();
int k = 0;
while (k < 50) {
k = k + 1;
}
int inner_elapsed = bench_end(inner_timer);
int outer_elapsed = bench_end(outer_timer);
tc.assertGreater(inner_elapsed, 0, "Inner timer measured time");
tc.assertGreater(outer_elapsed, 0, "Outer timer measured time");
tc.assertGreaterEqual(outer_elapsed, inner_elapsed, "Outer timer >= inner timer");
printf("\n=== Summary ===\n");
printf("Total Passed: %d\n", tc.passed);
printf("Total Failed: %d\n", tc.failed);
if (tc.failed == 0) {
printf("All benchmark stdlib tests PASSED!\n");
}
return 0;
}
+29
View File
@@ -61,6 +61,35 @@ int main() {
result_int = double_to_int(d_quot);
tc.assertEqual(result_int, 1, "Double division: 5.0 / 3.0 = 1.666... (truncated to 1)");
printf("\n=== Testing Random Functions ===\n");
srand(42);
int r1 = rand();
int r2 = rand();
tc.assertNotEqual(r1, r2, "Two consecutive rand() calls should give different values");
tc.assertGreaterEqual(r1, 0, "rand() returns non-negative");
tc.assertGreaterEqual(r2, 0, "rand() returns non-negative");
srand(42);
int r3 = rand();
tc.assertEqual(r1, r3, "Same seed produces same first value");
printf("\n=== Testing rand_range ===\n");
srand(time());
int i = 0;
int range_ok = 1;
while (i < 100) {
int val = rand_range(10, 20);
if (val < 10 || val > 20) {
range_ok = 0;
}
i = i + 1;
}
tc.assertTrue(range_ok, "rand_range(10, 20) returns values in [10, 20]");
printf("\n=== Testing time ===\n");
int t1 = time();
tc.assertGreater(t1, 0, "time() returns positive timestamp");
printf("\n=== Summary ===\n");
printf("Total Passed: %d\n", tc.passed);
printf("Total Failed: %d\n", tc.failed);