Compare commits
45
Commits
63f9eb48a4
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5609a7558c | ||
|
|
e7fe6d2e56 | ||
|
|
09e3339855 | ||
|
|
274c44e0f7 | ||
|
|
2419b9b51e | ||
|
|
b5a0faac68 | ||
|
|
8c59cfafa5 | ||
|
|
7dd3a13732 | ||
|
|
6be2eb28f0 | ||
|
|
acc2ac6088 | ||
|
|
5ba90c77f3 | ||
|
|
b6a99ec2c8 | ||
|
|
8c0548960f | ||
|
|
2fc2e79e68 | ||
|
|
7e0a843afa | ||
|
|
179ebc1892 | ||
|
|
c1fd0a22fe | ||
|
|
9b2a2c73d5 | ||
|
|
619c0467b0 | ||
|
|
5634bf5e11 | ||
|
|
655d33d789 | ||
|
|
9a83539e33 | ||
|
|
e4bac92971 | ||
|
|
6e6f522a55 | ||
|
|
6bae549720 | ||
|
|
8c5de5ee9d | ||
|
|
0ea0f8e9eb | ||
|
|
7b1ca4459c | ||
|
|
a63dda5de3 | ||
|
|
3fab35d7cb | ||
|
|
837e254ad6 | ||
|
|
76b1da5351 | ||
|
|
6a34114899 | ||
|
|
d669b1da3d | ||
|
|
79a06a0be8 | ||
|
|
03bca46d88 | ||
|
|
67c15f026d | ||
|
|
eb3d29ac8d | ||
|
|
82722a757a | ||
|
|
b89a794817 | ||
|
|
5ceaf35d5a | ||
|
|
b5b720d993 | ||
|
|
1561e6a542 | ||
|
|
dc91958deb | ||
|
|
586a171d86 |
@@ -0,0 +1,13 @@
|
||||
name: Build and Test
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: make
|
||||
- name: Test
|
||||
run: make test
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
## Claude Code Prompt: Implement Simple, Effective DateTime/Timezone Library for RC Language
|
||||
|
||||
### Project Context
|
||||
Design and implement a concise, modern datetime and timezone library for your RC language's stdlib. The API should provide the most useful features of Python's `datetime`, but with smarter, clearer naming (CamelCase), easier usage, and required UTC/timezone awareness by default. Sleep (blocking and async) and formatting must also be included. All functions are accessible without ambiguity from the scripting interface, emphasizing simplicity but full timezone safety.[1][2][3][4][5][6]
|
||||
|
||||
***
|
||||
|
||||
### Naming and Structure Guidelines
|
||||
|
||||
- All class and function names use CamelCase (e.g. DateTime, Timezone, sleep, now).
|
||||
- The core class is `DateTime`.
|
||||
- All DateTime instances are always timezone-aware (default is UTC unless set).
|
||||
- A `Timezone` class provides methods to create fixed-offset or system-local timezones.
|
||||
- Module name for scripts: `datetime`.
|
||||
- Provide both synchronous and async `sleep()` functions as module-level methods.
|
||||
|
||||
***
|
||||
|
||||
### API and Features
|
||||
|
||||
#### Class: DateTime
|
||||
|
||||
Fields:
|
||||
- `year`, `month`, `day`, `hour`, `minute`, `second`, `microsecond` (all int)
|
||||
- `tz` (Timezone object)
|
||||
|
||||
Construction:
|
||||
- `DateTime.now(timezone=None)` — Get current time (with timezone awareness, default: UTC).
|
||||
- `DateTime.fromTimestamp(timestamp, timezone=None)` — Create from timestamp (in seconds, with optional timezone).
|
||||
- `DateTime(year, month, day, hour=0, minute=0, second=0, microsecond=0, timezone=None)` — Direct constructor, all fields with timezone argument (default UTC).
|
||||
- `DateTime.fromIso(string)` — Parse from ISO-8601 string with timezone info (throws if missing).
|
||||
|
||||
Key methods:
|
||||
- `DateTime.toTimezone(tz)` — Returns a copy in the given timezone.
|
||||
- `DateTime.toUTC()` — Shortcut for conversion to UTC timezone.
|
||||
- `DateTime.timestamp()` — Returns Unix timestamp (float seconds since epoch).
|
||||
- `DateTime.add(seconds=0, days=0, weeks=0)` — Datetime arithmetic; returns modified copy.
|
||||
- `DateTime.subtract(other)` — Returns seconds between this and `other` DateTime.
|
||||
- `DateTime.format(fmt)` — Returns formatted string using simple placeholders: `YYYY`, `MM`, `DD`, `hh`, `mm`, `ss`, `zzz` for zone name, `zzz` for offset, etc.
|
||||
|
||||
Specials:
|
||||
- All DateTime methods always keep full timezone information and offset internally.
|
||||
- Arithmetic and conversions never result in a "naive" (zoneless) datetime.
|
||||
- Raise error if attempted operations would drop timezone information.
|
||||
|
||||
***
|
||||
|
||||
#### Class: Timezone
|
||||
|
||||
Construction:
|
||||
- `Timezone.utc()` — UTC timezone static method.
|
||||
- `Timezone.offset(hours, minutes=0, name=None)` — Create a fixed-offset zone.
|
||||
- `Timezone.local()` — System local timezone (from OS).
|
||||
- `Timezone.of(name)` — Lookup zone by well-known IANA name (Europe/Amsterdam, America/New_York, etc.). Throws if unknown.
|
||||
|
||||
Fields:
|
||||
- `name` (e.g., 'UTC', 'Europe/Amsterdam')
|
||||
- `offsetSeconds` (signed int, offset in seconds from UTC)
|
||||
|
||||
Methods:
|
||||
- `isDst(dt)` — Whether this datetime is in daylight saving for this zone (if data is available).
|
||||
- `getOffsetSeconds(dt)` — Get zone offset from UTC for provided DateTime.
|
||||
- `abbreviation(dt)` — Return zone name/abbrev for datetime if available.
|
||||
|
||||
***
|
||||
|
||||
#### Module-level Functions
|
||||
|
||||
- `now(timezone=None)` — Same as DateTime.now().
|
||||
- `sleep(seconds)` — Blocking sleep; integer or float seconds allowed.
|
||||
- `asyncSleep(seconds)` — Coroutine-based sleep for use with RC's async (awaitable).
|
||||
- `timestamp()` — Return current Unix timestamp (in UTC).
|
||||
- `fromIso(string)` — Parse ISO-8601 string to DateTime (defaults to UTC if missing).
|
||||
- `parse(string, format)` — Parse other formats if needed.
|
||||
|
||||
***
|
||||
|
||||
### Usage Design Principles
|
||||
|
||||
- All datetime comparisons must be valid even across timezones (error if ambiguous, e.g., DST switch).
|
||||
- Formatting is simple, robust, and defaults to ISO output if not specified.
|
||||
- Default for all methods is the safest, least-error-prone: no naive zones, always explicit.
|
||||
- API must be easy and fast to use for:
|
||||
- Current time in various zones.
|
||||
- Formatting for APIs/logging (ISO, RFC, compact).
|
||||
- Parsing and comparing datetimes.
|
||||
- Scheduling (with async sleep support).
|
||||
- Time arithmetic (add/subtract deltas).
|
||||
|
||||
***
|
||||
|
||||
### Example RC Script Usage (Not code to Claude, but for your design)
|
||||
|
||||
- `d1 = now()` // yields DateTime in UTC
|
||||
- `d2 = DateTime(2022, 1, 1, 10, 0, 0, timezone=Timezone.of("Europe/Amsterdam"))`
|
||||
- `local = d1.toTimezone(Timezone.local())`
|
||||
- `sleep(2.5)` // blocks for 2.5 seconds
|
||||
- `await asyncSleep(5)` // non-blocking sleep
|
||||
- `diff = d1.subtract(d2)` // Returns difference in seconds
|
||||
- `d1.format("YYYY-MM-DD hh:mm:ss zzz")` // "2025-11-23 23:15:11 UTC"
|
||||
- `d3 = DateTime.fromIso("2025-11-23T23:15:11+01:00")`
|
||||
- `zonename = d3.tz.abbreviation(d3)` // e.g., "CET"
|
||||
|
||||
***
|
||||
|
||||
### Implementation Order
|
||||
|
||||
1. Native C layer:
|
||||
- Timezone class with IANA database basics (UTC and fixed-offset required, named lookups optional at first).
|
||||
- DateTime struct with always timezone info.
|
||||
- Exact Unix timestamp support (with microseconds).
|
||||
- ISO and simple custom formatters.
|
||||
- Sleep using system calls (and async with event loop).
|
||||
2. Register DateTime, Timezone, and module-level functions to RC scripting system.
|
||||
3. Add tests for:
|
||||
- All construction modes.
|
||||
- ISO parse/format cycles.
|
||||
- Cross-timezone operations.
|
||||
- Sleep and asyncSleep for delays.
|
||||
- DST edge cases.
|
||||
4. Document in your TUTORIAL.md, covering every exported method with examples.
|
||||
|
||||
***
|
||||
|
||||
### Prompt for Claude
|
||||
|
||||
You are to implement a minimal but robust datetime and timezone library for RC, following this spec:
|
||||
|
||||
- All objects are always timezone aware with a Timezone field (UTC by default).
|
||||
- DateTime represents a specific moment, with fields for all components; supports arithmetic, formatting, ISO parse/format, etc.
|
||||
- Timezone lets you create, query, and convert between zones. Support creating via UTC offset or by name (IANA/tzdb).
|
||||
- Expose now(), sleep(), asyncSleep(), timestamp(), parse(), and formatting at module level.
|
||||
- Everything's named in CamelCase and concise; methods never return naive datetimes.
|
||||
- All code is to be robust, safe, and succinct.
|
||||
- Add comprehensive unit tests.
|
||||
- Make this part of the RC stdlib and document usage clearly as described.
|
||||
|
||||
[1](https://docs.python.org/3/library/datetime.html)
|
||||
[2](https://www.geeksforgeeks.org/python/working-with-datetime-objects-and-timezones-in-python/)
|
||||
[3](https://stackoverflow.com/questions/7065164/how-to-make-a-datetime-object-aware-not-naive)
|
||||
[4](https://www.browserstack.com/guide/python-datetime-astimezone)
|
||||
[5](https://fintechpython.pages.oit.duke.edu/jupyternotebooks/1-Core%20Python/answers/rq-28-answers.html)
|
||||
[6](https://queirozf.com/entries/python-datetime-with-timezones-examples-and-reference)
|
||||
[7](https://www.w3schools.com/python/python_datetime.asp)
|
||||
[8](https://www.influxdata.com/blog/python-timezones-complete-introduction-influxdb/)
|
||||
[9](https://python.readthedocs.io/fr/latest/library/datetime.html)
|
||||
@@ -1,6 +1,6 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -O2 -Isrc
|
||||
LDFLAGS = -lm
|
||||
CFLAGS = -Wall -Wextra -O2 -Isrc -mcmodel=medium
|
||||
LDFLAGS = -lm -lpthread
|
||||
|
||||
SRC_DIR = src
|
||||
TEST_DIR = tests
|
||||
@@ -16,20 +16,45 @@ SOURCES = $(SRC_DIR)/main.c \
|
||||
$(SRC_DIR)/parser.c \
|
||||
$(SRC_DIR)/interpreter.c \
|
||||
$(SRC_DIR)/native_functions.c \
|
||||
$(SRC_DIR)/string_utils.c
|
||||
$(SRC_DIR)/string_utils.c \
|
||||
$(SRC_DIR)/preprocessor.c \
|
||||
$(SRC_DIR)/oop.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 \
|
||||
$(SRC_DIR)/stdlib/io/file_stdlib.c \
|
||||
$(SRC_DIR)/stdlib/io/socket_stdlib.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)
|
||||
|
||||
TESTS = $(TEST_DIR)/feature_test.rc \
|
||||
$(TEST_DIR)/endless_loop_test.rc \
|
||||
$(TEST_DIR)/math_test.rc \
|
||||
$(TEST_DIR)/string_test.rc \
|
||||
$(TEST_DIR)/string_manip_test.rc \
|
||||
$(TEST_DIR)/increment_decrement_test.rc
|
||||
TESTS = $(TEST_DIR)/unittest.rc \
|
||||
$(TEST_DIR)/test_language_features.rc \
|
||||
$(TEST_DIR)/test_stdlib_string.rc \
|
||||
$(TEST_DIR)/test_stdlib_math.rc \
|
||||
$(TEST_DIR)/test_stdlib_io.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 \
|
||||
$(TEST_DIR)/test_input_validation.rc \
|
||||
$(TEST_DIR)/run_all_tests.rc
|
||||
|
||||
EXAMPLES = $(EXAMPLE_DIR)/http_simple.rc \
|
||||
$(EXAMPLE_DIR)/http_persistent.rc \
|
||||
$(EXAMPLE_DIR)/http_multi.rc
|
||||
$(EXAMPLE_DIR)/http_multi.rc \
|
||||
$(EXAMPLE_DIR)/async_demo.rc
|
||||
|
||||
.PHONY: all clean test run-tests run-examples help
|
||||
|
||||
@@ -37,8 +62,16 @@ all: dirs $(TARGET)
|
||||
|
||||
dirs:
|
||||
@mkdir -p $(BUILD_DIR) $(BIN_DIR)
|
||||
@mkdir -p $(BUILD_DIR)/stdlib/string
|
||||
@mkdir -p $(BUILD_DIR)/stdlib/math
|
||||
@mkdir -p $(BUILD_DIR)/stdlib/io
|
||||
@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 $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(TARGET): dirs $(OBJECTS)
|
||||
@@ -48,45 +81,105 @@ clean:
|
||||
rm -rf $(BUILD_DIR) $(BIN_DIR)
|
||||
|
||||
test: $(TARGET)
|
||||
@echo "=== Running All Tests ==="
|
||||
@echo "================================================================"
|
||||
@echo " RC LANGUAGE - COMPREHENSIVE TEST SUITE"
|
||||
@echo "================================================================"
|
||||
@echo ""
|
||||
@echo "Running feature tests..."
|
||||
@$(TARGET) $(TEST_DIR)/feature_test.rc 2>&1 | grep -v "Error at token" || true
|
||||
@echo "[1/12] Running Language Features Tests..."
|
||||
@$(TARGET) $(TEST_DIR)/test_language_features.rc
|
||||
@echo ""
|
||||
@echo "Running endless loop test..."
|
||||
@$(TARGET) $(TEST_DIR)/endless_loop_test.rc 2>&1 | grep -v "Error at token" || true
|
||||
@echo "[2/12] Running String Standard Library Tests..."
|
||||
@$(TARGET) $(TEST_DIR)/test_stdlib_string.rc
|
||||
@echo ""
|
||||
@echo "Running math tests..."
|
||||
@$(TARGET) $(TEST_DIR)/math_test.rc 2>&1 | grep -v "Error at token" || true
|
||||
@echo "[3/12] Running Math Standard Library Tests..."
|
||||
@$(TARGET) $(TEST_DIR)/test_stdlib_math.rc
|
||||
@echo ""
|
||||
@echo "Running string concatenation tests..."
|
||||
@$(TARGET) $(TEST_DIR)/string_test.rc 2>&1 | grep -v "Error at token" || true
|
||||
@echo "[4/12] Running I/O Standard Library Tests..."
|
||||
@$(TARGET) $(TEST_DIR)/test_stdlib_io.rc
|
||||
@echo ""
|
||||
@echo "Running string manipulation tests..."
|
||||
@$(TARGET) $(TEST_DIR)/string_manip_test.rc 2>&1 | grep -v "Error at token" || true
|
||||
@echo "[5/12] Running Socket Standard Library Tests..."
|
||||
@$(TARGET) $(TEST_DIR)/test_stdlib_socket.rc
|
||||
@echo ""
|
||||
@echo "Running increment decrement tests..."
|
||||
@$(TARGET) $(TEST_DIR)/increment_decrement_test.rc 2>&1 | grep -v "Error at token" || true
|
||||
@echo "[6/12] Running Async Standard Library Tests..."
|
||||
@$(TARGET) $(TEST_DIR)/test_stdlib_async.rc
|
||||
@echo ""
|
||||
@echo "=== All Tests Completed ==="
|
||||
@echo "[7/12] Running Constants Standard Library Tests..."
|
||||
@$(TARGET) $(TEST_DIR)/test_stdlib_constants.rc
|
||||
@echo ""
|
||||
@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 "[10/12] Running Preprocessor Tests..."
|
||||
@$(TARGET) $(TEST_DIR)/test_preprocessor.rc
|
||||
@echo ""
|
||||
@echo "[11/12] Running Eval Function Tests..."
|
||||
@$(TARGET) $(TEST_DIR)/test_eval.rc
|
||||
@echo ""
|
||||
@echo "[12/12] Running Input Validation Tests..."
|
||||
@$(TARGET) $(TEST_DIR)/test_input_validation.rc
|
||||
@echo ""
|
||||
@echo "================================================================"
|
||||
@echo "All comprehensive tests completed!"
|
||||
@echo "================================================================"
|
||||
|
||||
run-feature-test: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/feature_test.rc
|
||||
test-smoke: $(TARGET)
|
||||
@$(TARGET) $(TEST_DIR)/run_all_tests.rc
|
||||
|
||||
run-endless-loop: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/endless_loop_test.rc
|
||||
test-language: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/test_language_features.rc
|
||||
|
||||
run-math-test: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/math_test.rc
|
||||
test-string: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/test_stdlib_string.rc
|
||||
|
||||
run-string-test: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/string_test.rc
|
||||
test-math: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/test_stdlib_math.rc
|
||||
|
||||
run-string-manip: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/string_manip_test.rc
|
||||
test-io: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/test_stdlib_io.rc
|
||||
|
||||
run-increment-decrement-test: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/increment_decrement_test.rc
|
||||
test-socket: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/test_stdlib_socket.rc
|
||||
|
||||
test-async: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/test_stdlib_async.rc
|
||||
|
||||
test-oop: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/test_oop.rc
|
||||
|
||||
test-preprocessor: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/test_preprocessor.rc
|
||||
|
||||
test-eval: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/test_eval.rc
|
||||
|
||||
test-validation: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/test_input_validation.rc
|
||||
|
||||
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
|
||||
|
||||
demo-error-stacktrace: $(TARGET)
|
||||
@echo "=== Demonstrating Full Stack Trace ==="
|
||||
-$(TARGET) $(TEST_DIR)/test_error_stacktrace.rc
|
||||
|
||||
demo-error-undefined: $(TARGET)
|
||||
@echo "=== Demonstrating Undefined Variable Error ==="
|
||||
-$(TARGET) $(TEST_DIR)/test_error_undefined_var.rc
|
||||
|
||||
demo-error-native: $(TARGET)
|
||||
@echo "=== Demonstrating Error in Native Function Call ==="
|
||||
-$(TARGET) $(TEST_DIR)/test_error_native.rc
|
||||
|
||||
demo-errors: demo-error-stacktrace demo-error-undefined demo-error-native
|
||||
|
||||
run-http-simple: $(TARGET)
|
||||
$(TARGET) $(EXAMPLE_DIR)/http_simple.rc
|
||||
@@ -97,29 +190,54 @@ run-http-persistent: $(TARGET)
|
||||
run-http-multi: $(TARGET)
|
||||
$(TARGET) $(EXAMPLE_DIR)/http_multi.rc
|
||||
|
||||
run-async-demo: $(TARGET)
|
||||
$(TARGET) $(EXAMPLE_DIR)/async_demo.rc
|
||||
|
||||
help:
|
||||
@echo "RC - Retoor's C Interpreter"
|
||||
@echo ""
|
||||
@echo "Usage:"
|
||||
@echo " make - Build the interpreter"
|
||||
@echo " make test - Run all feature 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 ""
|
||||
@echo "Individual Tests:"
|
||||
@echo " make run-feature-test - Run feature_test.rc (negative numbers, ==, !=)"
|
||||
@echo " make run-endless-loop - Run endless_loop_test.rc (while(1) test)"
|
||||
@echo " make run-math-test - Run math_test.rc (math functions)"
|
||||
@echo " make run-string-test - Run string_test.rc (string concatenation)"
|
||||
@echo " make run-string-manip - Run string_manip_test.rc (string manipulation & slicing)"
|
||||
@echo "Test Categories (organized by src/ structure):"
|
||||
@echo " make test-language - Language features (63 tests)"
|
||||
@echo " make test-string - String stdlib (40 tests)"
|
||||
@echo " make test-math - Math stdlib (26 tests)"
|
||||
@echo " make test-io - I/O stdlib (20 tests)"
|
||||
@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)"
|
||||
@echo " make test-validation - Input validation (77 tests)"
|
||||
@echo " make test-robustness - Robustness and safety (25 tests)"
|
||||
@echo ""
|
||||
@echo "Error Handling Demonstrations:"
|
||||
@echo " make demo-errors - Run all error handling demos"
|
||||
@echo " make demo-error-stacktrace - Show full stack trace with file:line"
|
||||
@echo " make demo-error-undefined - Show undefined variable error with tips"
|
||||
@echo " make demo-error-native - Show error during native function call"
|
||||
@echo ""
|
||||
@echo "Examples:"
|
||||
@echo " make run-http-simple - Run http_simple.rc (single connection HTTP server)"
|
||||
@echo " make run-http-persistent - Run http_persistent.rc (persistent HTTP server)"
|
||||
@echo " make run-http-multi - Run http_multi.rc (HTTP server, 100 connections)"
|
||||
@echo " make run-http-simple - Run http_simple.rc (single connection HTTP server)"
|
||||
@echo " make run-http-persistent - Run http_persistent.rc (persistent HTTP server)"
|
||||
@echo " make run-http-multi - Run http_multi.rc (HTTP server, 100 connections)"
|
||||
@echo " make run-async-demo - Run async_demo.rc (comprehensive async I/O demo)"
|
||||
@echo ""
|
||||
@echo "Directory Structure:"
|
||||
@echo " src/ - Source code files"
|
||||
@echo " tests/ - Test programs (.rc files)"
|
||||
@echo " examples/ - Example programs (.rc files)"
|
||||
@echo " build/ - Compiled object files"
|
||||
@echo " bin/ - Final executable (rc)"
|
||||
@echo " src/ - Source code files"
|
||||
@echo " stdlib/string/ - String functions"
|
||||
@echo " stdlib/math/ - Math functions"
|
||||
@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"
|
||||
@echo " bin/ - Final executable (rc)"
|
||||
|
||||
@@ -8,6 +8,7 @@ RC is a lightweight, recursive-descent C interpreter written in C. It executes a
|
||||
|
||||
**Data Types**
|
||||
- Integers (long)
|
||||
- Doubles (floating-point numbers)
|
||||
- Character pointers (char*)
|
||||
- Pointer operations (address-of &, dereference *)
|
||||
- Array declarations and indexing
|
||||
@@ -15,6 +16,7 @@ RC is a lightweight, recursive-descent C interpreter written in C. It executes a
|
||||
**Control Flow**
|
||||
- if/else statements
|
||||
- while loops (including infinite loops)
|
||||
- break and continue statements
|
||||
- Comparison operators: ==, !=, <, >, <=, >=
|
||||
- Logical operators: &&, ||
|
||||
|
||||
@@ -39,6 +41,13 @@ RC is a lightweight, recursive-descent C interpreter written in C. It executes a
|
||||
- sin(x), cos(x), tan(x) - Trigonometric functions
|
||||
- floor(x), ceil(x) - Rounding functions
|
||||
|
||||
**Double Type Functions**
|
||||
- int_to_double(i) - Convert int to double
|
||||
- double_to_int(d) - Convert double to int
|
||||
- double_add(a, b), double_sub(a, b) - Double arithmetic
|
||||
- double_mul(a, b), double_div(a, b) - Double arithmetic
|
||||
- printf with %f format for doubles
|
||||
|
||||
**String Functions**
|
||||
- strpos(haystack, needle) - Find substring position
|
||||
- substr(str, start, length) - Extract substring
|
||||
@@ -49,6 +58,34 @@ RC is a lightweight, recursive-descent C interpreter written in C. It executes a
|
||||
- endswith(str, suffix) - Suffix check
|
||||
- strlen(str) - String length
|
||||
|
||||
**File I/O**
|
||||
- fopen(filename, mode) - Open a file
|
||||
- fclose(file) - Close a file
|
||||
- fread(file, buffer, size) - Read from file
|
||||
- fwrite(file, buffer, size) - Write to file
|
||||
- fgets(file, max_size) - Read a line
|
||||
- fputs(file, string) - Write a string
|
||||
- feof(file) - Check end of file
|
||||
- ftell(file), fseek(file, offset, whence) - File positioning
|
||||
- fremove(filename), frename(old, new) - File operations
|
||||
- SEEK_SET, SEEK_CUR, SEEK_END constants
|
||||
|
||||
**Async/Await (Python-like)**
|
||||
- async keyword - Declare async functions
|
||||
- await(coro) - Wait for coroutine to complete
|
||||
- gather(c1, c2, ...) - Wait for multiple coroutines
|
||||
- Thread-safe execution with proper isolation
|
||||
- Maximum 100 concurrent coroutines
|
||||
|
||||
**Async I/O (Low-level)**
|
||||
- async_fread(file, buffer, size) - Async file read
|
||||
- async_fwrite(file, buffer, size) - Async file write
|
||||
- async_recv(socket, buffer, len, flags) - Async socket receive
|
||||
- async_send(socket, buffer, len, flags) - Async socket send
|
||||
- async_wait(handle) - Wait for async operation to complete
|
||||
- async_poll(handle) - Check if async operation is complete
|
||||
- async_result(handle) - Get result of completed async operation
|
||||
|
||||
**Socket Programming**
|
||||
- socket(), bind(), listen(), accept()
|
||||
- send(), recv(), close()
|
||||
@@ -118,13 +155,25 @@ int main() {
|
||||
### HTTP Server Examples
|
||||
|
||||
**Simple HTTP Server** (`examples/http_simple.rc`)
|
||||
Single-connection HTTP server that accepts one request and exits.
|
||||
Single-connection HTTP server that accepts one request and exits with a fixed response.
|
||||
|
||||
**Persistent HTTP Server** (`examples/http_persistent.rc`)
|
||||
HTTP server with request counter that continuously accepts connections.
|
||||
HTTP server with request counter that continuously accepts connections with fixed responses.
|
||||
|
||||
**Multi-Connection HTTP Server** (`examples/http_multi.rc`)
|
||||
HTTP server that handles up to 100 consecutive connections.
|
||||
**HTTP File Server** (`examples/http_fileserver.rc`)
|
||||
HTTP server that serves files from the filesystem. Handles up to 100 connections and streams file content using proper HTTP responses. Demonstrates socket programming, file I/O, and HTTP protocol implementation within RC's type system constraints.
|
||||
|
||||
Run examples:
|
||||
```bash
|
||||
make run-http-simple # Simple single-connection server
|
||||
make run-http-persistent # Persistent multi-connection server
|
||||
./bin/rc examples/http_fileserver.rc # File serving server
|
||||
```
|
||||
|
||||
Test with curl or browser:
|
||||
```bash
|
||||
curl http://localhost:8080/
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
@@ -148,7 +197,7 @@ rc/
|
||||
## Architecture
|
||||
|
||||
### Tokenizer
|
||||
Converts source code into tokens, handling keywords, identifiers, literals, and operators. Supports line comments (//) and escape sequences in strings.
|
||||
Converts source code into tokens, handling keywords, identifiers, literals, and operators. Supports line comments (//) and escape sequences in strings. Thread-safe token array access with mutex protection.
|
||||
|
||||
### Parser
|
||||
Recursive-descent parser with operator precedence:
|
||||
@@ -163,16 +212,57 @@ Recursive-descent parser with operator precedence:
|
||||
Direct execution model without intermediate bytecode. Uses a virtual memory array for stack and variables, with separate string pool for dynamic strings.
|
||||
|
||||
### Native Functions
|
||||
Extensible system for binding C functions. Current bindings include math operations, string manipulation, and socket programming.
|
||||
Extensible system for binding C functions. Current bindings include math operations, string manipulation, socket programming, async/await coroutines, and file I/O.
|
||||
|
||||
### Execution Context System
|
||||
Isolated execution environments for coroutines:
|
||||
- `ExecutionContext` structure encapsulates pc, sp, bp, memory, locals
|
||||
- `context_snapshot()` saves current execution state
|
||||
- `context_restore()` restores saved state
|
||||
- `context_destroy()` frees allocated memory
|
||||
- Prevents race conditions in async/coroutine execution
|
||||
|
||||
### Object-Oriented Programming
|
||||
Class-based OOP with full field mutation support:
|
||||
- Classes with fields and methods
|
||||
- Object creation with `new` keyword
|
||||
- Direct field access and mutation
|
||||
- Method calls with automatic object binding
|
||||
- Maximum 100 classes, 1000 objects total
|
||||
|
||||
## Stability and Reliability
|
||||
|
||||
### Async/Coroutine System
|
||||
RC features a robust async/coroutine implementation with proven stability:
|
||||
- **464+ consecutive test runs** without failures
|
||||
- Thread-safe execution using ExecutionContext isolation
|
||||
- Proper state management prevents race conditions
|
||||
- Dynamic memory allocation for coroutine arguments
|
||||
- Mutex-based synchronization ensures correctness
|
||||
|
||||
### Execution Context Architecture
|
||||
The interpreter uses an ExecutionContext system for coroutine isolation:
|
||||
- Each coroutine saves and restores execution state at runtime
|
||||
- Prevents stale state corruption during concurrent execution
|
||||
- Guarantees serial execution (thread-safe, not parallel)
|
||||
- Clean memory management with proper cleanup
|
||||
|
||||
For technical details, see:
|
||||
- `ASYNC_FIX_SOLUTION.md` - Async race condition fix documentation
|
||||
- `MEMORY_SAFETY_PLAN.md` - Future dynamic allocation roadmap
|
||||
|
||||
## Limitations
|
||||
|
||||
- Memory model uses long cells (not byte-accurate)
|
||||
- No support for structs, unions, or enums as user types
|
||||
- Limited type system (int and char* only)
|
||||
- Limited type system (int, double, and char*)
|
||||
- Double arithmetic requires explicit helper functions
|
||||
- No preprocessor directives
|
||||
- Error messages show token index rather than line/column
|
||||
- Pointer arithmetic works on virtual memory addresses
|
||||
- Maximum 100 concurrent async operations
|
||||
- Async execution is serialized (not parallel)
|
||||
- **Type system constraints**: String functions (substr, strpos, strcmp) work with string pointers (`char*`) but not with char arrays used as buffers for socket operations
|
||||
|
||||
## Testing
|
||||
|
||||
@@ -208,6 +298,8 @@ make run-string-manip
|
||||
- Stack-based allocation for local variables
|
||||
- String pool for dynamic string operations
|
||||
- Automatic overflow detection
|
||||
- Dynamic allocation for coroutine arguments
|
||||
- Proper cleanup in async/await operations
|
||||
|
||||
**Function Calls**
|
||||
- Stack-based activation records
|
||||
|
||||
+1445
-8
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
async int fetch_data(int id) {
|
||||
printf("Fetching data for ID: %d\n", id);
|
||||
int i = 0;
|
||||
int sum = 0;
|
||||
while (i < 1000) {
|
||||
sum = sum + i;
|
||||
i = i + 1;
|
||||
}
|
||||
printf("Data fetched for ID %d: value = %d\n", id, sum);
|
||||
return sum;
|
||||
}
|
||||
|
||||
async int process_data(int data) {
|
||||
printf("Processing data: %d\n", data);
|
||||
int result = data * 2;
|
||||
printf("Processed result: %d\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
async int save_result(int result) {
|
||||
printf("Saving result: %d\n", result);
|
||||
printf("Result saved successfully\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("=== Python-like Async/Await Demo ===\n\n");
|
||||
|
||||
printf("Example 1: Sequential async operations\n");
|
||||
int coro1 = fetch_data(1);
|
||||
int data = await(coro1);
|
||||
printf("Received data: %d\n\n", data);
|
||||
|
||||
printf("Example 2: Chained async operations\n");
|
||||
int fetch_coro = fetch_data(2);
|
||||
int fetched = await(fetch_coro);
|
||||
int process_coro = process_data(fetched);
|
||||
int processed = await(process_coro);
|
||||
int save_coro = save_result(processed);
|
||||
int saved = await(save_coro);
|
||||
printf("Pipeline complete, status: %d\n\n", saved);
|
||||
|
||||
printf("Example 3: Parallel execution with gather\n");
|
||||
int task1 = fetch_data(10);
|
||||
int task2 = fetch_data(20);
|
||||
int task3 = fetch_data(30);
|
||||
printf("Started 3 parallel tasks\n");
|
||||
gather(task1, task2, task3);
|
||||
printf("All parallel tasks completed\n\n");
|
||||
|
||||
printf("Example 4: Mixed parallel and sequential\n");
|
||||
int parallel1 = fetch_data(100);
|
||||
int parallel2 = fetch_data(200);
|
||||
gather(parallel1, parallel2);
|
||||
printf("Parallel fetch completed\n");
|
||||
|
||||
int sequential = process_data(500);
|
||||
int seq_result = await(sequential);
|
||||
printf("Sequential processing result: %d\n\n", seq_result);
|
||||
|
||||
printf("Example 5: Multiple gather batches\n");
|
||||
printf("Batch 1: Fetching\n");
|
||||
int batch1_a = fetch_data(1);
|
||||
int batch1_b = fetch_data(2);
|
||||
int batch1_c = fetch_data(3);
|
||||
gather(batch1_a, batch1_b, batch1_c);
|
||||
|
||||
printf("Batch 2: Processing\n");
|
||||
int batch2_a = process_data(100);
|
||||
int batch2_b = process_data(200);
|
||||
gather(batch2_a, batch2_b);
|
||||
|
||||
printf("All batches completed\n\n");
|
||||
|
||||
printf("=== Demo Complete ===\n");
|
||||
printf("This demonstrates Python-like async/await with:\n");
|
||||
printf("- async function definitions\n");
|
||||
printf("- await() for waiting on coroutines\n");
|
||||
printf("- gather() for parallel execution\n");
|
||||
printf("- Thread-safe execution\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
int main() {
|
||||
printf("=== Async I/O Demo: File I/O + Async I/O + Socket I/O ===\n\n");
|
||||
|
||||
printf("Step 1: Synchronous File I/O\n");
|
||||
int log_file = fopen("demo_log.txt", "w");
|
||||
fputs(log_file, "Server Log Started\n");
|
||||
fclose(log_file);
|
||||
printf("Created log file\n\n");
|
||||
|
||||
printf("Step 2: Async File Operations\n");
|
||||
log_file = fopen("demo_log.txt", "a");
|
||||
int write_op1 = async_fwrite(log_file, "Initializing server...\n", 24);
|
||||
int write_op2 = async_fwrite(log_file, "Binding to port 8080...\n", 25);
|
||||
|
||||
printf("Multiple async writes queued\n");
|
||||
async_wait(write_op1);
|
||||
async_wait(write_op2);
|
||||
printf("All async writes completed\n\n");
|
||||
|
||||
printf("Step 3: Socket Server Setup\n");
|
||||
int server_socket = socket(AF_INET(), SOCK_STREAM(), 0);
|
||||
if (server_socket < 0) {
|
||||
printf("ERROR: Could not create socket\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Socket created: %d\n", server_socket);
|
||||
|
||||
int bind_result = bind(server_socket, 8080);
|
||||
if (bind_result < 0) {
|
||||
printf("ERROR: Could not bind to port\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Bound to port 8080\n");
|
||||
|
||||
listen(server_socket, 5);
|
||||
printf("Listening for connections...\n\n");
|
||||
|
||||
printf("Step 4: Log async write while waiting for connection\n");
|
||||
int log_op = async_fwrite(log_file, "Waiting for client...\n", 23);
|
||||
|
||||
printf("Accepting client (this will wait for a connection)\n");
|
||||
printf("In another terminal, run: curl http://localhost:8080/\n\n");
|
||||
|
||||
int client = accept(server_socket);
|
||||
async_wait(log_op);
|
||||
printf("Client connected: %d\n\n", client);
|
||||
|
||||
printf("Step 5: Handle client with async socket I/O\n");
|
||||
int buffer[1024];
|
||||
int recv_op = async_recv(client, &buffer, 1024, 0);
|
||||
|
||||
printf("Async receive started...\n");
|
||||
while (async_poll(recv_op) == 0) {
|
||||
printf(".");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
int bytes = async_result(recv_op);
|
||||
printf("Received %d bytes\n", bytes);
|
||||
|
||||
char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nAsync I/O Demo Success!\n";
|
||||
int send_op = async_send(client, response, strlen(response), 0);
|
||||
async_wait(send_op);
|
||||
printf("Response sent asynchronously\n\n");
|
||||
|
||||
printf("Step 6: Final log entry\n");
|
||||
int final_log = async_fwrite(log_file, "Client served successfully\n", 28);
|
||||
async_wait(final_log);
|
||||
|
||||
close(client);
|
||||
close(server_socket);
|
||||
fclose(log_file);
|
||||
|
||||
printf("\nStep 7: Read log file\n");
|
||||
log_file = fopen("demo_log.txt", "r");
|
||||
char *line = fgets(log_file, 256);
|
||||
while (strlen(line) > 0 && feof(log_file) == 0) {
|
||||
printf("LOG: %s", line);
|
||||
line = fgets(log_file, 256);
|
||||
}
|
||||
fclose(log_file);
|
||||
|
||||
printf("\n=== Demo Complete ===\n");
|
||||
printf("Demonstrated: Sync File I/O, Async File I/O, Socket I/O, and Async Socket I/O\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
int deep_function_3() {
|
||||
int result = non_existent_variable * 5;
|
||||
return result;
|
||||
}
|
||||
|
||||
int deep_function_2(int x) {
|
||||
printf("In deep_function_2 with x=%d\n", x);
|
||||
int value = deep_function_3();
|
||||
return value + x;
|
||||
}
|
||||
|
||||
int deep_function_1(int a, int b) {
|
||||
printf("In deep_function_1 with a=%d, b=%d\n", a, b);
|
||||
int sum = a + b;
|
||||
int result = deep_function_2(sum);
|
||||
return result;
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("=== Error Handling Demonstration ===\n");
|
||||
printf("This program will intentionally trigger an error\n");
|
||||
printf("to demonstrate the enhanced error reporting system.\n\n");
|
||||
|
||||
int final_result = deep_function_1(10, 20);
|
||||
printf("Final result: %d\n", final_result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
|
||||
int main() {
|
||||
int server_fd;
|
||||
int client_fd;
|
||||
char buffer[4096];
|
||||
int bytes_received;
|
||||
int file;
|
||||
char *line;
|
||||
int line_len;
|
||||
char *response_header;
|
||||
int header_len;
|
||||
int total_sent;
|
||||
int count;
|
||||
char *filename;
|
||||
|
||||
server_fd = socket(AF_INET(), SOCK_STREAM(), 0);
|
||||
if (server_fd < 0) {
|
||||
printf("Failed to create socket\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (bind(server_fd, 8080) < 0) {
|
||||
printf("Failed to bind to port 8080\n");
|
||||
close(server_fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (listen(server_fd, 10) < 0) {
|
||||
printf("Failed to listen\n");
|
||||
close(server_fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("HTTP File Server listening on port 8080\n");
|
||||
printf("Serving README.md for all requests\n");
|
||||
printf("Example: http://localhost:8080/\n\n");
|
||||
|
||||
filename = "README.md";
|
||||
count = 1;
|
||||
while (count > 0) {
|
||||
printf("Waiting for connection %d\n", count + 1);
|
||||
client_fd = accept(server_fd);
|
||||
|
||||
if (client_fd < 0) {
|
||||
printf("Failed to accept connection\n");
|
||||
count = count + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Client connected\n");
|
||||
|
||||
bytes_received = recv(client_fd, buffer, 1024, 0);
|
||||
if (bytes_received <= 0) {
|
||||
printf("Failed to receive request\n");
|
||||
close(client_fd);
|
||||
count = count + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Received %d bytes\n", bytes_received);
|
||||
|
||||
file = fopen(filename, "r");
|
||||
|
||||
if (file == 0) {
|
||||
printf("File not found: %s\n", filename);
|
||||
response_header = "HTTP/1.1 404 Not Found\nContent-Type: text/html\n\n<html><body><h1>404 Not Found</h1><p>The requested file was not found.</p></body></html>";
|
||||
header_len = strlen(response_header);
|
||||
send(client_fd, response_header, header_len, 0);
|
||||
close(client_fd);
|
||||
count = count + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("File opened successfully\n");
|
||||
|
||||
response_header = "HTTP/1.1 200 OK\nContent-Type: text/plain\n\n";
|
||||
header_len = strlen(response_header);
|
||||
send(client_fd, response_header, header_len, 0);
|
||||
|
||||
total_sent = 0;
|
||||
while (feof(file) == 0) {
|
||||
line = fgets(file, 8192);
|
||||
line_len = strlen(line);
|
||||
if (line_len > 0) {
|
||||
send(client_fd, line, line_len, 0);
|
||||
total_sent = total_sent + line_len;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Sent %d bytes\n", total_sent);
|
||||
|
||||
fclose(file);
|
||||
close(client_fd);
|
||||
printf("Connection closed\n\n");
|
||||
|
||||
count = count + 1;
|
||||
}
|
||||
|
||||
close(server_fd);
|
||||
printf("Server shutdown after %d requests\n", count);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import os
|
||||
import fnmatch
|
||||
from pathlib import Path
|
||||
|
||||
gitignore_patterns = []
|
||||
if os.path.exists('.gitignore'):
|
||||
with open('.gitignore', 'r', encoding='utf-8') as gitignore_file:
|
||||
for line in gitignore_file:
|
||||
stripped_line = line.strip()
|
||||
if stripped_line and not stripped_line.startswith('#'):
|
||||
gitignore_patterns.append(stripped_line)
|
||||
|
||||
files_to_include = []
|
||||
for path in Path('.').rglob('*'):
|
||||
if path.is_file():
|
||||
relative_path = str(path.relative_to('.'))
|
||||
ignored = False
|
||||
for pattern in gitignore_patterns:
|
||||
if fnmatch.fnmatch(relative_path, pattern) or fnmatch.fnmatch(path.name, pattern):
|
||||
ignored = True
|
||||
break
|
||||
if ignored:
|
||||
continue
|
||||
extension = path.suffix.lower()
|
||||
if extension in ['.c', '.h', '.rc'] or path.name in ['TUTORIAL.md', 'README.md', 'CLAUDE.md']:
|
||||
files_to_include.append(path)
|
||||
|
||||
files_to_include.sort(key=lambda file_path: str(file_path))
|
||||
|
||||
with open('complete_source.md', 'w', encoding='utf-8') as output_file:
|
||||
for file_path in files_to_include:
|
||||
relative_path = str(file_path.relative_to('.'))
|
||||
extension = file_path.suffix.lower()
|
||||
if extension == '.c':
|
||||
file_type = 'C Source'
|
||||
language = 'c'
|
||||
elif extension == '.h':
|
||||
file_type = 'C Header'
|
||||
language = 'c'
|
||||
elif extension == '.rc':
|
||||
file_type = 'rc interpreter script file (this is used by the interpreter)'
|
||||
language = ''
|
||||
elif file_path.name.endswith('.md'):
|
||||
file_type = 'Markdown'
|
||||
language = 'markdown'
|
||||
output_file.write(f'# File: {relative_path} ({file_type})\n\n')
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as input_file:
|
||||
content = input_file.read()
|
||||
except UnicodeDecodeError:
|
||||
with open(file_path, 'r', encoding='latin-1') as input_file:
|
||||
content = input_file.read()
|
||||
output_file.write(f'```{language}\n{content}\n```\n\n')
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to run 'make test' 1000 times.
|
||||
This will execute the comprehensive test suite repeatedly.
|
||||
Note: This may take a significant amount of time.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
def run_make_test(iteration):
|
||||
"""Run make test for a given iteration."""
|
||||
print(f"Starting iteration {iteration}/1000...")
|
||||
start_time = time.time()
|
||||
try:
|
||||
result = subprocess.run(['make', 'test'], capture_output=True, text=True, timeout=300) # 5 min timeout per run
|
||||
end_time = time.time()
|
||||
duration = end_time - start_time
|
||||
|
||||
if result.returncode == 0:
|
||||
print(f"Iteration {iteration} PASSED in {duration:.2f} seconds")
|
||||
# Optionally, you can log the output if needed
|
||||
# print(result.stdout)
|
||||
else:
|
||||
print(f"Iteration {iteration} FAILED in {duration:.2f} seconds")
|
||||
print("STDOUT:", result.stdout)
|
||||
print("STDERR:", result.stderr)
|
||||
return False # Stop on failure
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f"Iteration {iteration} TIMED OUT after 300 seconds")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Iteration {iteration} ERROR: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def main():
|
||||
print("Running 'make test' 1000 times...")
|
||||
print("This will take approximately 1000 * test_duration (likely 10-30 seconds each) = 10,000-30,000 seconds (~3-8 hours)")
|
||||
print("Press Ctrl+C to interrupt if needed.\n")
|
||||
|
||||
for i in range(1, 1001):
|
||||
if not run_make_test(i):
|
||||
print(f"Stopping at iteration {i} due to failure.")
|
||||
sys.exit(1)
|
||||
|
||||
# Optional: small delay between runs
|
||||
time.sleep(0.1)
|
||||
|
||||
print("\nAll 1000 iterations completed successfully!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "types.h"
|
||||
#include "scope.h"
|
||||
|
||||
extern long memory[MEM_SIZE];
|
||||
extern int sp;
|
||||
extern int bp;
|
||||
extern int pc;
|
||||
extern long ax;
|
||||
extern int return_flag;
|
||||
extern Symbol locals[VAR_MAX];
|
||||
extern int loc_cnt;
|
||||
|
||||
ExecutionContext* context_create() {
|
||||
ExecutionContext *ctx = (ExecutionContext*)calloc(1, sizeof(ExecutionContext));
|
||||
if (!ctx) return NULL;
|
||||
|
||||
ctx->memory_size = MEM_SIZE;
|
||||
ctx->memory = (long*)calloc(MEM_SIZE, sizeof(long));
|
||||
if (!ctx->memory) {
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->loc_capacity = VAR_MAX;
|
||||
ctx->locals = (Symbol*)calloc(VAR_MAX, sizeof(Symbol));
|
||||
if (!ctx->locals) {
|
||||
free(ctx->memory);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->pc = 0;
|
||||
ctx->sp = 0;
|
||||
ctx->bp = 0;
|
||||
ctx->ax = 0;
|
||||
ctx->return_flag = 0;
|
||||
ctx->loc_cnt = 0;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void context_destroy(ExecutionContext *ctx) {
|
||||
if (!ctx) return;
|
||||
|
||||
if (ctx->memory) {
|
||||
free(ctx->memory);
|
||||
}
|
||||
if (ctx->locals) {
|
||||
free(ctx->locals);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
ExecutionContext* context_snapshot() {
|
||||
ExecutionContext *ctx = context_create();
|
||||
if (!ctx) return NULL;
|
||||
|
||||
int safe_sp = sp;
|
||||
if (safe_sp < 0) safe_sp = 0;
|
||||
if (safe_sp > MEM_SIZE) safe_sp = MEM_SIZE;
|
||||
|
||||
for (int i = 0; i < safe_sp; i++) {
|
||||
ctx->memory[i] = memory[i];
|
||||
}
|
||||
|
||||
int safe_loc_cnt = loc_cnt;
|
||||
if (safe_loc_cnt < 0) safe_loc_cnt = 0;
|
||||
if (safe_loc_cnt > VAR_MAX) safe_loc_cnt = VAR_MAX;
|
||||
|
||||
for (int i = 0; i < safe_loc_cnt; i++) {
|
||||
ctx->locals[i] = locals[i];
|
||||
}
|
||||
|
||||
ctx->pc = pc;
|
||||
ctx->sp = safe_sp;
|
||||
ctx->bp = bp;
|
||||
ctx->ax = ax;
|
||||
ctx->return_flag = return_flag;
|
||||
ctx->loc_cnt = safe_loc_cnt;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void context_restore(ExecutionContext *ctx) {
|
||||
if (!ctx) return;
|
||||
|
||||
int safe_sp = ctx->sp;
|
||||
if (safe_sp < 0) safe_sp = 0;
|
||||
if (safe_sp > MEM_SIZE) safe_sp = MEM_SIZE;
|
||||
|
||||
for (int i = 0; i < safe_sp; i++) {
|
||||
memory[i] = ctx->memory[i];
|
||||
}
|
||||
|
||||
int safe_loc_cnt = ctx->loc_cnt;
|
||||
if (safe_loc_cnt < 0) safe_loc_cnt = 0;
|
||||
if (safe_loc_cnt > VAR_MAX) safe_loc_cnt = VAR_MAX;
|
||||
|
||||
for (int i = 0; i < safe_loc_cnt; i++) {
|
||||
locals[i] = ctx->locals[i];
|
||||
}
|
||||
|
||||
pc = ctx->pc;
|
||||
sp = safe_sp;
|
||||
bp = ctx->bp;
|
||||
ax = ctx->ax;
|
||||
return_flag = ctx->return_flag;
|
||||
loc_cnt = safe_loc_cnt;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef DEBUG_MODE
|
||||
FILE *debug_file = NULL;
|
||||
#endif
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
#ifndef DEBUG_H
|
||||
#define DEBUG_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_MODE
|
||||
|
||||
extern FILE *debug_file;
|
||||
|
||||
#define DEBUG_INIT() do { \
|
||||
debug_file = fopen("debug.log", "w"); \
|
||||
if (debug_file) { \
|
||||
fprintf(debug_file, "=== RC Interpreter Debug Log ===\n"); \
|
||||
fflush(debug_file); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define DEBUG_CLOSE() do { \
|
||||
if (debug_file) { \
|
||||
fprintf(debug_file, "=== Debug Log End ===\n"); \
|
||||
fclose(debug_file); \
|
||||
debug_file = NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define DEBUG_LOG(fmt, ...) do { \
|
||||
if (debug_file) { \
|
||||
fprintf(debug_file, "[%s:%d in %s] " fmt "\n", \
|
||||
__FILE__, __LINE__, __func__, ##__VA_ARGS__); \
|
||||
fflush(debug_file); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define DEBUG_FUNC_ENTRY() DEBUG_LOG(">>> ENTER")
|
||||
#define DEBUG_FUNC_EXIT() DEBUG_LOG("<<< EXIT")
|
||||
|
||||
#define DEBUG_PTR(name, ptr) DEBUG_LOG("%s = %p", name, (void*)(ptr))
|
||||
#define DEBUG_INT(name, val) DEBUG_LOG("%s = %d", name, (int)(val))
|
||||
#define DEBUG_LONG(name, val) DEBUG_LOG("%s = %ld", name, (long)(val))
|
||||
#define DEBUG_STR(name, str) DEBUG_LOG("%s = \"%s\"", name, (str) ? (str) : "(null)")
|
||||
|
||||
#define DEBUG_MEMORY_ACCESS(addr, action) \
|
||||
DEBUG_LOG("MEMORY %s at addr=%d (MEM_SIZE=%d)", action, (int)(addr), MEM_SIZE)
|
||||
|
||||
#define DEBUG_TOKEN(idx) do { \
|
||||
if ((idx) >= 0 && (idx) < tk_idx && (idx) < MAX_TOK) { \
|
||||
DEBUG_LOG("TOKEN[%d]: type=%d, val=%ld, line=%d, file=%s", \
|
||||
idx, tokens[idx].type, tokens[idx].val, \
|
||||
tokens[idx].line, \
|
||||
tokens[idx].filename ? tokens[idx].filename : "<null>"); \
|
||||
} else { \
|
||||
DEBUG_LOG("TOKEN[%d]: OUT OF BOUNDS (tk_idx=%d)", idx, tk_idx); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define DEBUG_STACK_STATE() do { \
|
||||
DEBUG_LOG("STACK: sp=%d, bp=%d, pc=%d, ax=%ld", sp, bp, pc, ax); \
|
||||
} while(0)
|
||||
|
||||
#define DEBUG_CHECKPOINT(label) DEBUG_LOG("CHECKPOINT: %s", label)
|
||||
|
||||
#else
|
||||
|
||||
#define DEBUG_INIT() do {} while(0)
|
||||
#define DEBUG_CLOSE() do {} while(0)
|
||||
#define DEBUG_LOG(fmt, ...) do {} while(0)
|
||||
#define DEBUG_FUNC_ENTRY() do {} while(0)
|
||||
#define DEBUG_FUNC_EXIT() do {} while(0)
|
||||
#define DEBUG_PTR(name, ptr) do {} while(0)
|
||||
#define DEBUG_INT(name, val) do {} while(0)
|
||||
#define DEBUG_LONG(name, val) do {} while(0)
|
||||
#define DEBUG_STR(name, str) do {} while(0)
|
||||
#define DEBUG_MEMORY_ACCESS(addr, action) do {} while(0)
|
||||
#define DEBUG_TOKEN(idx) do {} while(0)
|
||||
#define DEBUG_STACK_STATE() do {} while(0)
|
||||
#define DEBUG_CHECKPOINT(label) do {} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "types.h"
|
||||
#include "error.h"
|
||||
#include "debug.h"
|
||||
|
||||
extern Token tokens[];
|
||||
extern int tk_idx;
|
||||
extern int pc;
|
||||
extern CallStack call_stack;
|
||||
extern pthread_rwlock_t tokens_rwlock;
|
||||
|
||||
void push_call_frame(const char *function_name, const char *filename, int line, int is_native) {
|
||||
if (call_stack.depth >= MAX_CALL_STACK) {
|
||||
return;
|
||||
}
|
||||
|
||||
CallStackFrame *frame = &call_stack.frames[call_stack.depth];
|
||||
frame->function_name = function_name;
|
||||
frame->filename = filename;
|
||||
frame->line = line;
|
||||
frame->is_native = is_native;
|
||||
call_stack.depth++;
|
||||
}
|
||||
|
||||
void pop_call_frame() {
|
||||
if (call_stack.depth > 0) {
|
||||
call_stack.depth--;
|
||||
}
|
||||
}
|
||||
|
||||
void print_stacktrace() {
|
||||
if (call_stack.depth == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
|
||||
fprintf(stderr, "║ STACK TRACE ║\n");
|
||||
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
|
||||
|
||||
int frames_to_show = call_stack.depth;
|
||||
if (frames_to_show > 10) {
|
||||
frames_to_show = 10;
|
||||
}
|
||||
|
||||
for (int i = call_stack.depth - 1; i >= call_stack.depth - frames_to_show; i--) {
|
||||
if (i < 0) break;
|
||||
|
||||
CallStackFrame *frame = &call_stack.frames[i];
|
||||
const char *type_marker = frame->is_native ? "[native]" : "[script]";
|
||||
|
||||
fprintf(stderr, " %s ", type_marker);
|
||||
|
||||
if (frame->function_name) {
|
||||
fprintf(stderr, "%s()", frame->function_name);
|
||||
} else {
|
||||
fprintf(stderr, "<anonymous>");
|
||||
}
|
||||
|
||||
if (frame->filename && frame->line > 0) {
|
||||
fprintf(stderr, "\n at %s:%d", frame->filename, frame->line);
|
||||
} else if (frame->filename) {
|
||||
fprintf(stderr, "\n at %s", frame->filename);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
if (call_stack.depth > 10) {
|
||||
fprintf(stderr, " ... %d more frames\n", call_stack.depth - 10);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
static const char* get_error_tip(const char *error_type, const char *message) {
|
||||
if (strstr(message, "index out of bounds") || strstr(message, "Token index")) {
|
||||
return "Check array bounds and ensure index is within valid range";
|
||||
}
|
||||
if (strstr(message, "Too many")) {
|
||||
return "Consider breaking down into smaller components or increasing limits";
|
||||
}
|
||||
if (strstr(message, "Stack overflow")) {
|
||||
return "Reduce recursion depth or local variable usage";
|
||||
}
|
||||
if (strstr(message, "Undefined variable")) {
|
||||
return "Ensure variable is declared before use with: int varname;";
|
||||
}
|
||||
if (strstr(message, "Unknown function")) {
|
||||
return "Check function name spelling and ensure it's defined before calling";
|
||||
}
|
||||
if (strstr(message, "Unexpected token")) {
|
||||
return "Check for missing semicolons, braces, or parentheses";
|
||||
}
|
||||
if (strstr(message, "division by zero") || strstr(message, "divide by zero")) {
|
||||
return "Add a check: if (divisor != 0) before division";
|
||||
}
|
||||
if (strstr(message, "Null pointer")) {
|
||||
return "Check if pointer is null before dereferencing: if (ptr != null)";
|
||||
}
|
||||
if (strstr(message, "Memory")) {
|
||||
return "Reduce memory usage or check for memory leaks";
|
||||
}
|
||||
if (strstr(message, "Unknown class") || strstr(message, "Unknown field") ||
|
||||
strstr(message, "Unknown method")) {
|
||||
return "Verify class/field/method name and ensure class is defined";
|
||||
}
|
||||
|
||||
return "Review the code at the error location and check for typos";
|
||||
}
|
||||
|
||||
static void print_code_context(int token_index) {
|
||||
if (pthread_rwlock_tryrdlock(&tokens_rwlock) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (token_index < 0 || token_index >= tk_idx) {
|
||||
pthread_rwlock_unlock(&tokens_rwlock);
|
||||
return;
|
||||
}
|
||||
|
||||
Token *error_token = &tokens[token_index];
|
||||
if (!error_token->filename || error_token->line <= 0) {
|
||||
pthread_rwlock_unlock(&tokens_rwlock);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
|
||||
fprintf(stderr, "║ CODE CONTEXT ║\n");
|
||||
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
|
||||
|
||||
int start_token = token_index - 3;
|
||||
int end_token = token_index + 3;
|
||||
|
||||
if (start_token < 0) start_token = 0;
|
||||
if (end_token >= tk_idx) end_token = tk_idx - 1;
|
||||
|
||||
for (int i = start_token; i <= end_token && i < tk_idx; i++) {
|
||||
if (i < 0) continue;
|
||||
|
||||
Token *t = &tokens[i];
|
||||
const char *marker = (i == token_index) ? ">>> " : " ";
|
||||
|
||||
fprintf(stderr, "%s", marker);
|
||||
|
||||
if (t->type >= 32 && t->type < 127) {
|
||||
fprintf(stderr, "'%c' ", t->type);
|
||||
} else if (t->type == Num) {
|
||||
fprintf(stderr, "%ld ", t->val);
|
||||
} else if (t->type == Id && t->text) {
|
||||
fprintf(stderr, "%.*s ", (int)t->val, t->text);
|
||||
} else if (t->type == Str && t->text) {
|
||||
fprintf(stderr, "\"%s\" ", t->text);
|
||||
} else {
|
||||
fprintf(stderr, "[token:%d] ", t->type);
|
||||
}
|
||||
|
||||
if (i == token_index) {
|
||||
fprintf(stderr, "<-- ERROR HERE");
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
pthread_rwlock_unlock(&tokens_rwlock);
|
||||
}
|
||||
|
||||
void error_with_context(const char *error_type, const char *message, const char *tip) {
|
||||
DEBUG_LOG("FATAL ERROR: %s - %s", error_type ? error_type : "General Error", message);
|
||||
DEBUG_STACK_STATE();
|
||||
DEBUG_TOKEN(pc);
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
|
||||
fprintf(stderr, "║ RUNTIME ERROR ║\n");
|
||||
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
fprintf(stderr, " Error Type: %s\n", error_type ? error_type : "General Error");
|
||||
fprintf(stderr, " Message: %s\n", message);
|
||||
|
||||
if (pc >= 0 && pc < tk_idx) {
|
||||
Token *current_token = &tokens[pc];
|
||||
if (current_token->filename) {
|
||||
fprintf(stderr, " Location: %s", current_token->filename);
|
||||
if (current_token->line > 0) {
|
||||
fprintf(stderr, ":%d", current_token->line);
|
||||
if (current_token->column > 0) {
|
||||
fprintf(stderr, ":%d", current_token->column);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
const char *actual_tip = tip ? tip : get_error_tip(error_type, message);
|
||||
if (actual_tip) {
|
||||
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
|
||||
fprintf(stderr, "║ TIP ║\n");
|
||||
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
|
||||
fprintf(stderr, " 💡 %s\n", actual_tip);
|
||||
}
|
||||
|
||||
print_code_context(pc);
|
||||
print_stacktrace();
|
||||
|
||||
DEBUG_CLOSE();
|
||||
exit(1);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef ERROR_H
|
||||
#define ERROR_H
|
||||
|
||||
void push_call_frame(const char *function_name, const char *filename, int line, int is_native);
|
||||
void pop_call_frame();
|
||||
void print_stacktrace();
|
||||
void error_with_context(const char *error_type, const char *message, const char *tip);
|
||||
|
||||
#endif
|
||||
+20
-2
@@ -8,11 +8,29 @@ int sp = 0;
|
||||
int bp = 0;
|
||||
Symbol locals[VAR_MAX];
|
||||
int loc_cnt = 0;
|
||||
Func funcs[100];
|
||||
Func funcs[MAX_FUNCTIONS];
|
||||
int func_cnt = 0;
|
||||
NativeFuncDef native_funcs[100];
|
||||
NativeFuncDef native_funcs[MAX_NATIVE_FUNCTIONS];
|
||||
int native_func_cnt = 0;
|
||||
char *src_code;
|
||||
char str_pool[STR_POOL_SIZE];
|
||||
int str_pool_idx = 0;
|
||||
long ax = 0;
|
||||
int return_flag = 0;
|
||||
Coroutine coroutines[MAX_COROUTINES];
|
||||
int coroutine_count = 0;
|
||||
ClassDef classes[MAX_CLASSES];
|
||||
int class_count = 0;
|
||||
Object objects[MAX_OBJECTS];
|
||||
int object_count = 0;
|
||||
CallStack call_stack = {{}, 0};
|
||||
pthread_rwlock_t tokens_rwlock;
|
||||
pthread_mutex_t str_pool_mutex;
|
||||
pthread_mutex_t memory_mutex;
|
||||
pthread_mutex_t locals_mutex;
|
||||
pthread_mutex_t interpreter_state_mutex;
|
||||
pthread_rwlock_t funcs_rwlock;
|
||||
pthread_rwlock_t native_funcs_rwlock;
|
||||
pthread_rwlock_t classes_rwlock;
|
||||
pthread_mutex_t objects_mutex;
|
||||
pthread_mutex_t call_stack_mutex;
|
||||
|
||||
+224
-62
@@ -5,14 +5,12 @@
|
||||
#include "types.h"
|
||||
#include "interpreter.h"
|
||||
#include "parser.h"
|
||||
#include "error.h"
|
||||
#include "debug.h"
|
||||
#include "memory.h"
|
||||
|
||||
void error(char *msg) {
|
||||
if (pc >= 0 && pc < MAX_TOK && pc < tk_idx) {
|
||||
printf("Error at token %d ('%c'): %s\n", pc, tokens[pc].type, msg);
|
||||
} else {
|
||||
printf("Error at token %d: %s\n", pc, msg);
|
||||
}
|
||||
exit(1);
|
||||
error_with_context("ParseError", msg, NULL);
|
||||
}
|
||||
|
||||
void match(int type) {
|
||||
@@ -35,7 +33,7 @@ int find_local(char *name, int len) {
|
||||
|
||||
int find_func(char *name, int len) {
|
||||
if (!name || len < 0 || len >= 32) return -1;
|
||||
if (func_cnt > 100) func_cnt = 100;
|
||||
if (func_cnt > MAX_FUNCTIONS) func_cnt = MAX_FUNCTIONS;
|
||||
for (int i = 0; i < func_cnt; i++) {
|
||||
if (!strncmp(funcs[i].name, name, len) && funcs[i].name[len] == 0)
|
||||
return i;
|
||||
@@ -45,7 +43,7 @@ int find_func(char *name, int len) {
|
||||
|
||||
int find_native_func(char *name, int len) {
|
||||
if (!name || len < 0 || len >= 32) return -1;
|
||||
if (native_func_cnt > 100) native_func_cnt = 100;
|
||||
if (native_func_cnt > MAX_NATIVE_FUNCTIONS) native_func_cnt = MAX_NATIVE_FUNCTIONS;
|
||||
for (int i = 0; i < native_func_cnt; i++) {
|
||||
if (!strncmp(native_funcs[i].name, name, len) && native_funcs[i].name[len] == 0)
|
||||
return i;
|
||||
@@ -69,15 +67,44 @@ void statement() {
|
||||
if (pc >= MAX_TOK || pc >= tk_idx) {
|
||||
error("Token index out of bounds in statement");
|
||||
}
|
||||
if (tokens[pc].type == Class) {
|
||||
pc++;
|
||||
while (pc < MAX_TOK && pc < tk_idx && tokens[pc].type != Id) pc++;
|
||||
if (pc < MAX_TOK) pc++;
|
||||
if (pc < MAX_TOK && tokens[pc].type == ':') {
|
||||
pc++;
|
||||
while (pc < MAX_TOK && tokens[pc].type != '{') pc++;
|
||||
}
|
||||
if (pc < MAX_TOK && tokens[pc].type == '{') {
|
||||
int brace = 1;
|
||||
pc++;
|
||||
while (brace > 0 && pc < MAX_TOK && pc < tk_idx) {
|
||||
if (tokens[pc].type == '{') brace++;
|
||||
if (tokens[pc].type == '}') brace--;
|
||||
pc++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (tokens[pc].type == '{') {
|
||||
pc++;
|
||||
while (pc < MAX_TOK && pc < tk_idx && tokens[pc].type != '}' && tokens[pc].type != 0) {
|
||||
statement();
|
||||
if (ax == -999) break;
|
||||
if (return_flag || ax == -998 || ax == -997) {
|
||||
int brace = 1;
|
||||
while (brace > 0 && pc < MAX_TOK && pc < tk_idx && tokens[pc].type != 0) {
|
||||
if (tokens[pc].type == '{') brace++;
|
||||
if (tokens[pc].type == '}') brace--;
|
||||
pc++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
match('}');
|
||||
}
|
||||
else if (tokens[pc].type == Int || tokens[pc].type == Char) {
|
||||
else if (tokens[pc].type == Int || tokens[pc].type == Char || tokens[pc].type == Double ||
|
||||
(tokens[pc].type == Id && find_class(tokens[pc].text, tokens[pc].val) >= 0)) {
|
||||
int var_type = tokens[pc].type;
|
||||
pc++;
|
||||
while (pc < MAX_TOK && pc < tk_idx && tokens[pc].type != ';') {
|
||||
while (pc < MAX_TOK && pc < tk_idx && tokens[pc].type == '*') pc++;
|
||||
@@ -85,38 +112,75 @@ void statement() {
|
||||
Token *t = &tokens[pc];
|
||||
match(Id);
|
||||
|
||||
if (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) {
|
||||
if (!mem_check_sp(sp + 1, alloc_ctx)) {
|
||||
error("Stack overflow");
|
||||
}
|
||||
|
||||
int addr = sp;
|
||||
Symbol *s = &locals[loc_cnt++];
|
||||
strncpy(s->name, t->text, t->val); s->name[t->val] = 0;
|
||||
if (!t->text) {
|
||||
error("Invalid token text");
|
||||
}
|
||||
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++;
|
||||
int size = (int)expression();
|
||||
long size_val = expression();
|
||||
match(']');
|
||||
|
||||
if (size_val < 0) {
|
||||
error("Array size must be positive");
|
||||
}
|
||||
if (size_val > MEM_SIZE) {
|
||||
error("Array size too large");
|
||||
}
|
||||
|
||||
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 {
|
||||
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++;
|
||||
}
|
||||
@@ -129,42 +193,45 @@ void statement() {
|
||||
match(')');
|
||||
if (cond) {
|
||||
statement();
|
||||
if (ax == -999) return;
|
||||
if (return_flag || ax == -998 || ax == -997) return;
|
||||
if (pc < MAX_TOK && pc < tk_idx && tokens[pc].type == Else) { pc++; skip_block(); }
|
||||
} else {
|
||||
skip_block();
|
||||
if (pc < MAX_TOK && pc < tk_idx && tokens[pc].type == Else) {
|
||||
pc++;
|
||||
statement();
|
||||
if (ax == -999) return;
|
||||
if (return_flag || ax == -998 || ax == -997) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tokens[pc].type == While) {
|
||||
pc++;
|
||||
int loop_start = pc;
|
||||
match('(');
|
||||
long cond = expression();
|
||||
match(')');
|
||||
if (!cond) {
|
||||
skip_block();
|
||||
} else {
|
||||
int iteration_count = 0;
|
||||
while (1) {
|
||||
if (++iteration_count > 1000000) {
|
||||
error("Potential infinite loop detected");
|
||||
}
|
||||
statement();
|
||||
if (ax == -999) return;
|
||||
int save_pc = pc;
|
||||
if (loop_start >= MAX_TOK || loop_start >= tk_idx) {
|
||||
error("Loop start out of bounds");
|
||||
}
|
||||
pc = loop_start;
|
||||
match('(');
|
||||
cond = expression();
|
||||
match(')');
|
||||
if (!cond) { pc = save_pc; break; }
|
||||
int body_start;
|
||||
int iteration_count = 0;
|
||||
while (1) {
|
||||
if (++iteration_count > 1000000) {
|
||||
error("Potential infinite loop detected");
|
||||
}
|
||||
pc = loop_start;
|
||||
match('(');
|
||||
long cond = expression();
|
||||
match(')');
|
||||
if (!cond) {
|
||||
skip_block();
|
||||
break;
|
||||
}
|
||||
body_start = pc;
|
||||
statement();
|
||||
if (return_flag) return;
|
||||
if (ax == -998) {
|
||||
ax = 0;
|
||||
pc = body_start;
|
||||
skip_block();
|
||||
break;
|
||||
}
|
||||
if (ax == -997) {
|
||||
ax = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,7 +240,17 @@ void statement() {
|
||||
if (pc < MAX_TOK && pc < tk_idx && tokens[pc].type != ';') ax = expression();
|
||||
else ax = 0;
|
||||
match(';');
|
||||
ax = -999;
|
||||
return_flag = 1;
|
||||
}
|
||||
else if (tokens[pc].type == Break) {
|
||||
pc++;
|
||||
match(';');
|
||||
ax = -998;
|
||||
}
|
||||
else if (tokens[pc].type == Continue) {
|
||||
pc++;
|
||||
match(';');
|
||||
ax = -997;
|
||||
}
|
||||
else if (tokens[pc].type == Printf) {
|
||||
pc++;
|
||||
@@ -187,16 +264,35 @@ void statement() {
|
||||
|
||||
char *p = fmt;
|
||||
while (*p) {
|
||||
if (*p == '%' && (p[1] == 'd' || p[1] == 's')) {
|
||||
p++;
|
||||
match(',');
|
||||
long val = expression();
|
||||
if (*p == 'd') printf("%ld", val);
|
||||
else if (*p == 's') {
|
||||
char *str = (char*)val;
|
||||
if (str) printf("%s", str);
|
||||
if (*p == '%' && p[1]) {
|
||||
if (p[1] == 'd' || p[1] == 's' || p[1] == 'f') {
|
||||
p++;
|
||||
if (pc >= MAX_TOK || pc >= tk_idx) {
|
||||
error("Unexpected end of tokens in printf");
|
||||
}
|
||||
match(',');
|
||||
long val = expression();
|
||||
if (*p == 'd') {
|
||||
printf("%ld", val);
|
||||
} else if (*p == 'f') {
|
||||
union { double d; long l; } u;
|
||||
u.l = val;
|
||||
printf("%f", u.d);
|
||||
} else if (*p == 's') {
|
||||
char *str = (char*)val;
|
||||
if (str) {
|
||||
printf("%s", str);
|
||||
} else {
|
||||
printf("(null)");
|
||||
}
|
||||
}
|
||||
p++;
|
||||
} else if (p[1] == '%') {
|
||||
putchar('%');
|
||||
p += 2;
|
||||
} else {
|
||||
putchar(*p++);
|
||||
}
|
||||
p++;
|
||||
} else {
|
||||
putchar(*p++);
|
||||
}
|
||||
@@ -211,23 +307,89 @@ void statement() {
|
||||
}
|
||||
|
||||
void scan_functions() {
|
||||
char current_class[32] = "";
|
||||
int in_class = 0;
|
||||
|
||||
int i = 0;
|
||||
while (i < MAX_TOK && i < tk_idx && tokens[i].type != 0) {
|
||||
if (i + 2 < MAX_TOK && i + 2 < tk_idx &&
|
||||
(tokens[i].type == Int || tokens[i].type == Char) &&
|
||||
tokens[i+1].type == Id && tokens[i+2].type == '(') {
|
||||
if (tokens[i].type == Class) {
|
||||
i++;
|
||||
if (i < MAX_TOK && tokens[i].type == Id) {
|
||||
Token *class_name = &tokens[i];
|
||||
int name_len = class_name->val;
|
||||
if (name_len > 31) name_len = 31;
|
||||
strncpy(current_class, class_name->text, name_len);
|
||||
current_class[name_len] = 0;
|
||||
i++;
|
||||
}
|
||||
if (i < MAX_TOK && tokens[i].type == ':') {
|
||||
i++;
|
||||
if (i < MAX_TOK && tokens[i].type == Id) i++;
|
||||
}
|
||||
if (i < MAX_TOK && tokens[i].type == '{') {
|
||||
in_class = 1;
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (func_cnt >= 100) {
|
||||
if (in_class && tokens[i].type == '}') {
|
||||
in_class = 0;
|
||||
current_class[0] = 0;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int is_async = 0;
|
||||
int offset = 0;
|
||||
if (tokens[i].type == Async) {
|
||||
is_async = 1;
|
||||
offset = 1;
|
||||
}
|
||||
|
||||
int is_static = 0;
|
||||
if (tokens[i + offset].type == Static) {
|
||||
is_static = 1;
|
||||
offset++;
|
||||
}
|
||||
|
||||
int is_constructor = 0;
|
||||
int is_destructor = 0;
|
||||
if (tokens[i + offset].type == Constructor) {
|
||||
is_constructor = 1;
|
||||
offset++;
|
||||
} else if (tokens[i + offset].type == Destructor) {
|
||||
is_destructor = 1;
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (i + 2 + offset < MAX_TOK && i + 2 + offset < tk_idx &&
|
||||
(tokens[i+offset].type == Int || tokens[i+offset].type == Char ||
|
||||
tokens[i+offset].type == Double || tokens[i+offset].type == Void) &&
|
||||
tokens[i+1+offset].type == Id && tokens[i+2+offset].type == '(') {
|
||||
|
||||
if (func_cnt >= MAX_FUNCTIONS) {
|
||||
error("Too many functions defined");
|
||||
}
|
||||
Func *f = &funcs[func_cnt++];
|
||||
Token *name = &tokens[i+1];
|
||||
strncpy(f->name, name->text, name->val); f->name[name->val] = 0;
|
||||
Token *name = &tokens[i+1+offset];
|
||||
|
||||
i += 3;
|
||||
if (in_class && current_class[0] != 0 && !is_constructor && !is_destructor) {
|
||||
snprintf(f->name, 31, "%s_%.*s", current_class, name->val, name->text);
|
||||
} else if (in_class && (is_constructor || is_destructor)) {
|
||||
snprintf(f->name, 31, "%s_%s", current_class,
|
||||
is_constructor ? "constructor" : "destructor");
|
||||
} else {
|
||||
strncpy(f->name, name->text, name->val);
|
||||
f->name[name->val] = 0;
|
||||
}
|
||||
f->is_async = is_async;
|
||||
|
||||
i += 3 + offset;
|
||||
f->params_start = i;
|
||||
int params = 0;
|
||||
while(i < MAX_TOK && i < tk_idx && tokens[i].type != ')') {
|
||||
if (tokens[i].type == Int || tokens[i].type == Char) {
|
||||
if (tokens[i].type == Int || tokens[i].type == Char || tokens[i].type == Double) {
|
||||
params++;
|
||||
i++;
|
||||
while (i < MAX_TOK && i < tk_idx && tokens[i].type == '*') i++;
|
||||
|
||||
@@ -9,5 +9,12 @@ int find_native_func(char *name, int len);
|
||||
void statement();
|
||||
void skip_block();
|
||||
void scan_functions();
|
||||
void scan_classes();
|
||||
int find_class(char *name, int len);
|
||||
int find_class_field(int class_idx, char *name, int len);
|
||||
int find_class_method(int class_idx, char *name, int len);
|
||||
long allocate_object(int class_idx);
|
||||
void free_object(long obj_ptr);
|
||||
void call_destructor(long obj_ptr);
|
||||
|
||||
#endif
|
||||
|
||||
+53
-14
@@ -1,39 +1,64 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <libgen.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "types.h"
|
||||
#include "tokenizer.h"
|
||||
#include "interpreter.h"
|
||||
#include "native_functions.h"
|
||||
#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);
|
||||
pthread_mutex_init(&memory_mutex, NULL);
|
||||
pthread_mutex_init(&locals_mutex, NULL);
|
||||
pthread_mutex_init(&interpreter_state_mutex, NULL);
|
||||
pthread_rwlock_init(&funcs_rwlock, NULL);
|
||||
pthread_rwlock_init(&native_funcs_rwlock, NULL);
|
||||
pthread_rwlock_init(&classes_rwlock, NULL);
|
||||
pthread_mutex_init(&objects_mutex, NULL);
|
||||
pthread_mutex_init(&call_stack_mutex, NULL);
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: rc <file.rc>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE *f = fopen(argv[1], "rb");
|
||||
if (!f) {
|
||||
printf("Could not open file.\n");
|
||||
return 1;
|
||||
}
|
||||
char file_path[512];
|
||||
strncpy(file_path, argv[1], 511);
|
||||
file_path[511] = 0;
|
||||
|
||||
src_code = malloc(MAX_SRC + 1);
|
||||
char dir_path[512];
|
||||
strncpy(dir_path, argv[1], 511);
|
||||
dir_path[511] = 0;
|
||||
char *dir = dirname(dir_path);
|
||||
|
||||
src_code = preprocess(file_path, dir);
|
||||
if (!src_code) {
|
||||
printf("Memory allocation failed.\n");
|
||||
fclose(f);
|
||||
printf("Preprocessing failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t n = fread(src_code, 1, MAX_SRC, f);
|
||||
if (n >= MAX_SRC) n = MAX_SRC - 1;
|
||||
src_code[n] = 0;
|
||||
fclose(f);
|
||||
|
||||
register_native_functions();
|
||||
|
||||
tokenize(src_code);
|
||||
tokenize_with_location(src_code, file_path);
|
||||
|
||||
scan_classes();
|
||||
scan_functions();
|
||||
|
||||
link_class_methods();
|
||||
|
||||
int main_idx = find_func("main", 4);
|
||||
if (main_idx == -1 || main_idx >= func_cnt) {
|
||||
printf("No main function found.\n");
|
||||
@@ -53,5 +78,19 @@ 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);
|
||||
pthread_mutex_destroy(&locals_mutex);
|
||||
pthread_mutex_destroy(&interpreter_state_mutex);
|
||||
pthread_rwlock_destroy(&funcs_rwlock);
|
||||
pthread_rwlock_destroy(&native_funcs_rwlock);
|
||||
pthread_rwlock_destroy(&classes_rwlock);
|
||||
pthread_mutex_destroy(&objects_mutex);
|
||||
pthread_mutex_destroy(&call_stack_mutex);
|
||||
|
||||
DEBUG_LOG("RC Interpreter exiting normally");
|
||||
DEBUG_CLOSE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
+267
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
+3
-461
@@ -1,18 +1,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "types.h"
|
||||
#include "native_functions.h"
|
||||
#include "interpreter.h"
|
||||
#include "stdlib/stdlib.h"
|
||||
|
||||
void register_native_func(char *name, NativeFunc func) {
|
||||
if (!name || !func || native_func_cnt >= 100) {
|
||||
if (!name || !func || native_func_cnt >= MAX_NATIVE_FUNCTIONS) {
|
||||
return;
|
||||
}
|
||||
NativeFuncDef *nf = &native_funcs[native_func_cnt++];
|
||||
@@ -21,458 +15,6 @@ void register_native_func(char *name, NativeFunc func) {
|
||||
nf->func = func;
|
||||
}
|
||||
|
||||
long native_socket(long *args, int argc) {
|
||||
if (!args || argc < 3) {
|
||||
return -1;
|
||||
}
|
||||
int domain = (int)args[0];
|
||||
int type = (int)args[1];
|
||||
int protocol = (int)args[2];
|
||||
return socket(domain, type, protocol);
|
||||
}
|
||||
|
||||
long native_bind(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
int sockfd = (int)args[0];
|
||||
int port = (int)args[1];
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
return bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
|
||||
}
|
||||
|
||||
long native_listen(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
int sockfd = (int)args[0];
|
||||
int backlog = (int)args[1];
|
||||
return listen(sockfd, backlog);
|
||||
}
|
||||
|
||||
long native_accept(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return -1;
|
||||
}
|
||||
int sockfd = (int)args[0];
|
||||
return accept(sockfd, NULL, NULL);
|
||||
}
|
||||
|
||||
long native_recv(long *args, int argc) {
|
||||
if (!args || argc < 4) {
|
||||
return -1;
|
||||
}
|
||||
int sockfd = (int)args[0];
|
||||
int addr = (int)args[1];
|
||||
int len = (int)args[2];
|
||||
int flags = (int)args[3];
|
||||
|
||||
if (addr < 0 || addr >= MEM_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char temp_buf[8192];
|
||||
if (len > 8192) len = 8192;
|
||||
if (len < 0) len = 0;
|
||||
|
||||
if (addr + len > MEM_SIZE) {
|
||||
len = MEM_SIZE - addr;
|
||||
}
|
||||
|
||||
int result = recv(sockfd, temp_buf, len, flags);
|
||||
if (result > 0) {
|
||||
for (int i = 0; i < result && addr + i < MEM_SIZE; i++) {
|
||||
memory[addr + i] = temp_buf[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
long native_send(long *args, int argc) {
|
||||
if (!args || argc < 4) {
|
||||
return -1;
|
||||
}
|
||||
int sockfd = (int)args[0];
|
||||
long buf_arg = args[1];
|
||||
int len = (int)args[2];
|
||||
int flags = (int)args[3];
|
||||
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf_arg > MEM_SIZE * 8 || buf_arg < 0) {
|
||||
return send(sockfd, (char*)buf_arg, len, flags);
|
||||
}
|
||||
|
||||
if (buf_arg >= MEM_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char temp_buf[8192];
|
||||
if (len > 8192) len = 8192;
|
||||
|
||||
if (buf_arg + len > MEM_SIZE) {
|
||||
len = MEM_SIZE - buf_arg;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len && buf_arg + i < MEM_SIZE; i++) {
|
||||
temp_buf[i] = (char)memory[buf_arg + i];
|
||||
}
|
||||
|
||||
return send(sockfd, temp_buf, len, flags);
|
||||
}
|
||||
|
||||
long native_close(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return -1;
|
||||
}
|
||||
int fd = (int)args[0];
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
long native_strlen(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
if (!str) {
|
||||
return 0;
|
||||
}
|
||||
return strlen(str);
|
||||
}
|
||||
|
||||
long native_AF_INET(long *args, int argc) {
|
||||
return AF_INET;
|
||||
}
|
||||
|
||||
long native_SOCK_STREAM(long *args, int argc) {
|
||||
return SOCK_STREAM;
|
||||
}
|
||||
|
||||
long native_sqrt(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)sqrt((double)args[0]);
|
||||
}
|
||||
|
||||
long native_pow(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return 0;
|
||||
}
|
||||
return (long)pow((double)args[0], (double)args[1]);
|
||||
}
|
||||
|
||||
long native_sin(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)(sin((double)args[0]) * 1000000);
|
||||
}
|
||||
|
||||
long native_cos(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)(cos((double)args[0]) * 1000000);
|
||||
}
|
||||
|
||||
long native_tan(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)(tan((double)args[0]) * 1000000);
|
||||
}
|
||||
|
||||
long native_abs(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)abs((int)args[0]);
|
||||
}
|
||||
|
||||
long native_floor(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)floor((double)args[0]);
|
||||
}
|
||||
|
||||
long native_ceil(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)ceil((double)args[0]);
|
||||
}
|
||||
|
||||
long native_strpos(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
char *haystack = (char*)args[0];
|
||||
char *needle = (char*)args[1];
|
||||
if (!haystack || !needle) {
|
||||
return -1;
|
||||
}
|
||||
char *pos = strstr(haystack, needle);
|
||||
if (pos) {
|
||||
return pos - haystack;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
long native_substr(long *args, int argc) {
|
||||
static char empty_str[] = "";
|
||||
if (!args || argc < 3) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
if (!str) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
int start = (int)args[1];
|
||||
int length = (int)args[2];
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
char *result = &str_pool[str_pool_idx];
|
||||
int str_len = strlen(str);
|
||||
|
||||
if (start < 0) start = 0;
|
||||
if (start >= str_len) return (long)empty_str;
|
||||
if (start + length > str_len) length = str_len - start;
|
||||
if (length < 0) return (long)empty_str;
|
||||
|
||||
if (str_pool_idx + length + 1 >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
strncpy(result, str + start, length);
|
||||
result[length] = 0;
|
||||
|
||||
str_pool_idx += length + 1;
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
long native_upper(long *args, int argc) {
|
||||
static char empty_str[] = "";
|
||||
if (!args || argc < 1) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
if (!str) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
char *result = &str_pool[str_pool_idx];
|
||||
int len = strlen(str);
|
||||
|
||||
if (str_pool_idx + len + 1 >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= len; i++) {
|
||||
result[i] = toupper(str[i]);
|
||||
}
|
||||
|
||||
str_pool_idx += len + 1;
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
long native_lower(long *args, int argc) {
|
||||
static char empty_str[] = "";
|
||||
if (!args || argc < 1) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
if (!str) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
char *result = &str_pool[str_pool_idx];
|
||||
int len = strlen(str);
|
||||
|
||||
if (str_pool_idx + len + 1 >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= len; i++) {
|
||||
result[i] = tolower(str[i]);
|
||||
}
|
||||
|
||||
str_pool_idx += len + 1;
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
long native_strip(long *args, int argc) {
|
||||
static char empty_str[] = "";
|
||||
if (!args || argc < 1) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
if (!str) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
char *result = &str_pool[str_pool_idx];
|
||||
|
||||
while (*str && isspace(*str)) str++;
|
||||
|
||||
int len = strlen(str);
|
||||
while (len > 0 && isspace(str[len - 1])) len--;
|
||||
|
||||
if (str_pool_idx + len + 1 >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
strncpy(result, str, len);
|
||||
result[len] = 0;
|
||||
|
||||
str_pool_idx += len + 1;
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
long native_replace(long *args, int argc) {
|
||||
static char empty_str[] = "";
|
||||
if (!args || argc < 3) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
char *old_str = (char*)args[1];
|
||||
char *new_str = (char*)args[2];
|
||||
if (!str || !old_str || !new_str) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
char *result = &str_pool[str_pool_idx];
|
||||
char *src = str;
|
||||
char *dst = result;
|
||||
int old_len = strlen(old_str);
|
||||
int new_len = strlen(new_str);
|
||||
|
||||
if (old_len == 0) {
|
||||
int str_len = strlen(str);
|
||||
if (str_pool_idx + str_len + 1 >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
strcpy(result, str);
|
||||
str_pool_idx += str_len + 1;
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
while (*src) {
|
||||
if (strncmp(src, old_str, old_len) == 0) {
|
||||
int remaining = strlen(src + old_len);
|
||||
int current_pos = dst - result;
|
||||
if (str_pool_idx + current_pos + new_len + remaining + 1 >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
*dst = 0;
|
||||
str_pool_idx += current_pos + 1;
|
||||
return (long)result;
|
||||
}
|
||||
strcpy(dst, new_str);
|
||||
dst += new_len;
|
||||
src += old_len;
|
||||
} else {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
}
|
||||
*dst = 0;
|
||||
|
||||
int total_len = dst - result;
|
||||
str_pool_idx += total_len + 1;
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
long native_startswith(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return 0;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
char *prefix = (char*)args[1];
|
||||
if (!str || !prefix) {
|
||||
return 0;
|
||||
}
|
||||
int prefix_len = strlen(prefix);
|
||||
return strncmp(str, prefix, prefix_len) == 0;
|
||||
}
|
||||
|
||||
long native_endswith(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return 0;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
char *suffix = (char*)args[1];
|
||||
if (!str || !suffix) {
|
||||
return 0;
|
||||
}
|
||||
int str_len = strlen(str);
|
||||
int suffix_len = strlen(suffix);
|
||||
|
||||
if (suffix_len > str_len) return 0;
|
||||
return strcmp(str + str_len - suffix_len, suffix) == 0;
|
||||
}
|
||||
|
||||
void register_native_functions() {
|
||||
register_native_func("socket", native_socket);
|
||||
register_native_func("bind", native_bind);
|
||||
register_native_func("listen", native_listen);
|
||||
register_native_func("accept", native_accept);
|
||||
register_native_func("recv", native_recv);
|
||||
register_native_func("send", native_send);
|
||||
register_native_func("close", native_close);
|
||||
register_native_func("strlen", native_strlen);
|
||||
register_native_func("AF_INET", native_AF_INET);
|
||||
register_native_func("SOCK_STREAM", native_SOCK_STREAM);
|
||||
register_native_func("sqrt", native_sqrt);
|
||||
register_native_func("pow", native_pow);
|
||||
register_native_func("sin", native_sin);
|
||||
register_native_func("cos", native_cos);
|
||||
register_native_func("tan", native_tan);
|
||||
register_native_func("abs", native_abs);
|
||||
register_native_func("floor", native_floor);
|
||||
register_native_func("ceil", native_ceil);
|
||||
register_native_func("strpos", native_strpos);
|
||||
register_native_func("substr", native_substr);
|
||||
register_native_func("upper", native_upper);
|
||||
register_native_func("lower", native_lower);
|
||||
register_native_func("strip", native_strip);
|
||||
register_native_func("replace", native_replace);
|
||||
register_native_func("startswith", native_startswith);
|
||||
register_native_func("endswith", native_endswith);
|
||||
register_stdlib();
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
|
||||
void register_native_func(char *name, NativeFunc func);
|
||||
void register_native_functions();
|
||||
int create_coroutine(int func_idx, long *args, int argc);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,394 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#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;
|
||||
|
||||
for (int i = 0; i < class_count && i < MAX_CLASSES; i++) {
|
||||
if (strlen(classes[i].name) == (size_t)len &&
|
||||
strncmp(classes[i].name, name, len) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int find_class_field(int class_idx, char *name, int len) {
|
||||
if (class_idx < 0 || class_idx >= class_count || class_idx >= MAX_CLASSES) return -1;
|
||||
if (!name || len <= 0 || len > 31) return -1;
|
||||
|
||||
ClassDef *cls = &classes[class_idx];
|
||||
|
||||
for (int i = 0; i < cls->field_count && i < MAX_CLASS_FIELDS; i++) {
|
||||
if (strlen(cls->fields[i].name) == (size_t)len &&
|
||||
strncmp(cls->fields[i].name, name, len) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (cls->parent_class_idx >= 0 && cls->parent_class_idx < class_count) {
|
||||
return find_class_field(cls->parent_class_idx, name, len);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int find_class_method(int class_idx, char *name, int len) {
|
||||
if (class_idx < 0 || class_idx >= class_count || class_idx >= MAX_CLASSES) return -1;
|
||||
if (!name || len <= 0 || len > 31) return -1;
|
||||
|
||||
ClassDef *cls = &classes[class_idx];
|
||||
|
||||
for (int i = 0; i < cls->method_count && i < MAX_CLASS_METHODS; i++) {
|
||||
if (strlen(cls->methods[i].name) == (size_t)len &&
|
||||
strncmp(cls->methods[i].name, name, len) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (cls->parent_class_idx >= 0 && cls->parent_class_idx < class_count) {
|
||||
return find_class_method(cls->parent_class_idx, name, len);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long allocate_object(int class_idx) {
|
||||
if (class_idx < 0 || class_idx >= class_count || class_idx >= MAX_CLASSES) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (object_count >= MAX_OBJECTS) {
|
||||
error("Maximum number of objects reached");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ClassDef *cls = &classes[class_idx];
|
||||
|
||||
Object *obj = &objects[object_count];
|
||||
obj->class_idx = class_idx;
|
||||
obj->ref_count = 1;
|
||||
obj->marked_for_deletion = 0;
|
||||
|
||||
obj->field_data = (long*)malloc(cls->size * sizeof(long));
|
||||
if (!obj->field_data) {
|
||||
error("Failed to allocate object memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cls->field_count && i < MAX_CLASS_FIELDS; i++) {
|
||||
obj->field_data[cls->fields[i].offset] = cls->fields[i].default_value;
|
||||
}
|
||||
|
||||
if (cls->parent_class_idx >= 0 && cls->parent_class_idx < class_count) {
|
||||
ClassDef *parent = &classes[cls->parent_class_idx];
|
||||
for (int i = 0; i < parent->field_count && i < MAX_CLASS_FIELDS; i++) {
|
||||
obj->field_data[parent->fields[i].offset] = parent->fields[i].default_value;
|
||||
}
|
||||
}
|
||||
|
||||
object_count++;
|
||||
return (long)obj;
|
||||
}
|
||||
|
||||
void call_destructor(long obj_ptr) {
|
||||
if (obj_ptr == 0) return;
|
||||
|
||||
Object *obj = (Object*)obj_ptr;
|
||||
if (obj < objects || obj >= objects + MAX_OBJECTS) return;
|
||||
if (obj->marked_for_deletion) return;
|
||||
|
||||
obj->marked_for_deletion = 1;
|
||||
|
||||
int class_idx = obj->class_idx;
|
||||
if (class_idx < 0 || class_idx >= class_count || class_idx >= MAX_CLASSES) return;
|
||||
|
||||
ClassDef *cls = &classes[class_idx];
|
||||
|
||||
for (int i = 0; i < cls->method_count && i < MAX_CLASS_METHODS; i++) {
|
||||
if (cls->methods[i].is_destructor) {
|
||||
int func_idx = cls->methods[i].func_idx;
|
||||
if (func_idx >= 0 && func_idx < func_cnt) {
|
||||
int saved_pc = pc;
|
||||
long saved_ax = ax;
|
||||
int saved_return_flag = return_flag;
|
||||
|
||||
scope_push();
|
||||
|
||||
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 != ')') {
|
||||
if (tokens[scan_pc].type == Id) {
|
||||
Token *param_name = &tokens[scan_pc];
|
||||
if (loc_cnt < VAR_MAX) {
|
||||
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;
|
||||
sym->is_array = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pc = funcs[func_idx].entry_point;
|
||||
return_flag = 0;
|
||||
ax = 0;
|
||||
|
||||
statement();
|
||||
|
||||
scope_pop();
|
||||
|
||||
pc = saved_pc;
|
||||
ax = saved_ax;
|
||||
return_flag = saved_return_flag;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void free_object(long obj_ptr) {
|
||||
if (obj_ptr == 0) return;
|
||||
|
||||
Object *obj = (Object*)obj_ptr;
|
||||
if (obj < objects || obj >= objects + MAX_OBJECTS) return;
|
||||
|
||||
call_destructor(obj_ptr);
|
||||
|
||||
if (obj->field_data) {
|
||||
free(obj->field_data);
|
||||
obj->field_data = NULL;
|
||||
}
|
||||
|
||||
obj->class_idx = -1;
|
||||
obj->ref_count = 0;
|
||||
obj->marked_for_deletion = 0;
|
||||
}
|
||||
|
||||
void link_class_methods() {
|
||||
for (int c = 0; c < class_count && c < MAX_CLASSES; c++) {
|
||||
ClassDef *cls = &classes[c];
|
||||
for (int m = 0; m < cls->method_count && m < MAX_CLASS_METHODS; m++) {
|
||||
ClassMethod *method = &cls->methods[m];
|
||||
|
||||
char full_name[64];
|
||||
snprintf(full_name, 63, "%s_%s", cls->name, method->name);
|
||||
|
||||
extern int find_func(char *name, int len);
|
||||
int func_idx = find_func(full_name, strlen(full_name));
|
||||
if (func_idx >= 0) {
|
||||
method->func_idx = func_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scan_classes() {
|
||||
int i = 0;
|
||||
while (i < MAX_TOK && i < tk_idx && tokens[i].type != 0) {
|
||||
if (tokens[i].type == Class) {
|
||||
i++;
|
||||
if (i >= MAX_TOK || i >= tk_idx || tokens[i].type != Id) {
|
||||
error("Expected class name");
|
||||
return;
|
||||
}
|
||||
|
||||
if (class_count >= MAX_CLASSES) {
|
||||
error("Too many classes defined");
|
||||
return;
|
||||
}
|
||||
|
||||
ClassDef *cls = &classes[class_count];
|
||||
Token *name = &tokens[i];
|
||||
int name_len = name->val;
|
||||
if (name_len > 31) name_len = 31;
|
||||
strncpy(cls->name, name->text, name_len);
|
||||
cls->name[name_len] = 0;
|
||||
cls->parent_class_idx = -1;
|
||||
cls->field_count = 0;
|
||||
cls->method_count = 0;
|
||||
cls->size = 0;
|
||||
cls->token_start = i + 1;
|
||||
|
||||
i++;
|
||||
|
||||
if (i < MAX_TOK && i < tk_idx && tokens[i].type == ':') {
|
||||
i++;
|
||||
if (i >= MAX_TOK || i >= tk_idx || tokens[i].type != Id) {
|
||||
error("Expected parent class name");
|
||||
return;
|
||||
}
|
||||
|
||||
Token *parent_name = &tokens[i];
|
||||
int parent_idx = find_class(parent_name->text, parent_name->val);
|
||||
if (parent_idx < 0) {
|
||||
error("Parent class not found");
|
||||
return;
|
||||
}
|
||||
cls->parent_class_idx = parent_idx;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i >= MAX_TOK || i >= tk_idx || tokens[i].type != '{') {
|
||||
error("Expected '{' after class name");
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
|
||||
int field_offset = 0;
|
||||
if (cls->parent_class_idx >= 0) {
|
||||
field_offset = classes[cls->parent_class_idx].size;
|
||||
}
|
||||
|
||||
while (i < MAX_TOK && i < tk_idx && tokens[i].type != '}') {
|
||||
int is_static = 0;
|
||||
if (tokens[i].type == Static) {
|
||||
is_static = 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (tokens[i].type == Constructor || tokens[i].type == Destructor) {
|
||||
int is_constructor = (tokens[i].type == Constructor);
|
||||
int is_destructor = (tokens[i].type == Destructor);
|
||||
i++;
|
||||
|
||||
if (i >= MAX_TOK || i >= tk_idx || tokens[i].type != '(') {
|
||||
error("Expected '(' after constructor/destructor");
|
||||
return;
|
||||
}
|
||||
|
||||
int func_start = i - 1;
|
||||
|
||||
int brace_count = 0;
|
||||
while (i < MAX_TOK && i < tk_idx) {
|
||||
if (tokens[i].type == '{') brace_count++;
|
||||
if (tokens[i].type == '}') {
|
||||
brace_count--;
|
||||
if (brace_count == 0) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (cls->method_count < MAX_CLASS_METHODS) {
|
||||
ClassMethod *method = &cls->methods[cls->method_count];
|
||||
if (is_constructor) {
|
||||
strncpy(method->name, "constructor", 31);
|
||||
} else {
|
||||
strncpy(method->name, "destructor", 31);
|
||||
}
|
||||
method->func_idx = -1;
|
||||
method->is_static = 0;
|
||||
method->is_constructor = is_constructor;
|
||||
method->is_destructor = is_destructor;
|
||||
cls->method_count++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int type = tokens[i].type;
|
||||
if (type != Int && type != Char && type != Double && type != Void) {
|
||||
int class_type_idx = find_class(tokens[i].text, tokens[i].val);
|
||||
if (class_type_idx >= 0) {
|
||||
type = Class;
|
||||
} else {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
while (i < MAX_TOK && i < tk_idx && tokens[i].type == '*') i++;
|
||||
|
||||
if (i >= MAX_TOK || i >= tk_idx || tokens[i].type != Id) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Token *member_name = &tokens[i];
|
||||
i++;
|
||||
|
||||
if (i < MAX_TOK && i < tk_idx && tokens[i].type == '(') {
|
||||
if (cls->method_count < MAX_CLASS_METHODS) {
|
||||
ClassMethod *method = &cls->methods[cls->method_count];
|
||||
int name_len = member_name->val;
|
||||
if (name_len > 31) name_len = 31;
|
||||
strncpy(method->name, member_name->text, name_len);
|
||||
method->name[name_len] = 0;
|
||||
method->func_idx = -1;
|
||||
method->is_static = is_static;
|
||||
method->is_constructor = 0;
|
||||
method->is_destructor = 0;
|
||||
cls->method_count++;
|
||||
}
|
||||
|
||||
int brace_count = 0;
|
||||
while (i < MAX_TOK && i < tk_idx) {
|
||||
if (tokens[i].type == '{') brace_count++;
|
||||
if (tokens[i].type == '}') {
|
||||
brace_count--;
|
||||
if (brace_count == 0) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
if (cls->field_count < MAX_CLASS_FIELDS) {
|
||||
ClassField *field = &cls->fields[cls->field_count];
|
||||
int name_len = member_name->val;
|
||||
if (name_len > 31) name_len = 31;
|
||||
strncpy(field->name, member_name->text, name_len);
|
||||
field->name[name_len] = 0;
|
||||
field->type = type;
|
||||
field->default_value = 0;
|
||||
field->offset = field_offset++;
|
||||
|
||||
if (i < MAX_TOK && i < tk_idx && tokens[i].type == '=') {
|
||||
i++;
|
||||
if (i < MAX_TOK && i < tk_idx) {
|
||||
if (tokens[i].type == Num) {
|
||||
field->default_value = tokens[i].val;
|
||||
i++;
|
||||
} else if (tokens[i].type == Null) {
|
||||
field->default_value = 0;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cls->field_count++;
|
||||
}
|
||||
|
||||
if (i < MAX_TOK && i < tk_idx && tokens[i].type == ';') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cls->size = field_offset;
|
||||
|
||||
if (i < MAX_TOK && i < tk_idx && tokens[i].type == '}') {
|
||||
i++;
|
||||
}
|
||||
|
||||
class_count++;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef OOP_H
|
||||
#define OOP_H
|
||||
|
||||
int find_class(char *name, int len);
|
||||
int find_class_field(int class_idx, char *name, int len);
|
||||
int find_class_method(int class_idx, char *name, int len);
|
||||
long allocate_object(int class_idx);
|
||||
void free_object(long obj_ptr);
|
||||
void call_destructor(long obj_ptr);
|
||||
void scan_classes();
|
||||
void link_class_methods();
|
||||
|
||||
#endif
|
||||
+385
-23
@@ -5,6 +5,10 @@
|
||||
#include "parser.h"
|
||||
#include "interpreter.h"
|
||||
#include "string_utils.h"
|
||||
#include "error.h"
|
||||
#include "debug.h"
|
||||
#include "memory.h"
|
||||
#include "scope.h"
|
||||
|
||||
extern Token tokens[];
|
||||
extern int pc;
|
||||
@@ -18,6 +22,7 @@ extern int func_cnt;
|
||||
extern NativeFuncDef native_funcs[];
|
||||
extern int native_func_cnt;
|
||||
extern long ax;
|
||||
extern CallStack call_stack;
|
||||
|
||||
extern void error(char *msg);
|
||||
extern void match(int type);
|
||||
@@ -31,10 +36,79 @@ long factor() {
|
||||
Token *t = &tokens[pc];
|
||||
long val = 0;
|
||||
|
||||
if (t->type == Num) {
|
||||
if (t->type == Null) {
|
||||
pc++;
|
||||
return 0;
|
||||
}
|
||||
else if (t->type == This) {
|
||||
pc++;
|
||||
Symbol *sym = find_local("this", 4) >= 0 ? &locals[find_local("this", 4)] : NULL;
|
||||
if (sym) {
|
||||
return memory[sym->addr];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (t->type == New) {
|
||||
pc++;
|
||||
if (pc >= MAX_TOK || tokens[pc].type != Id) {
|
||||
error("Expected class name after 'new'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Token *class_name = &tokens[pc];
|
||||
pc++;
|
||||
|
||||
extern int find_class(char *name, int len);
|
||||
int class_idx = find_class(class_name->text, class_name->val);
|
||||
if (class_idx < 0) {
|
||||
error("Unknown class");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pc >= MAX_TOK || tokens[pc].type != '(') {
|
||||
error("Expected '(' after class name");
|
||||
return 0;
|
||||
}
|
||||
pc++;
|
||||
|
||||
if (pc < MAX_TOK && tokens[pc].type != ')') {
|
||||
do {
|
||||
expression();
|
||||
if (pc >= MAX_TOK) return 0;
|
||||
} while (tokens[pc].type == ',' && pc++);
|
||||
}
|
||||
match(')');
|
||||
|
||||
extern long allocate_object(int class_idx);
|
||||
long obj_ptr = allocate_object(class_idx);
|
||||
if (obj_ptr == 0) {
|
||||
error("Failed to allocate object");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return obj_ptr;
|
||||
}
|
||||
else if (t->type == Await) {
|
||||
pc++;
|
||||
match('(');
|
||||
long coro_id = expression();
|
||||
match(')');
|
||||
|
||||
extern long native_await(long*, int);
|
||||
long args[1];
|
||||
args[0] = coro_id;
|
||||
return native_await(args, 1);
|
||||
}
|
||||
else if (t->type == Num) {
|
||||
pc++;
|
||||
return t->val;
|
||||
}
|
||||
else if (t->type == Dbl) {
|
||||
pc++;
|
||||
union { double d; long l; } u;
|
||||
u.d = t->dval;
|
||||
return u.l;
|
||||
}
|
||||
else if (t->type == Str) {
|
||||
pc++;
|
||||
return (long)t->text;
|
||||
@@ -66,15 +140,25 @@ long factor() {
|
||||
}
|
||||
match(')');
|
||||
|
||||
return native_funcs[nf_idx].func(args, argc);
|
||||
const char *native_filename = NULL;
|
||||
int native_line = 0;
|
||||
if (pc < tk_idx && pc >= 0) {
|
||||
native_filename = tokens[pc].filename;
|
||||
native_line = tokens[pc].line;
|
||||
}
|
||||
push_call_frame(native_funcs[nf_idx].name, native_filename, native_line, 1);
|
||||
long result = native_funcs[nf_idx].func(args, argc);
|
||||
pop_call_frame();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int f_idx = find_func(t->text, t->val);
|
||||
if (f_idx == -1) error("Unknown function");
|
||||
if (f_idx < 0 || f_idx >= func_cnt) return 0;
|
||||
int call_pc = pc;
|
||||
pc += 2;
|
||||
|
||||
int old_bp = bp;
|
||||
long args[10];
|
||||
int argc = 0;
|
||||
|
||||
@@ -88,27 +172,75 @@ long factor() {
|
||||
}
|
||||
match(')');
|
||||
|
||||
int ret_pc = pc;
|
||||
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;
|
||||
for(int i=0; i<argc; i++) {
|
||||
if (sp >= MEM_SIZE) break;
|
||||
memory[sp++] = args[i];
|
||||
if (funcs[f_idx].is_async) {
|
||||
extern int create_coroutine(int func_idx, long *args, int argc);
|
||||
return create_coroutine(f_idx, args, argc);
|
||||
}
|
||||
|
||||
int ret_pc = pc;
|
||||
int saved_return_flag = return_flag;
|
||||
|
||||
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) {
|
||||
memory[sp++] = args[i];
|
||||
}
|
||||
}
|
||||
|
||||
int scan_pc = funcs[f_idx].params_start;
|
||||
int param_idx = 0;
|
||||
while (scan_pc < MAX_TOK && scan_pc < tk_idx && tokens[scan_pc].type != ')') {
|
||||
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) {
|
||||
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++;
|
||||
}
|
||||
|
||||
const char *func_filename = NULL;
|
||||
int func_line = 0;
|
||||
if (funcs[f_idx].entry_point < tk_idx) {
|
||||
func_filename = tokens[funcs[f_idx].entry_point].filename;
|
||||
func_line = tokens[funcs[f_idx].entry_point].line;
|
||||
}
|
||||
push_call_frame(funcs[f_idx].name, func_filename, func_line, 0);
|
||||
|
||||
pc = funcs[f_idx].entry_point;
|
||||
return_flag = 0;
|
||||
statement();
|
||||
|
||||
val = ax;
|
||||
ax = 0;
|
||||
return_flag = 0;
|
||||
|
||||
pop_call_frame();
|
||||
mem_exit_call(funcs[f_idx].name);
|
||||
|
||||
scope_pop();
|
||||
|
||||
if (bp < 0 || bp >= MEM_SIZE) return 0;
|
||||
sp = bp;
|
||||
if (sp < 0 || sp >= MEM_SIZE) return 0;
|
||||
bp = memory[sp];
|
||||
pc = ret_pc;
|
||||
return_flag = saved_return_flag;
|
||||
return val;
|
||||
}
|
||||
else {
|
||||
@@ -119,6 +251,11 @@ long factor() {
|
||||
|
||||
Symbol *sym = &locals[idx];
|
||||
|
||||
if (sym->addr < 0 || sym->addr >= MEM_SIZE) {
|
||||
error("Invalid variable memory address");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pc < MAX_TOK && (tokens[pc].type == Inc || tokens[pc].type == Dec)) {
|
||||
long val = memory[sym->addr];
|
||||
if (tokens[pc].type == Inc) {
|
||||
@@ -171,12 +308,18 @@ long unary() {
|
||||
if (pc >= MAX_TOK) return 0;
|
||||
if (tokens[pc].type == Inc || tokens[pc].type == Dec) {
|
||||
int op = tokens[pc++].type;
|
||||
if (pc >= MAX_TOK) return 0;
|
||||
Token *t = &tokens[pc];
|
||||
if (t->type != Id) error("Expected identifier after ++/--");
|
||||
int idx = find_local(t->text, t->val);
|
||||
if (idx == -1) error("Undefined variable");
|
||||
if (idx < 0 || idx >= VAR_MAX) return 0;
|
||||
pc++;
|
||||
long addr = locals[idx].addr;
|
||||
if (addr < 0 || addr >= MEM_SIZE) {
|
||||
error("Invalid memory address in increment/decrement");
|
||||
return 0;
|
||||
}
|
||||
if (op == Inc) {
|
||||
memory[addr]++;
|
||||
} else {
|
||||
@@ -186,11 +329,30 @@ long unary() {
|
||||
}
|
||||
if (tokens[pc].type == '*') {
|
||||
pc++;
|
||||
int addr = unary();
|
||||
if (addr > MEM_SIZE * 8 || addr < 0) {
|
||||
return *(char*)addr;
|
||||
long addr = unary();
|
||||
DEBUG_LONG("dereference_addr", addr);
|
||||
if (addr < 0 || addr >= MEM_SIZE) {
|
||||
if (addr < 0) {
|
||||
DEBUG_LOG("ERROR: Negative memory address: %ld", addr);
|
||||
error("Invalid memory address (negative)");
|
||||
return 0;
|
||||
}
|
||||
if (addr > MEM_SIZE * 8) {
|
||||
char *ptr = (char*)addr;
|
||||
DEBUG_PTR("external_ptr", ptr);
|
||||
if (ptr == NULL) {
|
||||
DEBUG_LOG("ERROR: Null pointer dereference");
|
||||
error("Null pointer dereference");
|
||||
return 0;
|
||||
}
|
||||
DEBUG_LOG("Dereferencing external pointer: %p", (void*)ptr);
|
||||
return *ptr;
|
||||
}
|
||||
DEBUG_LOG("ERROR: Memory access out of bounds: %ld (max: %d)", addr, MEM_SIZE);
|
||||
error("Memory access out of bounds");
|
||||
return 0;
|
||||
}
|
||||
if (addr < 0 || addr >= MEM_SIZE) return 0;
|
||||
DEBUG_MEMORY_ACCESS(addr, "READ");
|
||||
return memory[addr];
|
||||
}
|
||||
else if (tokens[pc].type == '&') {
|
||||
@@ -208,7 +370,129 @@ long unary() {
|
||||
pc++;
|
||||
return -unary();
|
||||
}
|
||||
return factor();
|
||||
|
||||
long val = factor();
|
||||
|
||||
while (pc < MAX_TOK && tokens[pc].type == '.') {
|
||||
pc++;
|
||||
if (pc >= MAX_TOK || tokens[pc].type != Id) {
|
||||
error("Expected field or method name after '.'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Token *member = &tokens[pc];
|
||||
pc++;
|
||||
|
||||
if (val == 0) {
|
||||
error("Null pointer dereference");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Object *obj = (Object*)val;
|
||||
if (obj < objects || obj >= objects + MAX_OBJECTS) {
|
||||
error("Invalid object pointer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int class_idx = obj->class_idx;
|
||||
if (class_idx < 0 || class_idx >= class_count) {
|
||||
error("Invalid class index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pc < MAX_TOK && tokens[pc].type == '(') {
|
||||
extern int find_class_method(int class_idx, char *name, int len);
|
||||
int method_idx = find_class_method(class_idx, member->text, member->val);
|
||||
if (method_idx < 0) {
|
||||
error("Unknown method");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int func_idx = classes[class_idx].methods[method_idx].func_idx;
|
||||
if (func_idx < 0 || func_idx >= func_cnt) {
|
||||
error("Invalid method function index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pc++;
|
||||
long args[10];
|
||||
int argc = 0;
|
||||
args[argc++] = val;
|
||||
|
||||
if (pc < MAX_TOK && tokens[pc].type != ')') {
|
||||
do {
|
||||
if (argc >= 10) break;
|
||||
args[argc++] = expression();
|
||||
if (pc >= MAX_TOK) return 0;
|
||||
} while (tokens[pc].type == ',' && pc++);
|
||||
}
|
||||
match(')');
|
||||
|
||||
int saved_pc = pc;
|
||||
int saved_return_flag = return_flag;
|
||||
|
||||
scope_push();
|
||||
|
||||
int param_base = sp;
|
||||
for (int i = 0; i < argc && i < 10; i++) {
|
||||
if (sp < MEM_SIZE) {
|
||||
memory[sp++] = args[i];
|
||||
}
|
||||
}
|
||||
|
||||
int scan_pc = funcs[func_idx].params_start;
|
||||
int param_idx = 0;
|
||||
while (scan_pc < MAX_TOK && scan_pc < tk_idx && tokens[scan_pc].type != ')') {
|
||||
if (tokens[scan_pc].type == Int || tokens[scan_pc].type == Char ||
|
||||
tokens[scan_pc].type == Double || tokens[scan_pc].type == Id) {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
scan_pc++;
|
||||
}
|
||||
|
||||
pc = funcs[func_idx].entry_point;
|
||||
return_flag = 0;
|
||||
statement();
|
||||
|
||||
val = ax;
|
||||
ax = 0;
|
||||
return_flag = 0;
|
||||
|
||||
scope_pop();
|
||||
pc = saved_pc;
|
||||
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);
|
||||
if (field_idx < 0) {
|
||||
error("Unknown field");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int offset = classes[class_idx].fields[field_idx].offset;
|
||||
if (offset < 0 || offset >= classes[class_idx].size) {
|
||||
error("Invalid field offset");
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = obj->field_data[offset];
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
long term() {
|
||||
@@ -252,6 +536,28 @@ long relational() {
|
||||
if (op == Ne) val = val != val2;
|
||||
if (op == Lt) val = val < val2;
|
||||
if (op == Gt) val = val > val2;
|
||||
if (op == Le) val = val <= val2;
|
||||
if (op == Ge) val = val >= val2;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
long logical_and() {
|
||||
long val = relational();
|
||||
while (pc < MAX_TOK && tokens[pc].type == And) {
|
||||
pc++;
|
||||
long val2 = relational();
|
||||
val = val && val2;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
long logical_or() {
|
||||
long val = logical_and();
|
||||
while (pc < MAX_TOK && tokens[pc].type == Or) {
|
||||
pc++;
|
||||
long val2 = logical_and();
|
||||
val = val || val2;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
@@ -277,7 +583,63 @@ long expression() {
|
||||
if (pc >= MAX_TOK) return 0;
|
||||
if (tokens[pc].type == Id) {
|
||||
if (pc + 1 >= MAX_TOK) return 0;
|
||||
if (tokens[pc+1].type == '[') {
|
||||
if (tokens[pc+1].type == '.') {
|
||||
int save_pc = pc;
|
||||
int temp_pc = pc + 1;
|
||||
if (temp_pc < MAX_TOK && tokens[temp_pc].type == '.') {
|
||||
temp_pc++;
|
||||
if (temp_pc < MAX_TOK && tokens[temp_pc].type == Id) {
|
||||
temp_pc++;
|
||||
if (temp_pc < MAX_TOK && tokens[temp_pc].type == '=') {
|
||||
int idx = find_local(tokens[pc].text, tokens[pc].val);
|
||||
if (idx == -1) error("Unknown object variable");
|
||||
if (idx < 0 || idx >= loc_cnt) return 0;
|
||||
long obj_ptr = memory[locals[idx].addr];
|
||||
if (obj_ptr == 0) {
|
||||
error("Null pointer dereference in assignment");
|
||||
return 0;
|
||||
}
|
||||
Object *obj = (Object*)obj_ptr;
|
||||
if (obj < objects || obj >= objects + MAX_OBJECTS) {
|
||||
error("Invalid object pointer in assignment");
|
||||
return 0;
|
||||
}
|
||||
pc++;
|
||||
match('.');
|
||||
if (pc >= MAX_TOK || tokens[pc].type != Id) {
|
||||
error("Expected field name after '.'");
|
||||
return 0;
|
||||
}
|
||||
Token *field_name = &tokens[pc];
|
||||
pc++;
|
||||
match('=');
|
||||
|
||||
int class_idx = obj->class_idx;
|
||||
if (class_idx < 0 || class_idx >= class_count) {
|
||||
error("Invalid class index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int find_class_field(int class_idx, char *name, int len);
|
||||
int field_idx = find_class_field(class_idx, field_name->text, field_name->val);
|
||||
if (field_idx < 0) {
|
||||
error("Unknown field");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int offset = classes[class_idx].fields[field_idx].offset;
|
||||
if (offset < 0 || offset >= classes[class_idx].size) {
|
||||
error("Invalid field offset");
|
||||
return 0;
|
||||
}
|
||||
|
||||
long val = expression();
|
||||
obj->field_data[offset] = val;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (tokens[pc+1].type == '[') {
|
||||
int idx = find_local(tokens[pc].text, tokens[pc].val);
|
||||
if (idx == -1) error("Assign to unknown var");
|
||||
if (idx < 0 || idx >= loc_cnt) return 0;
|
||||
@@ -327,5 +689,5 @@ long expression() {
|
||||
}
|
||||
}
|
||||
|
||||
return relational();
|
||||
return logical_or();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#define PARSER_H
|
||||
|
||||
long expression();
|
||||
long logical_or();
|
||||
long logical_and();
|
||||
long relational();
|
||||
long add();
|
||||
long term();
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <libgen.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "preprocessor.h"
|
||||
|
||||
static IncludedFile included_files[MAX_INCLUDED_FILES];
|
||||
static int included_count = 0;
|
||||
static int include_depth = 0;
|
||||
|
||||
static void normalize_path(const char *path, char *normalized) {
|
||||
strncpy(normalized, path, 255);
|
||||
normalized[255] = 0;
|
||||
}
|
||||
|
||||
static int is_already_included(const char *path) {
|
||||
char normalized[256];
|
||||
normalize_path(path, normalized);
|
||||
|
||||
for (int i = 0; i < included_count; i++) {
|
||||
if (strcmp(included_files[i].path, normalized) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mark_as_included(const char *path) {
|
||||
if (included_count >= MAX_INCLUDED_FILES) {
|
||||
fprintf(stderr, "Error: Too many included files (max %d)\n", MAX_INCLUDED_FILES);
|
||||
return;
|
||||
}
|
||||
|
||||
char normalized[256];
|
||||
normalize_path(path, normalized);
|
||||
|
||||
strncpy(included_files[included_count].path, normalized, 255);
|
||||
included_files[included_count].path[255] = 0;
|
||||
included_files[included_count].included = 1;
|
||||
included_count++;
|
||||
}
|
||||
|
||||
static char* read_file(const char *filename) {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (size <= 0 || size > MAX_PREPROCESSED_SIZE) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *content = malloc(size + 1);
|
||||
if (!content) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t read_size = fread(content, 1, size, f);
|
||||
content[read_size] = 0;
|
||||
fclose(f);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
static void resolve_include_path(const char *current_dir, const char *include_file, char *resolved_path) {
|
||||
if (include_file[0] == '/') {
|
||||
strncpy(resolved_path, include_file, 511);
|
||||
resolved_path[511] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(resolved_path, 512, "%s/%s", current_dir, include_file);
|
||||
}
|
||||
|
||||
static char* process_includes(const char *filename, const char *content, const char *current_dir);
|
||||
|
||||
static char* extract_include_filename(const char *line) {
|
||||
const char *p = line;
|
||||
while (*p && isspace(*p)) p++;
|
||||
|
||||
if (*p != '#') return NULL;
|
||||
p++;
|
||||
|
||||
while (*p && isspace(*p)) p++;
|
||||
|
||||
if (strncmp(p, "include", 7) != 0) return NULL;
|
||||
p += 7;
|
||||
|
||||
while (*p && isspace(*p)) p++;
|
||||
|
||||
if (*p != '"' && *p != '<') return NULL;
|
||||
|
||||
char quote = (*p == '"') ? '"' : '>';
|
||||
p++;
|
||||
|
||||
static char filename[256];
|
||||
int i = 0;
|
||||
while (*p && *p != quote && i < 255) {
|
||||
filename[i++] = *p++;
|
||||
}
|
||||
filename[i] = 0;
|
||||
|
||||
if (*p != quote) return NULL;
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
static char* process_includes(const char *filename, const char *content, const char *current_dir) {
|
||||
if (include_depth >= MAX_INCLUDE_DEPTH) {
|
||||
fprintf(stderr, "Error: Include depth exceeded (max %d) in file: %s\n",
|
||||
MAX_INCLUDE_DEPTH, filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
include_depth++;
|
||||
|
||||
size_t result_size = MAX_PREPROCESSED_SIZE;
|
||||
char *result = malloc(result_size);
|
||||
if (!result) {
|
||||
fprintf(stderr, "Error: Memory allocation failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
result[0] = 0;
|
||||
size_t result_len = 0;
|
||||
|
||||
const char *p = content;
|
||||
char line[1024];
|
||||
|
||||
while (*p) {
|
||||
int line_len = 0;
|
||||
while (*p && *p != '\n' && line_len < 1023) {
|
||||
line[line_len++] = *p++;
|
||||
}
|
||||
line[line_len] = 0;
|
||||
if (*p == '\n') {
|
||||
p++;
|
||||
}
|
||||
|
||||
char *include_file = extract_include_filename(line);
|
||||
|
||||
if (include_file) {
|
||||
char resolved_path[512];
|
||||
resolve_include_path(current_dir, include_file, resolved_path);
|
||||
|
||||
if (is_already_included(resolved_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mark_as_included(resolved_path);
|
||||
|
||||
char *included_content = read_file(resolved_path);
|
||||
if (!included_content) {
|
||||
fprintf(stderr, "Error: Cannot open included file: %s\n", resolved_path);
|
||||
fprintf(stderr, " Included from: %s\n", filename);
|
||||
free(result);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char include_dir[512];
|
||||
char temp_path[512];
|
||||
strncpy(temp_path, resolved_path, 511);
|
||||
temp_path[511] = 0;
|
||||
char *dir = dirname(temp_path);
|
||||
strncpy(include_dir, dir, 511);
|
||||
include_dir[511] = 0;
|
||||
|
||||
char *processed = process_includes(resolved_path, included_content, include_dir);
|
||||
free(included_content);
|
||||
|
||||
size_t processed_len = strlen(processed);
|
||||
if (result_len + processed_len + 2 >= result_size) {
|
||||
fprintf(stderr, "Error: Preprocessed output too large (max %d bytes)\n",
|
||||
MAX_PREPROCESSED_SIZE);
|
||||
free(processed);
|
||||
free(result);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memcpy(result + result_len, processed, processed_len);
|
||||
result_len += processed_len;
|
||||
result[result_len++] = '\n';
|
||||
result[result_len] = 0;
|
||||
|
||||
free(processed);
|
||||
} else {
|
||||
size_t line_total = line_len + 1;
|
||||
if (result_len + line_total >= result_size) {
|
||||
fprintf(stderr, "Error: Preprocessed output too large (max %d bytes)\n",
|
||||
MAX_PREPROCESSED_SIZE);
|
||||
free(result);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (line_len > 0) {
|
||||
memcpy(result + result_len, line, line_len);
|
||||
result_len += line_len;
|
||||
}
|
||||
result[result_len++] = '\n';
|
||||
result[result_len] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
include_depth--;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* preprocess(const char *filename, const char *source_dir) {
|
||||
included_count = 0;
|
||||
include_depth = 0;
|
||||
memset(included_files, 0, sizeof(included_files));
|
||||
|
||||
mark_as_included(filename);
|
||||
|
||||
char *content = read_file(filename);
|
||||
if (!content) {
|
||||
fprintf(stderr, "Error: Cannot read file: %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char dir_path[512];
|
||||
if (source_dir) {
|
||||
strncpy(dir_path, source_dir, 511);
|
||||
dir_path[511] = 0;
|
||||
} else {
|
||||
char temp_path[512];
|
||||
strncpy(temp_path, filename, 511);
|
||||
temp_path[511] = 0;
|
||||
char *dir = dirname(temp_path);
|
||||
strncpy(dir_path, dir, 511);
|
||||
dir_path[511] = 0;
|
||||
}
|
||||
|
||||
char *result = process_includes(filename, content, dir_path);
|
||||
free(content);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef PREPROCESSOR_H
|
||||
#define PREPROCESSOR_H
|
||||
|
||||
#define MAX_INCLUDE_DEPTH 32
|
||||
#define MAX_INCLUDED_FILES 256
|
||||
#define MAX_PREPROCESSED_SIZE 500000
|
||||
|
||||
typedef struct {
|
||||
char path[256];
|
||||
int included;
|
||||
} IncludedFile;
|
||||
|
||||
char* preprocess(const char *filename, const char *source_dir);
|
||||
|
||||
#endif
|
||||
+596
@@ -0,0 +1,596 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "runtime.h"
|
||||
|
||||
Runtime *global_runtime = NULL;
|
||||
|
||||
#define INITIAL_TOKEN_CAPACITY 1024
|
||||
#define INITIAL_MEMORY_CAPACITY 10000
|
||||
#define INITIAL_SYMBOL_CAPACITY 128
|
||||
#define INITIAL_FUNC_CAPACITY 32
|
||||
#define INITIAL_CLASS_CAPACITY 16
|
||||
#define INITIAL_OBJECT_CAPACITY 256
|
||||
#define INITIAL_COROUTINE_CAPACITY 16
|
||||
#define INITIAL_STRING_POOL_CAPACITY 65536
|
||||
#define INITIAL_CALL_STACK_CAPACITY 128
|
||||
|
||||
Runtime* runtime_create() {
|
||||
Runtime *rt = (Runtime*)calloc(1, sizeof(Runtime));
|
||||
if (!rt) return NULL;
|
||||
|
||||
if (!token_array_init(&rt->tokens)) goto fail;
|
||||
if (!memory_init(&rt->memory)) goto fail;
|
||||
if (!symbol_table_init(&rt->symbols)) goto fail;
|
||||
if (!func_table_init(&rt->funcs)) goto fail;
|
||||
if (!native_func_table_init(&rt->native_funcs)) goto fail;
|
||||
if (!class_table_init(&rt->classes)) goto fail;
|
||||
if (!object_table_init(&rt->objects)) goto fail;
|
||||
if (!coroutine_table_init(&rt->coroutines)) goto fail;
|
||||
if (!string_pool_init(&rt->str_pool)) goto fail;
|
||||
if (!call_stack_init(&rt->call_stack)) goto fail;
|
||||
|
||||
pthread_mutex_init(&rt->state_lock, NULL);
|
||||
|
||||
rt->pc = 0;
|
||||
rt->sp = 0;
|
||||
rt->bp = 0;
|
||||
rt->ax = 0;
|
||||
rt->return_flag = 0;
|
||||
|
||||
return rt;
|
||||
|
||||
fail:
|
||||
runtime_destroy(rt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void runtime_destroy(Runtime *rt) {
|
||||
if (!rt) return;
|
||||
|
||||
token_array_destroy(&rt->tokens);
|
||||
memory_destroy(&rt->memory);
|
||||
symbol_table_destroy(&rt->symbols);
|
||||
func_table_destroy(&rt->funcs);
|
||||
native_func_table_destroy(&rt->native_funcs);
|
||||
class_table_destroy(&rt->classes);
|
||||
object_table_destroy(&rt->objects);
|
||||
coroutine_table_destroy(&rt->coroutines);
|
||||
string_pool_destroy(&rt->str_pool);
|
||||
call_stack_destroy(&rt->call_stack);
|
||||
|
||||
pthread_mutex_destroy(&rt->state_lock);
|
||||
|
||||
free(rt);
|
||||
}
|
||||
|
||||
int token_array_init(TokenArray *arr) {
|
||||
arr->tokens = (Token*)calloc(INITIAL_TOKEN_CAPACITY, sizeof(Token));
|
||||
if (!arr->tokens) return 0;
|
||||
arr->count = 0;
|
||||
arr->capacity = INITIAL_TOKEN_CAPACITY;
|
||||
pthread_rwlock_init(&arr->lock, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void token_array_destroy(TokenArray *arr) {
|
||||
if (arr->tokens) {
|
||||
free(arr->tokens);
|
||||
arr->tokens = NULL;
|
||||
}
|
||||
pthread_rwlock_destroy(&arr->lock);
|
||||
}
|
||||
|
||||
int token_array_ensure_capacity(TokenArray *arr, int min_capacity) {
|
||||
if (arr->capacity >= min_capacity) return 1;
|
||||
|
||||
int new_capacity = arr->capacity * 2;
|
||||
if (new_capacity < min_capacity) new_capacity = min_capacity;
|
||||
|
||||
Token *new_tokens = (Token*)realloc(arr->tokens, new_capacity * sizeof(Token));
|
||||
if (!new_tokens) return 0;
|
||||
|
||||
memset(new_tokens + arr->capacity, 0, (new_capacity - arr->capacity) * sizeof(Token));
|
||||
arr->tokens = new_tokens;
|
||||
arr->capacity = new_capacity;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int token_array_add(TokenArray *arr, Token token) {
|
||||
pthread_rwlock_wrlock(&arr->lock);
|
||||
|
||||
if (!token_array_ensure_capacity(arr, arr->count + 1)) {
|
||||
pthread_rwlock_unlock(&arr->lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
arr->tokens[arr->count] = token;
|
||||
int index = arr->count;
|
||||
arr->count++;
|
||||
|
||||
pthread_rwlock_unlock(&arr->lock);
|
||||
return index;
|
||||
}
|
||||
|
||||
int memory_init(Memory *mem) {
|
||||
mem->data = (long*)calloc(INITIAL_MEMORY_CAPACITY, sizeof(long));
|
||||
if (!mem->data) return 0;
|
||||
mem->size = INITIAL_MEMORY_CAPACITY;
|
||||
mem->capacity = INITIAL_MEMORY_CAPACITY;
|
||||
pthread_mutex_init(&mem->lock, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void memory_destroy(Memory *mem) {
|
||||
if (mem->data) {
|
||||
free(mem->data);
|
||||
mem->data = NULL;
|
||||
}
|
||||
pthread_mutex_destroy(&mem->lock);
|
||||
}
|
||||
|
||||
int memory_ensure_capacity(Memory *mem, int min_size) {
|
||||
if (mem->capacity >= min_size) {
|
||||
if (mem->size < min_size) mem->size = min_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int new_capacity = mem->capacity * 2;
|
||||
if (new_capacity < min_size) new_capacity = min_size;
|
||||
|
||||
long *new_data = (long*)realloc(mem->data, new_capacity * sizeof(long));
|
||||
if (!new_data) return 0;
|
||||
|
||||
memset(new_data + mem->capacity, 0, (new_capacity - mem->capacity) * sizeof(long));
|
||||
mem->data = new_data;
|
||||
mem->capacity = new_capacity;
|
||||
mem->size = min_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
long memory_get(Memory *mem, int addr) {
|
||||
if (addr < 0 || addr >= mem->size) return 0;
|
||||
return mem->data[addr];
|
||||
}
|
||||
|
||||
void memory_set(Memory *mem, int addr, long value) {
|
||||
pthread_mutex_lock(&mem->lock);
|
||||
if (memory_ensure_capacity(mem, addr + 1)) {
|
||||
mem->data[addr] = value;
|
||||
}
|
||||
pthread_mutex_unlock(&mem->lock);
|
||||
}
|
||||
|
||||
int symbol_table_init(SymbolTable *tbl) {
|
||||
tbl->data = (Symbol*)calloc(INITIAL_SYMBOL_CAPACITY, sizeof(Symbol));
|
||||
if (!tbl->data) return 0;
|
||||
tbl->count = 0;
|
||||
tbl->capacity = INITIAL_SYMBOL_CAPACITY;
|
||||
pthread_mutex_init(&tbl->lock, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void symbol_table_destroy(SymbolTable *tbl) {
|
||||
if (tbl->data) {
|
||||
for (int i = 0; i < tbl->count; i++) {
|
||||
if (tbl->data[i].name) free(tbl->data[i].name);
|
||||
}
|
||||
free(tbl->data);
|
||||
tbl->data = NULL;
|
||||
}
|
||||
pthread_mutex_destroy(&tbl->lock);
|
||||
}
|
||||
|
||||
int symbol_table_ensure_capacity(SymbolTable *tbl, int min_capacity) {
|
||||
if (tbl->capacity >= min_capacity) return 1;
|
||||
|
||||
int new_capacity = tbl->capacity * 2;
|
||||
if (new_capacity < min_capacity) new_capacity = min_capacity;
|
||||
|
||||
Symbol *new_data = (Symbol*)realloc(tbl->data, new_capacity * sizeof(Symbol));
|
||||
if (!new_data) return 0;
|
||||
|
||||
memset(new_data + tbl->capacity, 0, (new_capacity - tbl->capacity) * sizeof(Symbol));
|
||||
tbl->data = new_data;
|
||||
tbl->capacity = new_capacity;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int symbol_table_add(SymbolTable *tbl, Symbol symbol) {
|
||||
pthread_mutex_lock(&tbl->lock);
|
||||
|
||||
if (!symbol_table_ensure_capacity(tbl, tbl->count + 1)) {
|
||||
pthread_mutex_unlock(&tbl->lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tbl->data[tbl->count] = symbol;
|
||||
int index = tbl->count;
|
||||
tbl->count++;
|
||||
|
||||
pthread_mutex_unlock(&tbl->lock);
|
||||
return index;
|
||||
}
|
||||
|
||||
int func_table_init(FuncTable *tbl) {
|
||||
tbl->data = (Func*)calloc(INITIAL_FUNC_CAPACITY, sizeof(Func));
|
||||
if (!tbl->data) return 0;
|
||||
tbl->count = 0;
|
||||
tbl->capacity = INITIAL_FUNC_CAPACITY;
|
||||
pthread_rwlock_init(&tbl->lock, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void func_table_destroy(FuncTable *tbl) {
|
||||
if (tbl->data) {
|
||||
for (int i = 0; i < tbl->count; i++) {
|
||||
if (tbl->data[i].name) free(tbl->data[i].name);
|
||||
}
|
||||
free(tbl->data);
|
||||
tbl->data = NULL;
|
||||
}
|
||||
pthread_rwlock_destroy(&tbl->lock);
|
||||
}
|
||||
|
||||
int func_table_ensure_capacity(FuncTable *tbl, int min_capacity) {
|
||||
if (tbl->capacity >= min_capacity) return 1;
|
||||
|
||||
int new_capacity = tbl->capacity * 2;
|
||||
if (new_capacity < min_capacity) new_capacity = min_capacity;
|
||||
|
||||
Func *new_data = (Func*)realloc(tbl->data, new_capacity * sizeof(Func));
|
||||
if (!new_data) return 0;
|
||||
|
||||
memset(new_data + tbl->capacity, 0, (new_capacity - tbl->capacity) * sizeof(Func));
|
||||
tbl->data = new_data;
|
||||
tbl->capacity = new_capacity;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int func_table_add(FuncTable *tbl, Func func) {
|
||||
pthread_rwlock_wrlock(&tbl->lock);
|
||||
|
||||
if (!func_table_ensure_capacity(tbl, tbl->count + 1)) {
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tbl->data[tbl->count] = func;
|
||||
int index = tbl->count;
|
||||
tbl->count++;
|
||||
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return index;
|
||||
}
|
||||
|
||||
int func_table_find(FuncTable *tbl, const char *name, int len) {
|
||||
pthread_rwlock_rdlock(&tbl->lock);
|
||||
|
||||
for (int i = 0; i < tbl->count; i++) {
|
||||
if (tbl->data[i].name && !strncmp(tbl->data[i].name, name, len) &&
|
||||
tbl->data[i].name[len] == 0) {
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int native_func_table_init(NativeFuncTable *tbl) {
|
||||
tbl->data = (NativeFuncDef*)calloc(INITIAL_FUNC_CAPACITY, sizeof(NativeFuncDef));
|
||||
if (!tbl->data) return 0;
|
||||
tbl->count = 0;
|
||||
tbl->capacity = INITIAL_FUNC_CAPACITY;
|
||||
pthread_rwlock_init(&tbl->lock, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void native_func_table_destroy(NativeFuncTable *tbl) {
|
||||
if (tbl->data) {
|
||||
for (int i = 0; i < tbl->count; i++) {
|
||||
if (tbl->data[i].name) free(tbl->data[i].name);
|
||||
}
|
||||
free(tbl->data);
|
||||
tbl->data = NULL;
|
||||
}
|
||||
pthread_rwlock_destroy(&tbl->lock);
|
||||
}
|
||||
|
||||
int native_func_table_add(NativeFuncTable *tbl, const char *name, NativeFunc func) {
|
||||
pthread_rwlock_wrlock(&tbl->lock);
|
||||
|
||||
if (tbl->count >= tbl->capacity) {
|
||||
int new_capacity = tbl->capacity * 2;
|
||||
NativeFuncDef *new_data = (NativeFuncDef*)realloc(tbl->data,
|
||||
new_capacity * sizeof(NativeFuncDef));
|
||||
if (!new_data) {
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return -1;
|
||||
}
|
||||
memset(new_data + tbl->capacity, 0, (new_capacity - tbl->capacity) * sizeof(NativeFuncDef));
|
||||
tbl->data = new_data;
|
||||
tbl->capacity = new_capacity;
|
||||
}
|
||||
|
||||
tbl->data[tbl->count].name = strdup(name);
|
||||
tbl->data[tbl->count].func = func;
|
||||
int index = tbl->count;
|
||||
tbl->count++;
|
||||
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return index;
|
||||
}
|
||||
|
||||
int native_func_table_find(NativeFuncTable *tbl, const char *name, int len) {
|
||||
pthread_rwlock_rdlock(&tbl->lock);
|
||||
|
||||
for (int i = 0; i < tbl->count; i++) {
|
||||
if (tbl->data[i].name && !strncmp(tbl->data[i].name, name, len) &&
|
||||
tbl->data[i].name[len] == 0) {
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int class_table_init(ClassTable *tbl) {
|
||||
tbl->data = (ClassDef*)calloc(INITIAL_CLASS_CAPACITY, sizeof(ClassDef));
|
||||
if (!tbl->data) return 0;
|
||||
tbl->count = 0;
|
||||
tbl->capacity = INITIAL_CLASS_CAPACITY;
|
||||
pthread_rwlock_init(&tbl->lock, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void class_table_destroy(ClassTable *tbl) {
|
||||
if (tbl->data) {
|
||||
for (int i = 0; i < tbl->count; i++) {
|
||||
if (tbl->data[i].name) free(tbl->data[i].name);
|
||||
if (tbl->data[i].fields) {
|
||||
for (int j = 0; j < tbl->data[i].field_count; j++) {
|
||||
if (tbl->data[i].fields[j].name) free(tbl->data[i].fields[j].name);
|
||||
}
|
||||
free(tbl->data[i].fields);
|
||||
}
|
||||
if (tbl->data[i].methods) {
|
||||
for (int j = 0; j < tbl->data[i].method_count; j++) {
|
||||
if (tbl->data[i].methods[j].name) free(tbl->data[i].methods[j].name);
|
||||
}
|
||||
free(tbl->data[i].methods);
|
||||
}
|
||||
}
|
||||
free(tbl->data);
|
||||
tbl->data = NULL;
|
||||
}
|
||||
pthread_rwlock_destroy(&tbl->lock);
|
||||
}
|
||||
|
||||
int class_table_add(ClassTable *tbl, ClassDef class_def) {
|
||||
pthread_rwlock_wrlock(&tbl->lock);
|
||||
|
||||
if (tbl->count >= tbl->capacity) {
|
||||
int new_capacity = tbl->capacity * 2;
|
||||
ClassDef *new_data = (ClassDef*)realloc(tbl->data, new_capacity * sizeof(ClassDef));
|
||||
if (!new_data) {
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return -1;
|
||||
}
|
||||
memset(new_data + tbl->capacity, 0, (new_capacity - tbl->capacity) * sizeof(ClassDef));
|
||||
tbl->data = new_data;
|
||||
tbl->capacity = new_capacity;
|
||||
}
|
||||
|
||||
tbl->data[tbl->count] = class_def;
|
||||
int index = tbl->count;
|
||||
tbl->count++;
|
||||
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return index;
|
||||
}
|
||||
|
||||
int class_table_find(ClassTable *tbl, const char *name, int len) {
|
||||
pthread_rwlock_rdlock(&tbl->lock);
|
||||
|
||||
for (int i = 0; i < tbl->count; i++) {
|
||||
if (tbl->data[i].name && !strncmp(tbl->data[i].name, name, len) &&
|
||||
tbl->data[i].name[len] == 0) {
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_rwlock_unlock(&tbl->lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int object_table_init(ObjectTable *tbl) {
|
||||
tbl->data = (Object*)calloc(INITIAL_OBJECT_CAPACITY, sizeof(Object));
|
||||
if (!tbl->data) return 0;
|
||||
tbl->count = 0;
|
||||
tbl->capacity = INITIAL_OBJECT_CAPACITY;
|
||||
pthread_mutex_init(&tbl->lock, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void object_table_destroy(ObjectTable *tbl) {
|
||||
if (tbl->data) {
|
||||
for (int i = 0; i < tbl->count; i++) {
|
||||
if (tbl->data[i].field_data) free(tbl->data[i].field_data);
|
||||
}
|
||||
free(tbl->data);
|
||||
tbl->data = NULL;
|
||||
}
|
||||
pthread_mutex_destroy(&tbl->lock);
|
||||
}
|
||||
|
||||
int object_table_add(ObjectTable *tbl, Object obj) {
|
||||
pthread_mutex_lock(&tbl->lock);
|
||||
|
||||
if (tbl->count >= tbl->capacity) {
|
||||
int new_capacity = tbl->capacity * 2;
|
||||
Object *new_data = (Object*)realloc(tbl->data, new_capacity * sizeof(Object));
|
||||
if (!new_data) {
|
||||
pthread_mutex_unlock(&tbl->lock);
|
||||
return -1;
|
||||
}
|
||||
memset(new_data + tbl->capacity, 0, (new_capacity - tbl->capacity) * sizeof(Object));
|
||||
tbl->data = new_data;
|
||||
tbl->capacity = new_capacity;
|
||||
}
|
||||
|
||||
tbl->data[tbl->count] = obj;
|
||||
int index = tbl->count;
|
||||
tbl->count++;
|
||||
|
||||
pthread_mutex_unlock(&tbl->lock);
|
||||
return index;
|
||||
}
|
||||
|
||||
int coroutine_table_init(CoroutineTable *tbl) {
|
||||
tbl->data = (Coroutine*)calloc(INITIAL_COROUTINE_CAPACITY, sizeof(Coroutine));
|
||||
if (!tbl->data) return 0;
|
||||
tbl->count = 0;
|
||||
tbl->capacity = INITIAL_COROUTINE_CAPACITY;
|
||||
pthread_mutex_init(&tbl->lock, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void coroutine_table_destroy(CoroutineTable *tbl) {
|
||||
if (tbl->data) {
|
||||
for (int i = 0; i < tbl->count; i++) {
|
||||
if (tbl->data[i].args) free(tbl->data[i].args);
|
||||
if (tbl->data[i].locals_saved) {
|
||||
for (int j = 0; j < tbl->data[i].loc_cnt_saved; j++) {
|
||||
if (tbl->data[i].locals_saved[j].name) {
|
||||
free(tbl->data[i].locals_saved[j].name);
|
||||
}
|
||||
}
|
||||
free(tbl->data[i].locals_saved);
|
||||
}
|
||||
if (tbl->data[i].memory_saved) free(tbl->data[i].memory_saved);
|
||||
}
|
||||
free(tbl->data);
|
||||
tbl->data = NULL;
|
||||
}
|
||||
pthread_mutex_destroy(&tbl->lock);
|
||||
}
|
||||
|
||||
int coroutine_table_alloc(CoroutineTable *tbl) {
|
||||
pthread_mutex_lock(&tbl->lock);
|
||||
|
||||
for (int i = 0; i < tbl->count; i++) {
|
||||
if (!tbl->data[i].active) {
|
||||
tbl->data[i].active = 1;
|
||||
tbl->data[i].complete = 0;
|
||||
pthread_mutex_unlock(&tbl->lock);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (tbl->count >= tbl->capacity) {
|
||||
int new_capacity = tbl->capacity * 2;
|
||||
Coroutine *new_data = (Coroutine*)realloc(tbl->data, new_capacity * sizeof(Coroutine));
|
||||
if (!new_data) {
|
||||
pthread_mutex_unlock(&tbl->lock);
|
||||
return -1;
|
||||
}
|
||||
memset(new_data + tbl->capacity, 0, (new_capacity - tbl->capacity) * sizeof(Coroutine));
|
||||
tbl->data = new_data;
|
||||
tbl->capacity = new_capacity;
|
||||
}
|
||||
|
||||
int index = tbl->count;
|
||||
tbl->data[index].active = 1;
|
||||
tbl->data[index].complete = 0;
|
||||
tbl->count++;
|
||||
|
||||
pthread_mutex_unlock(&tbl->lock);
|
||||
return index;
|
||||
}
|
||||
|
||||
int string_pool_init(StringPool *pool) {
|
||||
pool->data = (char*)malloc(INITIAL_STRING_POOL_CAPACITY);
|
||||
if (!pool->data) return 0;
|
||||
pool->size = 0;
|
||||
pool->capacity = INITIAL_STRING_POOL_CAPACITY;
|
||||
pthread_mutex_init(&pool->lock, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void string_pool_destroy(StringPool *pool) {
|
||||
if (pool->data) {
|
||||
free(pool->data);
|
||||
pool->data = NULL;
|
||||
}
|
||||
pthread_mutex_destroy(&pool->lock);
|
||||
}
|
||||
|
||||
char* string_pool_add(StringPool *pool, const char *str, size_t len) {
|
||||
pthread_mutex_lock(&pool->lock);
|
||||
|
||||
if (pool->size + len + 1 > pool->capacity) {
|
||||
size_t new_capacity = pool->capacity * 2;
|
||||
while (new_capacity < pool->size + len + 1) new_capacity *= 2;
|
||||
|
||||
char *new_data = (char*)realloc(pool->data, new_capacity);
|
||||
if (!new_data) {
|
||||
pthread_mutex_unlock(&pool->lock);
|
||||
return NULL;
|
||||
}
|
||||
pool->data = new_data;
|
||||
pool->capacity = new_capacity;
|
||||
}
|
||||
|
||||
char *result = pool->data + pool->size;
|
||||
memcpy(result, str, len);
|
||||
result[len] = '\0';
|
||||
pool->size += len + 1;
|
||||
|
||||
pthread_mutex_unlock(&pool->lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
int call_stack_init(CallStack *stack) {
|
||||
stack->frames = (CallStackFrame*)calloc(INITIAL_CALL_STACK_CAPACITY, sizeof(CallStackFrame));
|
||||
if (!stack->frames) return 0;
|
||||
stack->depth = 0;
|
||||
stack->capacity = INITIAL_CALL_STACK_CAPACITY;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void call_stack_destroy(CallStack *stack) {
|
||||
if (stack->frames) {
|
||||
free(stack->frames);
|
||||
stack->frames = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int call_stack_push(CallStack *stack, const char *func_name, const char *filename,
|
||||
int line, int is_native) {
|
||||
if (stack->depth >= stack->capacity) {
|
||||
int new_capacity = stack->capacity * 2;
|
||||
CallStackFrame *new_frames = (CallStackFrame*)realloc(stack->frames,
|
||||
new_capacity * sizeof(CallStackFrame));
|
||||
if (!new_frames) return 0;
|
||||
stack->frames = new_frames;
|
||||
stack->capacity = new_capacity;
|
||||
}
|
||||
|
||||
stack->frames[stack->depth].function_name = func_name;
|
||||
stack->frames[stack->depth].filename = filename;
|
||||
stack->frames[stack->depth].line = line;
|
||||
stack->frames[stack->depth].is_native = is_native;
|
||||
stack->depth++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void call_stack_pop(CallStack *stack) {
|
||||
if (stack->depth > 0) {
|
||||
stack->depth--;
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
#ifndef RUNTIME_H
|
||||
#define RUNTIME_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
long val;
|
||||
double dval;
|
||||
char *text;
|
||||
int line;
|
||||
int column;
|
||||
const char *filename;
|
||||
} Token;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int type;
|
||||
int addr;
|
||||
int is_array;
|
||||
} Symbol;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int entry_point;
|
||||
int param_count;
|
||||
int params_start;
|
||||
int is_async;
|
||||
} Func;
|
||||
|
||||
typedef long (*NativeFunc)(long*, int);
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
NativeFunc func;
|
||||
} NativeFuncDef;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int type;
|
||||
long default_value;
|
||||
int offset;
|
||||
} ClassField;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int func_idx;
|
||||
int is_static;
|
||||
int is_constructor;
|
||||
int is_destructor;
|
||||
} ClassMethod;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int parent_class_idx;
|
||||
ClassField *fields;
|
||||
int field_count;
|
||||
int field_capacity;
|
||||
ClassMethod *methods;
|
||||
int method_count;
|
||||
int method_capacity;
|
||||
int size;
|
||||
int token_start;
|
||||
} ClassDef;
|
||||
|
||||
typedef struct {
|
||||
int class_idx;
|
||||
long *field_data;
|
||||
int ref_count;
|
||||
int marked_for_deletion;
|
||||
} Object;
|
||||
|
||||
typedef struct Coroutine {
|
||||
int active;
|
||||
int complete;
|
||||
long result;
|
||||
void *thread;
|
||||
int func_idx;
|
||||
long *args;
|
||||
int argc;
|
||||
int pc_saved;
|
||||
int sp_saved;
|
||||
int bp_saved;
|
||||
int loc_cnt_saved;
|
||||
Symbol *locals_saved;
|
||||
long *memory_saved;
|
||||
int locals_capacity;
|
||||
int memory_capacity;
|
||||
} Coroutine;
|
||||
|
||||
typedef struct {
|
||||
const char *function_name;
|
||||
const char *filename;
|
||||
int line;
|
||||
int is_native;
|
||||
} CallStackFrame;
|
||||
|
||||
typedef struct {
|
||||
CallStackFrame *frames;
|
||||
int depth;
|
||||
int capacity;
|
||||
} CallStack;
|
||||
|
||||
typedef struct {
|
||||
Token *tokens;
|
||||
int count;
|
||||
int capacity;
|
||||
pthread_rwlock_t lock;
|
||||
} TokenArray;
|
||||
|
||||
typedef struct {
|
||||
long *data;
|
||||
int size;
|
||||
int capacity;
|
||||
pthread_mutex_t lock;
|
||||
} Memory;
|
||||
|
||||
typedef struct {
|
||||
Symbol *data;
|
||||
int count;
|
||||
int capacity;
|
||||
pthread_mutex_t lock;
|
||||
} SymbolTable;
|
||||
|
||||
typedef struct {
|
||||
Func *data;
|
||||
int count;
|
||||
int capacity;
|
||||
pthread_rwlock_t lock;
|
||||
} FuncTable;
|
||||
|
||||
typedef struct {
|
||||
NativeFuncDef *data;
|
||||
int count;
|
||||
int capacity;
|
||||
pthread_rwlock_t lock;
|
||||
} NativeFuncTable;
|
||||
|
||||
typedef struct {
|
||||
ClassDef *data;
|
||||
int count;
|
||||
int capacity;
|
||||
pthread_rwlock_t lock;
|
||||
} ClassTable;
|
||||
|
||||
typedef struct {
|
||||
Object *data;
|
||||
int count;
|
||||
int capacity;
|
||||
pthread_mutex_t lock;
|
||||
} ObjectTable;
|
||||
|
||||
typedef struct {
|
||||
Coroutine *data;
|
||||
int count;
|
||||
int capacity;
|
||||
pthread_mutex_t lock;
|
||||
} CoroutineTable;
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
pthread_mutex_t lock;
|
||||
} StringPool;
|
||||
|
||||
typedef struct {
|
||||
TokenArray tokens;
|
||||
Memory memory;
|
||||
SymbolTable symbols;
|
||||
FuncTable funcs;
|
||||
NativeFuncTable native_funcs;
|
||||
ClassTable classes;
|
||||
ObjectTable objects;
|
||||
CoroutineTable coroutines;
|
||||
StringPool str_pool;
|
||||
CallStack call_stack;
|
||||
|
||||
int pc;
|
||||
int sp;
|
||||
int bp;
|
||||
long ax;
|
||||
int return_flag;
|
||||
|
||||
pthread_mutex_t state_lock;
|
||||
} Runtime;
|
||||
|
||||
extern Runtime *global_runtime;
|
||||
|
||||
Runtime* runtime_create();
|
||||
void runtime_destroy(Runtime *rt);
|
||||
|
||||
int token_array_init(TokenArray *arr);
|
||||
void token_array_destroy(TokenArray *arr);
|
||||
int token_array_ensure_capacity(TokenArray *arr, int min_capacity);
|
||||
int token_array_add(TokenArray *arr, Token token);
|
||||
|
||||
int memory_init(Memory *mem);
|
||||
void memory_destroy(Memory *mem);
|
||||
int memory_ensure_capacity(Memory *mem, int min_size);
|
||||
long memory_get(Memory *mem, int addr);
|
||||
void memory_set(Memory *mem, int addr, long value);
|
||||
|
||||
int symbol_table_init(SymbolTable *tbl);
|
||||
void symbol_table_destroy(SymbolTable *tbl);
|
||||
int symbol_table_ensure_capacity(SymbolTable *tbl, int min_capacity);
|
||||
int symbol_table_add(SymbolTable *tbl, Symbol symbol);
|
||||
|
||||
int func_table_init(FuncTable *tbl);
|
||||
void func_table_destroy(FuncTable *tbl);
|
||||
int func_table_ensure_capacity(FuncTable *tbl, int min_capacity);
|
||||
int func_table_add(FuncTable *tbl, Func func);
|
||||
int func_table_find(FuncTable *tbl, const char *name, int len);
|
||||
|
||||
int native_func_table_init(NativeFuncTable *tbl);
|
||||
void native_func_table_destroy(NativeFuncTable *tbl);
|
||||
int native_func_table_add(NativeFuncTable *tbl, const char *name, NativeFunc func);
|
||||
int native_func_table_find(NativeFuncTable *tbl, const char *name, int len);
|
||||
|
||||
int class_table_init(ClassTable *tbl);
|
||||
void class_table_destroy(ClassTable *tbl);
|
||||
int class_table_add(ClassTable *tbl, ClassDef class_def);
|
||||
int class_table_find(ClassTable *tbl, const char *name, int len);
|
||||
|
||||
int object_table_init(ObjectTable *tbl);
|
||||
void object_table_destroy(ObjectTable *tbl);
|
||||
int object_table_add(ObjectTable *tbl, Object obj);
|
||||
|
||||
int coroutine_table_init(CoroutineTable *tbl);
|
||||
void coroutine_table_destroy(CoroutineTable *tbl);
|
||||
int coroutine_table_alloc(CoroutineTable *tbl);
|
||||
|
||||
int string_pool_init(StringPool *pool);
|
||||
void string_pool_destroy(StringPool *pool);
|
||||
char* string_pool_add(StringPool *pool, const char *str, size_t len);
|
||||
|
||||
int call_stack_init(CallStack *stack);
|
||||
void call_stack_destroy(CallStack *stack);
|
||||
int call_stack_push(CallStack *stack, const char *func_name, const char *filename, int line, int is_native);
|
||||
void call_stack_pop(CallStack *stack);
|
||||
|
||||
#endif
|
||||
+363
@@ -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 = ¤t_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
@@ -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
|
||||
@@ -0,0 +1,684 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include "../../types.h"
|
||||
#include "../../interpreter.h"
|
||||
#include "../../scope.h"
|
||||
#include "async_stdlib.h"
|
||||
#include "../../debug.h"
|
||||
|
||||
extern void register_native_func(char *name, NativeFunc func);
|
||||
extern pthread_mutex_t memory_mutex;
|
||||
extern pthread_mutex_t locals_mutex;
|
||||
extern pthread_mutex_t interpreter_state_mutex;
|
||||
|
||||
#define MAX_ASYNC_OPS 100
|
||||
|
||||
typedef struct {
|
||||
int active;
|
||||
int complete;
|
||||
long result;
|
||||
pthread_t thread;
|
||||
void *data;
|
||||
} AsyncOp;
|
||||
|
||||
static AsyncOp async_ops[MAX_ASYNC_OPS];
|
||||
static pthread_mutex_t async_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int async_initialized = 0;
|
||||
|
||||
typedef struct {
|
||||
FILE *f;
|
||||
int addr;
|
||||
int size;
|
||||
} AsyncFileReadData;
|
||||
|
||||
typedef struct {
|
||||
FILE *f;
|
||||
long buf_arg;
|
||||
int size;
|
||||
} AsyncFileWriteData;
|
||||
|
||||
typedef struct {
|
||||
int sockfd;
|
||||
int addr;
|
||||
int len;
|
||||
int flags;
|
||||
} AsyncRecvData;
|
||||
|
||||
typedef struct {
|
||||
int sockfd;
|
||||
long buf_arg;
|
||||
int len;
|
||||
int flags;
|
||||
} AsyncSendData;
|
||||
|
||||
void init_async() {
|
||||
if (!async_initialized) {
|
||||
for (int i = 0; i < MAX_ASYNC_OPS; i++) {
|
||||
async_ops[i].active = 0;
|
||||
async_ops[i].complete = 0;
|
||||
async_ops[i].result = 0;
|
||||
async_ops[i].data = NULL;
|
||||
}
|
||||
async_initialized = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int alloc_async_op() {
|
||||
init_async();
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
for (int i = 0; i < MAX_ASYNC_OPS; i++) {
|
||||
if (!async_ops[i].active) {
|
||||
async_ops[i].active = 1;
|
||||
async_ops[i].complete = 0;
|
||||
async_ops[i].result = 0;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void* async_fread_thread(void *arg) {
|
||||
int id = *(int*)arg;
|
||||
free(arg);
|
||||
AsyncFileReadData *data = (AsyncFileReadData*)async_ops[id].data;
|
||||
|
||||
char temp_buf[8192];
|
||||
int size = data->size;
|
||||
if (size > 8192) size = 8192;
|
||||
|
||||
int result = fread(temp_buf, 1, size, data->f);
|
||||
if (result > 0) {
|
||||
pthread_mutex_lock(&memory_mutex);
|
||||
for (int i = 0; i < result && data->addr + i < MEM_SIZE; i++) {
|
||||
memory[data->addr + i] = temp_buf[i];
|
||||
}
|
||||
pthread_mutex_unlock(&memory_mutex);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
async_ops[id].result = result;
|
||||
async_ops[id].complete = 1;
|
||||
async_ops[id].data = NULL;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* async_fwrite_thread(void *arg) {
|
||||
int id = *(int*)arg;
|
||||
free(arg);
|
||||
AsyncFileWriteData *data = (AsyncFileWriteData*)async_ops[id].data;
|
||||
|
||||
long result = -1;
|
||||
if (data->buf_arg > MEM_SIZE * 8 || data->buf_arg < 0) {
|
||||
result = fwrite((char*)data->buf_arg, 1, data->size, data->f);
|
||||
} else if (data->buf_arg < MEM_SIZE) {
|
||||
char temp_buf[8192];
|
||||
int size = data->size;
|
||||
if (size > 8192) size = 8192;
|
||||
if (data->buf_arg + size > MEM_SIZE) {
|
||||
size = MEM_SIZE - data->buf_arg;
|
||||
}
|
||||
pthread_mutex_lock(&memory_mutex);
|
||||
for (int i = 0; i < size; i++) {
|
||||
temp_buf[i] = (char)memory[data->buf_arg + i];
|
||||
}
|
||||
pthread_mutex_unlock(&memory_mutex);
|
||||
result = fwrite(temp_buf, 1, size, data->f);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
async_ops[id].result = result;
|
||||
async_ops[id].complete = 1;
|
||||
async_ops[id].data = NULL;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* async_recv_thread(void *arg) {
|
||||
int id = *(int*)arg;
|
||||
free(arg);
|
||||
AsyncRecvData *data = (AsyncRecvData*)async_ops[id].data;
|
||||
|
||||
char temp_buf[8192];
|
||||
int len = data->len;
|
||||
if (len > 8192) len = 8192;
|
||||
|
||||
int result = recv(data->sockfd, temp_buf, len, data->flags);
|
||||
if (result > 0) {
|
||||
pthread_mutex_lock(&memory_mutex);
|
||||
for (int i = 0; i < result && data->addr + i < MEM_SIZE; i++) {
|
||||
memory[data->addr + i] = temp_buf[i];
|
||||
}
|
||||
pthread_mutex_unlock(&memory_mutex);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
async_ops[id].result = result;
|
||||
async_ops[id].complete = 1;
|
||||
async_ops[id].data = NULL;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* async_send_thread(void *arg) {
|
||||
int id = *(int*)arg;
|
||||
free(arg);
|
||||
AsyncSendData *data = (AsyncSendData*)async_ops[id].data;
|
||||
|
||||
long result = -1;
|
||||
if (data->buf_arg > MEM_SIZE * 8 || data->buf_arg < 0) {
|
||||
result = send(data->sockfd, (char*)data->buf_arg, data->len, data->flags);
|
||||
} else if (data->buf_arg < MEM_SIZE) {
|
||||
char temp_buf[8192];
|
||||
int len = data->len;
|
||||
if (len > 8192) len = 8192;
|
||||
if (data->buf_arg + len > MEM_SIZE) {
|
||||
len = MEM_SIZE - data->buf_arg;
|
||||
}
|
||||
pthread_mutex_lock(&memory_mutex);
|
||||
for (int i = 0; i < len; i++) {
|
||||
temp_buf[i] = (char)memory[data->buf_arg + i];
|
||||
}
|
||||
pthread_mutex_unlock(&memory_mutex);
|
||||
result = send(data->sockfd, temp_buf, len, data->flags);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
async_ops[id].result = result;
|
||||
async_ops[id].complete = 1;
|
||||
async_ops[id].data = NULL;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
long native_async_fread(long *args, int argc) {
|
||||
if (!args || argc < 3) return -1;
|
||||
FILE *f = (FILE*)args[0];
|
||||
int addr = (int)args[1];
|
||||
int size = (int)args[2];
|
||||
if (!f || addr < 0 || addr >= MEM_SIZE || size <= 0) return -1;
|
||||
|
||||
int id = alloc_async_op();
|
||||
if (id < 0) return -1;
|
||||
|
||||
AsyncFileReadData *data = malloc(sizeof(AsyncFileReadData));
|
||||
if (!data) {
|
||||
error("Failed to allocate async read data");
|
||||
return -1;
|
||||
}
|
||||
data->f = f;
|
||||
data->addr = addr;
|
||||
data->size = size;
|
||||
async_ops[id].data = data;
|
||||
|
||||
int *id_ptr = malloc(sizeof(int));
|
||||
if (!id_ptr) {
|
||||
free(data);
|
||||
error("Failed to allocate async operation ID");
|
||||
return -1;
|
||||
}
|
||||
*id_ptr = id;
|
||||
pthread_create(&async_ops[id].thread, NULL, async_fread_thread, id_ptr);
|
||||
return id;
|
||||
}
|
||||
|
||||
long native_async_fwrite(long *args, int argc) {
|
||||
if (!args || argc < 3) return -1;
|
||||
FILE *f = (FILE*)args[0];
|
||||
long buf_arg = args[1];
|
||||
int size = (int)args[2];
|
||||
if (!f || size <= 0) return -1;
|
||||
|
||||
int id = alloc_async_op();
|
||||
if (id < 0) return -1;
|
||||
|
||||
AsyncFileWriteData *data = malloc(sizeof(AsyncFileWriteData));
|
||||
if (!data) {
|
||||
error("Failed to allocate async write data");
|
||||
return -1;
|
||||
}
|
||||
data->f = f;
|
||||
data->buf_arg = buf_arg;
|
||||
data->size = size;
|
||||
async_ops[id].data = data;
|
||||
|
||||
int *id_ptr = malloc(sizeof(int));
|
||||
if (!id_ptr) {
|
||||
free(data);
|
||||
error("Failed to allocate async operation ID");
|
||||
return -1;
|
||||
}
|
||||
*id_ptr = id;
|
||||
pthread_create(&async_ops[id].thread, NULL, async_fwrite_thread, id_ptr);
|
||||
return id;
|
||||
}
|
||||
|
||||
long native_async_recv(long *args, int argc) {
|
||||
if (!args || argc < 4) return -1;
|
||||
int sockfd = (int)args[0];
|
||||
int addr = (int)args[1];
|
||||
int len = (int)args[2];
|
||||
int flags = (int)args[3];
|
||||
if (addr < 0 || addr >= MEM_SIZE) return -1;
|
||||
|
||||
int id = alloc_async_op();
|
||||
if (id < 0) return -1;
|
||||
|
||||
AsyncRecvData *data = malloc(sizeof(AsyncRecvData));
|
||||
if (!data) {
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
async_ops[id].active = 0;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
return -1;
|
||||
}
|
||||
data->sockfd = sockfd;
|
||||
data->addr = addr;
|
||||
data->len = len;
|
||||
data->flags = flags;
|
||||
async_ops[id].data = data;
|
||||
|
||||
int *id_ptr = malloc(sizeof(int));
|
||||
if (!id_ptr) {
|
||||
free(data);
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
async_ops[id].active = 0;
|
||||
async_ops[id].data = NULL;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
return -1;
|
||||
}
|
||||
*id_ptr = id;
|
||||
pthread_create(&async_ops[id].thread, NULL, async_recv_thread, id_ptr);
|
||||
return id;
|
||||
}
|
||||
|
||||
long native_async_send(long *args, int argc) {
|
||||
if (!args || argc < 4) return -1;
|
||||
int sockfd = (int)args[0];
|
||||
long buf_arg = args[1];
|
||||
int len = (int)args[2];
|
||||
int flags = (int)args[3];
|
||||
|
||||
int id = alloc_async_op();
|
||||
if (id < 0) return -1;
|
||||
|
||||
AsyncSendData *data = malloc(sizeof(AsyncSendData));
|
||||
if (!data) {
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
async_ops[id].active = 0;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
return -1;
|
||||
}
|
||||
data->sockfd = sockfd;
|
||||
data->buf_arg = buf_arg;
|
||||
data->len = len;
|
||||
data->flags = flags;
|
||||
async_ops[id].data = data;
|
||||
|
||||
int *id_ptr = malloc(sizeof(int));
|
||||
if (!id_ptr) {
|
||||
free(data);
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
async_ops[id].active = 0;
|
||||
async_ops[id].data = NULL;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
return -1;
|
||||
}
|
||||
*id_ptr = id;
|
||||
pthread_create(&async_ops[id].thread, NULL, async_send_thread, id_ptr);
|
||||
return id;
|
||||
}
|
||||
|
||||
long native_async_wait(long *args, int argc) {
|
||||
if (!args || argc < 1) return -1;
|
||||
int id = (int)args[0];
|
||||
if (id < 0 || id >= MAX_ASYNC_OPS) return -1;
|
||||
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
if (!async_ops[id].active) {
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
return -1;
|
||||
}
|
||||
pthread_t thread = async_ops[id].thread;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
|
||||
pthread_join(thread, NULL);
|
||||
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
long result = async_ops[id].result;
|
||||
async_ops[id].active = 0;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
long native_async_poll(long *args, int argc) {
|
||||
if (!args || argc < 1) return 0;
|
||||
int id = (int)args[0];
|
||||
if (id < 0 || id >= MAX_ASYNC_OPS) return 0;
|
||||
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
if (!async_ops[id].active) {
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
return 0;
|
||||
}
|
||||
int complete = async_ops[id].complete;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
|
||||
return complete;
|
||||
}
|
||||
|
||||
long native_async_result(long *args, int argc) {
|
||||
if (!args || argc < 1) return -1;
|
||||
int id = (int)args[0];
|
||||
if (id < 0 || id >= MAX_ASYNC_OPS) return -1;
|
||||
|
||||
pthread_mutex_lock(&async_mutex);
|
||||
if (!async_ops[id].active) {
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
return -1;
|
||||
}
|
||||
long result = async_ops[id].result;
|
||||
async_ops[id].active = 0;
|
||||
pthread_mutex_unlock(&async_mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static pthread_mutex_t coroutine_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int coroutine_initialized = 0;
|
||||
|
||||
void init_coroutines() {
|
||||
if (!coroutine_initialized) {
|
||||
pthread_mutex_lock(&coroutine_mutex);
|
||||
for (int i = 0; i < MAX_COROUTINES; i++) {
|
||||
coroutines[i].active = 0;
|
||||
coroutines[i].complete = 0;
|
||||
coroutines[i].result = 0;
|
||||
coroutines[i].thread = NULL;
|
||||
}
|
||||
coroutine_count = 0;
|
||||
coroutine_initialized = 1;
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
int alloc_coroutine() {
|
||||
init_coroutines();
|
||||
pthread_mutex_lock(&coroutine_mutex);
|
||||
for (int i = 1; i < MAX_COROUTINES; i++) {
|
||||
if (!coroutines[i].active) {
|
||||
coroutines[i].active = 1;
|
||||
coroutines[i].complete = 0;
|
||||
coroutines[i].result = 0;
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int coroutine_id;
|
||||
} CoroutineThreadData;
|
||||
|
||||
void* coroutine_thread_func(void *arg) {
|
||||
CoroutineThreadData *data = (CoroutineThreadData*)arg;
|
||||
int coro_id = data->coroutine_id;
|
||||
free(data);
|
||||
|
||||
DEBUG_LOG("Coroutine thread started: coro_id=%d", coro_id);
|
||||
|
||||
if (coro_id < 0 || coro_id >= MAX_COROUTINES) {
|
||||
DEBUG_LOG("ERROR: Invalid coro_id=%d (max=%d)", coro_id, MAX_COROUTINES);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&coroutine_mutex);
|
||||
|
||||
Coroutine *coro = &coroutines[coro_id];
|
||||
if (!coro->active) {
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
DEBUG_LOG("ERROR: Coroutine %d was deactivated before thread started", coro_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int func_idx = coro->func_idx;
|
||||
int argc = coro->argc;
|
||||
|
||||
long local_args[10];
|
||||
for (int i = 0; i < argc && i < 10; i++) {
|
||||
local_args[i] = coro->args ? coro->args[i] : 0;
|
||||
}
|
||||
|
||||
if (func_idx < 0 || func_idx >= func_cnt) {
|
||||
coro->complete = 1;
|
||||
coro->result = 0;
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern ExecutionContext* context_snapshot();
|
||||
extern void context_restore(ExecutionContext *ctx);
|
||||
extern void context_destroy(ExecutionContext *ctx);
|
||||
|
||||
ExecutionContext *saved_ctx = context_snapshot();
|
||||
if (!saved_ctx) {
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
coro->complete = 1;
|
||||
coro->result = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
loc_cnt = 0;
|
||||
sp = 0;
|
||||
bp = 0;
|
||||
return_flag = 0;
|
||||
|
||||
if (sp >= MEM_SIZE) {
|
||||
context_destroy(saved_ctx);
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
coro->complete = 1;
|
||||
coro->result = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memory[sp] = bp;
|
||||
bp = sp++;
|
||||
|
||||
if (sp >= MEM_SIZE - 2) {
|
||||
context_destroy(saved_ctx);
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
coro->complete = 1;
|
||||
coro->result = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memory[sp++] = 0;
|
||||
memory[sp++] = 0;
|
||||
|
||||
int param_base = sp;
|
||||
for (int i = 0; i < argc && i < 10; i++) {
|
||||
if (sp >= MEM_SIZE) break;
|
||||
memory[sp++] = local_args[i];
|
||||
}
|
||||
|
||||
pthread_rwlock_rdlock(&tokens_rwlock);
|
||||
int scan_pc = funcs[func_idx].params_start;
|
||||
int param_idx = 0;
|
||||
int safe_tk_idx = tk_idx;
|
||||
while (scan_pc < MAX_TOK && scan_pc < safe_tk_idx && tokens[scan_pc].type != ')') {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
scan_pc++;
|
||||
}
|
||||
pthread_rwlock_unlock(&tokens_rwlock);
|
||||
|
||||
pc = funcs[func_idx].entry_point;
|
||||
ax = 0;
|
||||
|
||||
extern void statement();
|
||||
statement();
|
||||
|
||||
long result_val = ax;
|
||||
|
||||
context_restore(saved_ctx);
|
||||
context_destroy(saved_ctx);
|
||||
|
||||
coro->result = result_val;
|
||||
coro->complete = 1;
|
||||
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static long execute_function_sync(int func_idx, long *args, int argc) {
|
||||
if (func_idx < 0 || func_idx >= func_cnt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int saved_pc = pc;
|
||||
long saved_ax = ax;
|
||||
int saved_return_flag = return_flag;
|
||||
|
||||
scope_push();
|
||||
|
||||
int param_base = sp;
|
||||
for (int i = 0; i < argc && i < 10 && sp < MEM_SIZE; i++) {
|
||||
memory[sp++] = args[i];
|
||||
}
|
||||
|
||||
pthread_rwlock_rdlock(&tokens_rwlock);
|
||||
int scan_pc = funcs[func_idx].params_start;
|
||||
int param_idx = 0;
|
||||
int safe_tk_idx = tk_idx;
|
||||
while (scan_pc < MAX_TOK && scan_pc < safe_tk_idx && tokens[scan_pc].type != ')') {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
scan_pc++;
|
||||
}
|
||||
pthread_rwlock_unlock(&tokens_rwlock);
|
||||
|
||||
pc = funcs[func_idx].entry_point;
|
||||
ax = 0;
|
||||
return_flag = 0;
|
||||
|
||||
extern void statement();
|
||||
statement();
|
||||
|
||||
long result = ax;
|
||||
|
||||
scope_pop();
|
||||
|
||||
pc = saved_pc;
|
||||
ax = saved_ax;
|
||||
return_flag = saved_return_flag;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int create_coroutine(int func_idx, long *args, int argc) {
|
||||
DEBUG_FUNC_ENTRY();
|
||||
DEBUG_INT("func_idx", func_idx);
|
||||
DEBUG_INT("argc", argc);
|
||||
|
||||
int coro_id = alloc_coroutine();
|
||||
if (coro_id < 0) {
|
||||
DEBUG_LOG("ERROR: Failed to allocate coroutine");
|
||||
return -1;
|
||||
}
|
||||
DEBUG_INT("allocated_coro_id", coro_id);
|
||||
|
||||
Coroutine *coro = &coroutines[coro_id];
|
||||
|
||||
coro->func_idx = func_idx;
|
||||
coro->argc = argc;
|
||||
coro->args = NULL;
|
||||
|
||||
long result = execute_function_sync(func_idx, args, argc);
|
||||
|
||||
coro->result = result;
|
||||
coro->complete = 1;
|
||||
|
||||
DEBUG_FUNC_EXIT();
|
||||
return coro_id;
|
||||
}
|
||||
|
||||
long native_await(long *args, int argc) {
|
||||
if (!args || argc < 1) return 0;
|
||||
int coro_id = (int)args[0];
|
||||
|
||||
if (coro_id < 0 || coro_id >= MAX_COROUTINES) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Coroutine *coro = &coroutines[coro_id];
|
||||
if (!coro->active) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long result = coro->result;
|
||||
coro->active = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
long native_gather(long *args, int argc) {
|
||||
if (!args || argc < 1) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void register_async_stdlib() {
|
||||
register_native_func("async_fread", native_async_fread);
|
||||
register_native_func("async_fwrite", native_async_fwrite);
|
||||
register_native_func("async_recv", native_async_recv);
|
||||
register_native_func("async_send", native_async_send);
|
||||
register_native_func("async_wait", native_async_wait);
|
||||
register_native_func("async_poll", native_async_poll);
|
||||
register_native_func("async_result", native_async_result);
|
||||
register_native_func("await", native_await);
|
||||
register_native_func("gather", native_gather);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef ASYNC_STDLIB_H
|
||||
#define ASYNC_STDLIB_H
|
||||
|
||||
void register_async_stdlib();
|
||||
int create_coroutine(int func_idx, long *args, int argc);
|
||||
|
||||
#endif
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef BENCHMARK_STDLIB_H
|
||||
#define BENCHMARK_STDLIB_H
|
||||
|
||||
void register_benchmark_stdlib();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include "../../types.h"
|
||||
#include "constants_stdlib.h"
|
||||
|
||||
extern void register_native_func(char *name, NativeFunc func);
|
||||
|
||||
long native_AF_INET(long *args, int argc) {
|
||||
return AF_INET;
|
||||
}
|
||||
|
||||
long native_SOCK_STREAM(long *args, int argc) {
|
||||
return SOCK_STREAM;
|
||||
}
|
||||
|
||||
long native_SEEK_SET(long *args, int argc) {
|
||||
return SEEK_SET;
|
||||
}
|
||||
|
||||
long native_SEEK_CUR(long *args, int argc) {
|
||||
return SEEK_CUR;
|
||||
}
|
||||
|
||||
long native_SEEK_END(long *args, int argc) {
|
||||
return SEEK_END;
|
||||
}
|
||||
|
||||
void register_constants_stdlib() {
|
||||
register_native_func("AF_INET", native_AF_INET);
|
||||
register_native_func("SOCK_STREAM", native_SOCK_STREAM);
|
||||
register_native_func("SEEK_SET", native_SEEK_SET);
|
||||
register_native_func("SEEK_CUR", native_SEEK_CUR);
|
||||
register_native_func("SEEK_END", native_SEEK_END);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef CONSTANTS_STDLIB_H
|
||||
#define CONSTANTS_STDLIB_H
|
||||
|
||||
void register_constants_stdlib();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "../../types.h"
|
||||
#include "../../tokenizer.h"
|
||||
#include "../../interpreter.h"
|
||||
#include "../../native_functions.h"
|
||||
#include "eval_stdlib.h"
|
||||
|
||||
extern Token tokens[];
|
||||
extern int tk_idx;
|
||||
extern int pc;
|
||||
extern long memory[];
|
||||
extern int sp;
|
||||
extern int bp;
|
||||
extern Symbol locals[];
|
||||
extern int loc_cnt;
|
||||
extern long ax;
|
||||
extern int return_flag;
|
||||
extern char str_pool[];
|
||||
extern int str_pool_idx;
|
||||
extern pthread_mutex_t token_mutex;
|
||||
|
||||
long native_eval(long *args, int argc) {
|
||||
if (argc < 1 || !args) {
|
||||
printf("Error: eval requires at least 1 argument (code string)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *code = (char*)args[0];
|
||||
if (!code) {
|
||||
printf("Error: eval received null code string\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int code_len = strlen(code);
|
||||
if (code_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (code_len > MAX_SRC - 1000) {
|
||||
printf("Error: eval code too large\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int saved_pc = pc;
|
||||
int old_pc_val = pc;
|
||||
|
||||
extern long expression();
|
||||
|
||||
char temp_buffer[1024];
|
||||
if (code_len > 1000) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inserted_pos = -1;
|
||||
for (int i = 0; i < tk_idx && i < MAX_TOK; i++) {
|
||||
if (tokens[i].type == 0) {
|
||||
inserted_pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inserted_pos == -1) {
|
||||
inserted_pos = tk_idx;
|
||||
}
|
||||
|
||||
if (inserted_pos + 50 >= MAX_TOK) {
|
||||
printf("Error: token buffer full\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int original_tk_idx = tk_idx;
|
||||
|
||||
Token temp_tokens[1000];
|
||||
int temp_tk_idx = 0;
|
||||
|
||||
char *eval_copy = (char*)malloc(code_len + 1);
|
||||
if (!eval_copy) {
|
||||
return 0;
|
||||
}
|
||||
strcpy(eval_copy, code);
|
||||
|
||||
char *s = eval_copy;
|
||||
while (*s && temp_tk_idx < 100) {
|
||||
while (*s && (*s == ' ' || *s == '\t' || *s == '\n')) s++;
|
||||
if (!*s) break;
|
||||
|
||||
Token *t = &temp_tokens[temp_tk_idx++];
|
||||
t->text = s;
|
||||
t->line = 1;
|
||||
t->column = 1;
|
||||
t->filename = "<eval>";
|
||||
|
||||
if (*s >= '0' && *s <= '9') {
|
||||
t->type = 128;
|
||||
t->val = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
t->val = t->val * 10 + (*s - '0');
|
||||
s++;
|
||||
}
|
||||
} else if ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z')) {
|
||||
t->type = 131;
|
||||
int len = 0;
|
||||
char *start = s;
|
||||
while ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
|
||||
(*s >= '0' && *s <= '9') || *s == '_') {
|
||||
len++;
|
||||
s++;
|
||||
}
|
||||
t->val = len;
|
||||
t->text = start;
|
||||
} else {
|
||||
t->type = *s;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
if (temp_tk_idx > 0) {
|
||||
temp_tokens[temp_tk_idx].type = 0;
|
||||
}
|
||||
|
||||
pthread_rwlock_wrlock(&tokens_rwlock);
|
||||
|
||||
Token *saved_tokens = (Token*)malloc(sizeof(Token) * MAX_TOK);
|
||||
if (!saved_tokens) {
|
||||
pthread_rwlock_unlock(&tokens_rwlock);
|
||||
free(eval_copy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int saved_tk_idx = tk_idx;
|
||||
for (int i = 0; i < tk_idx && i < MAX_TOK; i++) {
|
||||
saved_tokens[i] = tokens[i];
|
||||
}
|
||||
|
||||
pc = 0;
|
||||
long result = 0;
|
||||
|
||||
if (temp_tk_idx > 0) {
|
||||
for (int i = 0; i < temp_tk_idx && i < 100; i++) {
|
||||
tokens[i] = temp_tokens[i];
|
||||
}
|
||||
tk_idx = temp_tk_idx;
|
||||
if (tk_idx < MAX_TOK) {
|
||||
tokens[tk_idx].type = 0;
|
||||
}
|
||||
|
||||
pc = 0;
|
||||
result = expression();
|
||||
}
|
||||
|
||||
for (int i = 0; i < saved_tk_idx && i < MAX_TOK; i++) {
|
||||
tokens[i] = saved_tokens[i];
|
||||
}
|
||||
tk_idx = saved_tk_idx;
|
||||
pc = saved_pc;
|
||||
|
||||
free(saved_tokens);
|
||||
pthread_rwlock_unlock(&tokens_rwlock);
|
||||
|
||||
free(eval_copy);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void register_eval_stdlib() {
|
||||
register_native_func("eval", native_eval);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef EVAL_STDLIB_H
|
||||
#define EVAL_STDLIB_H
|
||||
|
||||
void register_eval_stdlib();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,211 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "../../types.h"
|
||||
#include "../../interpreter.h"
|
||||
#include "file_stdlib.h"
|
||||
|
||||
extern void register_native_func(char *name, NativeFunc func);
|
||||
extern pthread_mutex_t str_pool_mutex;
|
||||
|
||||
long native_fopen(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return 0;
|
||||
}
|
||||
char *filename = (char*)args[0];
|
||||
char *mode = (char*)args[1];
|
||||
if (!filename || !mode) {
|
||||
return 0;
|
||||
}
|
||||
FILE *f = fopen(filename, mode);
|
||||
return (long)f;
|
||||
}
|
||||
|
||||
long native_fclose(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return -1;
|
||||
}
|
||||
FILE *f = (FILE*)args[0];
|
||||
if (!f) {
|
||||
return -1;
|
||||
}
|
||||
return fclose(f);
|
||||
}
|
||||
|
||||
long native_fread(long *args, int argc) {
|
||||
if (!args || argc < 3) {
|
||||
return -1;
|
||||
}
|
||||
FILE *f = (FILE*)args[0];
|
||||
char *buffer = (char*)args[1];
|
||||
int size = (int)args[2];
|
||||
|
||||
if (!f || !buffer || size <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (size > 8192) size = 8192;
|
||||
|
||||
int result = fread(buffer, 1, size, f);
|
||||
return result;
|
||||
}
|
||||
|
||||
long native_fwrite(long *args, int argc) {
|
||||
if (!args || argc < 3) {
|
||||
return -1;
|
||||
}
|
||||
FILE *f = (FILE*)args[0];
|
||||
long buf_arg = args[1];
|
||||
int size = (int)args[2];
|
||||
|
||||
if (!f || size <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf_arg > MEM_SIZE * 8 || buf_arg < 0) {
|
||||
return fwrite((char*)buf_arg, 1, size, f);
|
||||
}
|
||||
|
||||
if (buf_arg >= MEM_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char temp_buf[8192];
|
||||
if (size > 8192) size = 8192;
|
||||
|
||||
if (buf_arg + size > MEM_SIZE) {
|
||||
size = MEM_SIZE - buf_arg;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size && buf_arg + i < MEM_SIZE; i++) {
|
||||
temp_buf[i] = (char)memory[buf_arg + i];
|
||||
}
|
||||
|
||||
return fwrite(temp_buf, 1, size, f);
|
||||
}
|
||||
|
||||
long native_fgets(long *args, int argc) {
|
||||
static char empty_str[] = "";
|
||||
if (!args || argc < 2) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
FILE *f = (FILE*)args[0];
|
||||
int max_size = (int)args[1];
|
||||
|
||||
if (!f || max_size <= 0) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&str_pool_mutex);
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
char *result = &str_pool[str_pool_idx];
|
||||
if (max_size > STR_POOL_SIZE - str_pool_idx) {
|
||||
max_size = STR_POOL_SIZE - str_pool_idx;
|
||||
}
|
||||
|
||||
if (fgets(result, max_size, f) == NULL) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
int len = strlen(result);
|
||||
str_pool_idx += len + 1;
|
||||
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
long native_fputs(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
FILE *f = (FILE*)args[0];
|
||||
char *str = (char*)args[1];
|
||||
|
||||
if (!f || !str) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fputs(str, f);
|
||||
}
|
||||
|
||||
long native_feof(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 1;
|
||||
}
|
||||
FILE *f = (FILE*)args[0];
|
||||
if (!f) {
|
||||
return 1;
|
||||
}
|
||||
return feof(f);
|
||||
}
|
||||
|
||||
long native_ftell(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return -1;
|
||||
}
|
||||
FILE *f = (FILE*)args[0];
|
||||
if (!f) {
|
||||
return -1;
|
||||
}
|
||||
return ftell(f);
|
||||
}
|
||||
|
||||
long native_fseek(long *args, int argc) {
|
||||
if (!args || argc < 3) {
|
||||
return -1;
|
||||
}
|
||||
FILE *f = (FILE*)args[0];
|
||||
long offset = args[1];
|
||||
int whence = (int)args[2];
|
||||
|
||||
if (!f) {
|
||||
return -1;
|
||||
}
|
||||
return fseek(f, offset, whence);
|
||||
}
|
||||
|
||||
long native_fremove(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return -1;
|
||||
}
|
||||
char *filename = (char*)args[0];
|
||||
if (!filename) {
|
||||
return -1;
|
||||
}
|
||||
return remove(filename);
|
||||
}
|
||||
|
||||
long native_frename(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
char *oldname = (char*)args[0];
|
||||
char *newname = (char*)args[1];
|
||||
if (!oldname || !newname) {
|
||||
return -1;
|
||||
}
|
||||
return rename(oldname, newname);
|
||||
}
|
||||
|
||||
void register_file_stdlib() {
|
||||
register_native_func("fopen", native_fopen);
|
||||
register_native_func("fclose", native_fclose);
|
||||
register_native_func("fread", native_fread);
|
||||
register_native_func("fwrite", native_fwrite);
|
||||
register_native_func("fgets", native_fgets);
|
||||
register_native_func("fputs", native_fputs);
|
||||
register_native_func("feof", native_feof);
|
||||
register_native_func("ftell", native_ftell);
|
||||
register_native_func("fseek", native_fseek);
|
||||
register_native_func("fremove", native_fremove);
|
||||
register_native_func("frename", native_frename);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef FILE_STDLIB_H
|
||||
#define FILE_STDLIB_H
|
||||
|
||||
void register_file_stdlib();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,142 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "../../types.h"
|
||||
#include "socket_stdlib.h"
|
||||
|
||||
extern void register_native_func(char *name, NativeFunc func);
|
||||
|
||||
long native_socket(long *args, int argc) {
|
||||
if (!args || argc < 3) {
|
||||
return -1;
|
||||
}
|
||||
int domain = (int)args[0];
|
||||
int type = (int)args[1];
|
||||
int protocol = (int)args[2];
|
||||
int sockfd = socket(domain, type, protocol);
|
||||
if (sockfd >= 0) {
|
||||
int opt = 1;
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
}
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
long native_bind(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
int sockfd = (int)args[0];
|
||||
int port = (int)args[1];
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
return bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
|
||||
}
|
||||
|
||||
long native_listen(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
int sockfd = (int)args[0];
|
||||
int backlog = (int)args[1];
|
||||
return listen(sockfd, backlog);
|
||||
}
|
||||
|
||||
long native_accept(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return -1;
|
||||
}
|
||||
int sockfd = (int)args[0];
|
||||
return accept(sockfd, NULL, NULL);
|
||||
}
|
||||
|
||||
long native_recv(long *args, int argc) {
|
||||
if (!args || argc < 4) {
|
||||
return -1;
|
||||
}
|
||||
int sockfd = (int)args[0];
|
||||
int addr = (int)args[1];
|
||||
int len = (int)args[2];
|
||||
int flags = (int)args[3];
|
||||
|
||||
if (addr < 0 || addr >= MEM_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char temp_buf[8192];
|
||||
if (len > 8192) len = 8192;
|
||||
if (len < 0) len = 0;
|
||||
|
||||
if (addr + len > MEM_SIZE) {
|
||||
len = MEM_SIZE - addr;
|
||||
}
|
||||
|
||||
int result = recv(sockfd, temp_buf, len, flags);
|
||||
if (result > 0) {
|
||||
for (int i = 0; i < result && addr + i < MEM_SIZE; i++) {
|
||||
memory[addr + i] = temp_buf[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
long native_send(long *args, int argc) {
|
||||
if (!args || argc < 4) {
|
||||
return -1;
|
||||
}
|
||||
int sockfd = (int)args[0];
|
||||
long buf_arg = args[1];
|
||||
int len = (int)args[2];
|
||||
int flags = (int)args[3];
|
||||
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf_arg > MEM_SIZE * 8 || buf_arg < 0) {
|
||||
return send(sockfd, (char*)buf_arg, len, flags);
|
||||
}
|
||||
|
||||
if (buf_arg >= MEM_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char temp_buf[8192];
|
||||
if (len > 8192) len = 8192;
|
||||
|
||||
if (buf_arg + len > MEM_SIZE) {
|
||||
len = MEM_SIZE - buf_arg;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len && buf_arg + i < MEM_SIZE; i++) {
|
||||
temp_buf[i] = (char)memory[buf_arg + i];
|
||||
}
|
||||
|
||||
return send(sockfd, temp_buf, len, flags);
|
||||
}
|
||||
|
||||
long native_close(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return -1;
|
||||
}
|
||||
int fd = (int)args[0];
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
void register_socket_stdlib() {
|
||||
register_native_func("socket", native_socket);
|
||||
register_native_func("bind", native_bind);
|
||||
register_native_func("listen", native_listen);
|
||||
register_native_func("accept", native_accept);
|
||||
register_native_func("recv", native_recv);
|
||||
register_native_func("send", native_send);
|
||||
register_native_func("close", native_close);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef SOCKET_STDLIB_H
|
||||
#define SOCKET_STDLIB_H
|
||||
|
||||
void register_socket_stdlib();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,180 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include "../../types.h"
|
||||
#include "math_stdlib.h"
|
||||
|
||||
extern void register_native_func(char *name, NativeFunc func);
|
||||
|
||||
long native_sqrt(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)sqrt((double)args[0]);
|
||||
}
|
||||
|
||||
long native_pow(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return 0;
|
||||
}
|
||||
return (long)pow((double)args[0], (double)args[1]);
|
||||
}
|
||||
|
||||
long native_sin(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)(sin((double)args[0]) * 1000000);
|
||||
}
|
||||
|
||||
long native_cos(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)(cos((double)args[0]) * 1000000);
|
||||
}
|
||||
|
||||
long native_tan(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)(tan((double)args[0]) * 1000000);
|
||||
}
|
||||
|
||||
long native_abs(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)abs((int)args[0]);
|
||||
}
|
||||
|
||||
long native_floor(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)floor((double)args[0]);
|
||||
}
|
||||
|
||||
long native_ceil(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
return (long)ceil((double)args[0]);
|
||||
}
|
||||
|
||||
long native_int_to_double(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
union { double d; long l; } u;
|
||||
u.d = (double)args[0];
|
||||
return u.l;
|
||||
}
|
||||
|
||||
long native_double_to_int(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
union { double d; long l; } u;
|
||||
u.l = args[0];
|
||||
return (long)u.d;
|
||||
}
|
||||
|
||||
long native_double_add(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return 0;
|
||||
}
|
||||
union { double d; long l; } u1, u2, result;
|
||||
u1.l = args[0];
|
||||
u2.l = args[1];
|
||||
result.d = u1.d + u2.d;
|
||||
return result.l;
|
||||
}
|
||||
|
||||
long native_double_sub(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return 0;
|
||||
}
|
||||
union { double d; long l; } u1, u2, result;
|
||||
u1.l = args[0];
|
||||
u2.l = args[1];
|
||||
result.d = u1.d - u2.d;
|
||||
return result.l;
|
||||
}
|
||||
|
||||
long native_double_mul(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return 0;
|
||||
}
|
||||
union { double d; long l; } u1, u2, result;
|
||||
u1.l = args[0];
|
||||
u2.l = args[1];
|
||||
result.d = u1.d * u2.d;
|
||||
return result.l;
|
||||
}
|
||||
|
||||
long native_double_div(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return 0;
|
||||
}
|
||||
union { double d; long l; } u1, u2, result;
|
||||
u1.l = args[0];
|
||||
u2.l = args[1];
|
||||
if (u2.d != 0.0) {
|
||||
result.d = u1.d / u2.d;
|
||||
} else {
|
||||
result.d = 0.0;
|
||||
}
|
||||
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);
|
||||
register_native_func("sin", native_sin);
|
||||
register_native_func("cos", native_cos);
|
||||
register_native_func("tan", native_tan);
|
||||
register_native_func("abs", native_abs);
|
||||
register_native_func("floor", native_floor);
|
||||
register_native_func("ceil", native_ceil);
|
||||
register_native_func("int_to_double", native_int_to_double);
|
||||
register_native_func("double_to_int", native_double_to_int);
|
||||
register_native_func("double_add", native_double_add);
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef MATH_STDLIB_H
|
||||
#define MATH_STDLIB_H
|
||||
|
||||
void register_math_stdlib();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "stdlib.h"
|
||||
#include "string/string_stdlib.h"
|
||||
#include "math/math_stdlib.h"
|
||||
#include "io/file_stdlib.h"
|
||||
#include "io/socket_stdlib.h"
|
||||
#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();
|
||||
register_math_stdlib();
|
||||
register_file_stdlib();
|
||||
register_socket_stdlib();
|
||||
register_async_stdlib();
|
||||
register_constants_stdlib();
|
||||
register_eval_stdlib();
|
||||
register_benchmark_stdlib();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef STDLIB_H
|
||||
#define STDLIB_H
|
||||
|
||||
void register_stdlib();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,328 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <pthread.h>
|
||||
#include "../../types.h"
|
||||
#include "../../interpreter.h"
|
||||
#include "string_stdlib.h"
|
||||
|
||||
extern void register_native_func(char *name, NativeFunc func);
|
||||
extern pthread_mutex_t str_pool_mutex;
|
||||
|
||||
long native_strlen(long *args, int argc) {
|
||||
if (!args || argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
if (!str) {
|
||||
return 0;
|
||||
}
|
||||
return strlen(str);
|
||||
}
|
||||
|
||||
long native_strpos(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
char *haystack = (char*)args[0];
|
||||
char *needle = (char*)args[1];
|
||||
if (!haystack || !needle) {
|
||||
return -1;
|
||||
}
|
||||
char *pos = strstr(haystack, needle);
|
||||
if (pos) {
|
||||
return pos - haystack;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
long native_substr(long *args, int argc) {
|
||||
static char empty_str[] = "";
|
||||
if (!args || argc < 3) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
if (!str) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
int start = (int)args[1];
|
||||
int length = (int)args[2];
|
||||
|
||||
pthread_mutex_lock(&str_pool_mutex);
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
char *result = &str_pool[str_pool_idx];
|
||||
int str_len = strlen(str);
|
||||
|
||||
if (start < 0) start = 0;
|
||||
if (start >= str_len) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
return (long)empty_str;
|
||||
}
|
||||
if (start + length > str_len) length = str_len - start;
|
||||
if (length < 0) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
if (str_pool_idx + length + 1 >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
strncpy(result, str + start, length);
|
||||
result[length] = 0;
|
||||
|
||||
str_pool_idx += length + 1;
|
||||
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
long native_upper(long *args, int argc) {
|
||||
static char empty_str[] = "";
|
||||
if (!args || argc < 1) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
if (!str) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&str_pool_mutex);
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
char *result = &str_pool[str_pool_idx];
|
||||
int len = strlen(str);
|
||||
|
||||
if (str_pool_idx + len + 1 >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= len; i++) {
|
||||
result[i] = toupper(str[i]);
|
||||
}
|
||||
|
||||
str_pool_idx += len + 1;
|
||||
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
long native_lower(long *args, int argc) {
|
||||
static char empty_str[] = "";
|
||||
if (!args || argc < 1) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
if (!str) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&str_pool_mutex);
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
char *result = &str_pool[str_pool_idx];
|
||||
int len = strlen(str);
|
||||
|
||||
if (str_pool_idx + len + 1 >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= len; i++) {
|
||||
result[i] = tolower(str[i]);
|
||||
}
|
||||
|
||||
str_pool_idx += len + 1;
|
||||
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
long native_strip(long *args, int argc) {
|
||||
static char empty_str[] = "";
|
||||
if (!args || argc < 1) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
if (!str) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&str_pool_mutex);
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
char *result = &str_pool[str_pool_idx];
|
||||
|
||||
while (*str && isspace(*str)) str++;
|
||||
|
||||
int len = strlen(str);
|
||||
while (len > 0 && isspace(str[len - 1])) len--;
|
||||
|
||||
if (str_pool_idx + len + 1 >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
strncpy(result, str, len);
|
||||
result[len] = 0;
|
||||
|
||||
str_pool_idx += len + 1;
|
||||
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
long native_replace(long *args, int argc) {
|
||||
static char empty_str[] = "";
|
||||
if (!args || argc < 3) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
char *old_str = (char*)args[1];
|
||||
char *new_str = (char*)args[2];
|
||||
if (!str || !old_str || !new_str) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&str_pool_mutex);
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
char *result = &str_pool[str_pool_idx];
|
||||
char *src = str;
|
||||
char *dst = result;
|
||||
int old_len = strlen(old_str);
|
||||
int new_len = strlen(new_str);
|
||||
|
||||
if (old_len == 0) {
|
||||
int str_len = strlen(str);
|
||||
if (str_pool_idx + str_len + 1 >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
strncpy(result, str, str_len);
|
||||
result[str_len] = '\0';
|
||||
str_pool_idx += str_len + 1;
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
while (*src) {
|
||||
if (strncmp(src, old_str, old_len) == 0) {
|
||||
int remaining = strlen(src + old_len);
|
||||
int current_pos = dst - result;
|
||||
if (str_pool_idx + current_pos + new_len + remaining + 1 >= STR_POOL_SIZE) {
|
||||
error("String pool overflow");
|
||||
*dst = 0;
|
||||
str_pool_idx += current_pos + 1;
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
return (long)result;
|
||||
}
|
||||
strncpy(dst, new_str, new_len);
|
||||
dst[new_len] = '\0';
|
||||
dst += new_len;
|
||||
src += old_len;
|
||||
} else {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
}
|
||||
*dst = 0;
|
||||
|
||||
int total_len = dst - result;
|
||||
str_pool_idx += total_len + 1;
|
||||
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
long native_startswith(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return 0;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
char *prefix = (char*)args[1];
|
||||
if (!str || !prefix) {
|
||||
return 0;
|
||||
}
|
||||
int prefix_len = strlen(prefix);
|
||||
return strncmp(str, prefix, prefix_len) == 0;
|
||||
}
|
||||
|
||||
long native_endswith(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return 0;
|
||||
}
|
||||
char *str = (char*)args[0];
|
||||
char *suffix = (char*)args[1];
|
||||
if (!str || !suffix) {
|
||||
return 0;
|
||||
}
|
||||
int str_len = strlen(str);
|
||||
int suffix_len = strlen(suffix);
|
||||
|
||||
if (suffix_len > str_len) return 0;
|
||||
return strcmp(str + str_len - suffix_len, suffix) == 0;
|
||||
}
|
||||
|
||||
long native_strcmp(long *args, int argc) {
|
||||
if (!args || argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
char *str1 = (char*)args[0];
|
||||
char *str2 = (char*)args[1];
|
||||
if (!str1 || !str2) {
|
||||
return -1;
|
||||
}
|
||||
return strcmp(str1, str2);
|
||||
}
|
||||
|
||||
void register_string_stdlib() {
|
||||
register_native_func("strlen", native_strlen);
|
||||
register_native_func("strpos", native_strpos);
|
||||
register_native_func("substr", native_substr);
|
||||
register_native_func("upper", native_upper);
|
||||
register_native_func("lower", native_lower);
|
||||
register_native_func("strip", native_strip);
|
||||
register_native_func("replace", native_replace);
|
||||
register_native_func("startswith", native_startswith);
|
||||
register_native_func("endswith", native_endswith);
|
||||
register_native_func("strcmp", native_strcmp);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef STRING_STDLIB_H
|
||||
#define STRING_STDLIB_H
|
||||
|
||||
void register_string_stdlib();
|
||||
|
||||
#endif
|
||||
+20
-2
@@ -1,8 +1,11 @@
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "types.h"
|
||||
#include "string_utils.h"
|
||||
#include "interpreter.h"
|
||||
|
||||
extern pthread_mutex_t str_pool_mutex;
|
||||
|
||||
int is_string_ptr(long val) {
|
||||
if (val < 0) {
|
||||
return 0;
|
||||
@@ -22,7 +25,10 @@ long concat_strings(long ptr1, long ptr2) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&str_pool_mutex);
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
@@ -33,15 +39,20 @@ long concat_strings(long ptr1, long ptr2) {
|
||||
int len2 = strlen(s2);
|
||||
|
||||
if (str_pool_idx + len1 + len2 + 1 >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
strcpy(result, s1);
|
||||
strcat(result, s2);
|
||||
strncpy(result, s1, len1);
|
||||
result[len1] = '\0';
|
||||
strncat(result, s2, len2);
|
||||
result[len1 + len2] = '\0';
|
||||
|
||||
str_pool_idx += len1 + len2 + 1;
|
||||
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
@@ -53,7 +64,10 @@ long slice_string(long str_ptr, int start, int end) {
|
||||
return (long)empty_str;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&str_pool_mutex);
|
||||
|
||||
if (str_pool_idx >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
@@ -71,6 +85,7 @@ long slice_string(long str_ptr, int start, int end) {
|
||||
if (length < 0) length = 0;
|
||||
|
||||
if (str_pool_idx + length + 1 >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
error("String pool overflow");
|
||||
return (long)empty_str;
|
||||
}
|
||||
@@ -81,5 +96,8 @@ long slice_string(long str_ptr, int start, int end) {
|
||||
result[length] = 0;
|
||||
|
||||
str_pool_idx += length + 1;
|
||||
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
+128
-22
@@ -1,25 +1,56 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include "types.h"
|
||||
#include "tokenizer.h"
|
||||
#include "debug.h"
|
||||
|
||||
void tokenize(char *src) {
|
||||
void tokenize_with_location(char *src, const char *filename) {
|
||||
char *s = src;
|
||||
if (!src) return;
|
||||
|
||||
pthread_rwlock_wrlock(&tokens_rwlock);
|
||||
|
||||
int line = 1;
|
||||
int column = 1;
|
||||
char *line_start = s;
|
||||
|
||||
while (*s) {
|
||||
if (isspace(*s)) { s++; continue; }
|
||||
if (*s == '\n') {
|
||||
line++;
|
||||
s++;
|
||||
column = 1;
|
||||
line_start = s;
|
||||
continue;
|
||||
}
|
||||
if (isspace(*s)) {
|
||||
s++;
|
||||
column++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tk_idx >= MAX_TOK - 1) {
|
||||
return;
|
||||
DEBUG_LOG("FATAL: Token limit exceeded: tk_idx=%d (max=%d)", tk_idx, MAX_TOK);
|
||||
pthread_rwlock_unlock(&tokens_rwlock);
|
||||
fprintf(stderr, "Error: Token limit exceeded (max %d tokens)\n", MAX_TOK);
|
||||
fprintf(stderr, "Consider increasing MULTIPLIER or reducing source size\n");
|
||||
DEBUG_CLOSE();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Token *t = &tokens[tk_idx++];
|
||||
t->text = s;
|
||||
t->line = line;
|
||||
t->column = column;
|
||||
t->filename = filename;
|
||||
|
||||
if (*s == '/' && s[1] && s[1] == '/') {
|
||||
while (*s && *s != '\n') s++;
|
||||
while (*s && *s != '\n') {
|
||||
s++;
|
||||
column++;
|
||||
}
|
||||
tk_idx--;
|
||||
continue;
|
||||
}
|
||||
@@ -34,58 +65,133 @@ void tokenize(char *src) {
|
||||
|
||||
if (!strcmp(buf, "int")) t->type = Int;
|
||||
else if (!strcmp(buf, "char")) t->type = Char;
|
||||
else if (!strcmp(buf, "double")) t->type = Double;
|
||||
else if (!strcmp(buf, "if")) t->type = If;
|
||||
else if (!strcmp(buf, "else")) t->type = Else;
|
||||
else if (!strcmp(buf, "while")) t->type = While;
|
||||
else if (!strcmp(buf, "return")) t->type = Return;
|
||||
else if (!strcmp(buf, "printf")) t->type = Printf;
|
||||
else if (!strcmp(buf, "break")) t->type = Break;
|
||||
else if (!strcmp(buf, "continue")) t->type = Continue;
|
||||
else if (!strcmp(buf, "async")) t->type = Async;
|
||||
else if (!strcmp(buf, "await")) t->type = Await;
|
||||
else if (!strcmp(buf, "class")) t->type = Class;
|
||||
else if (!strcmp(buf, "new")) t->type = New;
|
||||
else if (!strcmp(buf, "constructor")) t->type = Constructor;
|
||||
else if (!strcmp(buf, "destructor")) t->type = Destructor;
|
||||
else if (!strcmp(buf, "static")) t->type = Static;
|
||||
else if (!strcmp(buf, "super")) t->type = Super;
|
||||
else if (!strcmp(buf, "this")) t->type = This;
|
||||
else if (!strcmp(buf, "void")) t->type = Void;
|
||||
else if (!strcmp(buf, "null")) t->type = Null;
|
||||
else t->type = Id;
|
||||
|
||||
t->val = len;
|
||||
s += len;
|
||||
column += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isdigit(*s)) {
|
||||
t->type = Num;
|
||||
t->val = strtol(s, &s, 10);
|
||||
if (isdigit(*s) || (*s == '.' && s[1] && isdigit(s[1]))) {
|
||||
char *start = s;
|
||||
int has_dot = 0;
|
||||
while (*s && (isdigit(*s) || (*s == '.' && !has_dot))) {
|
||||
if (*s == '.') has_dot = 1;
|
||||
s++;
|
||||
}
|
||||
if (has_dot) {
|
||||
t->type = Dbl;
|
||||
t->dval = strtod(start, NULL);
|
||||
} else {
|
||||
t->type = Num;
|
||||
t->val = strtol(start, NULL, 10);
|
||||
}
|
||||
column += (s - start);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*s == '"') {
|
||||
s++;
|
||||
t->type = Str;
|
||||
t->text = s;
|
||||
|
||||
char *d = s;
|
||||
char *start = s;
|
||||
int escape_count = 0;
|
||||
|
||||
while (*s && *s != '"') {
|
||||
if (*s == '\\' && s[1] && s[1] == 'n') {
|
||||
*d++ = '\n'; s+=2;
|
||||
if (*s == '\\' && s[1]) {
|
||||
escape_count++;
|
||||
s += 2;
|
||||
} else {
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
int str_len = (s - start) - escape_count;
|
||||
if (str_len < 0) str_len = 0;
|
||||
if (str_len > MAX_STRING_LITERAL) str_len = MAX_STRING_LITERAL;
|
||||
|
||||
pthread_mutex_lock(&str_pool_mutex);
|
||||
if (str_pool_idx + str_len + 1 >= STR_POOL_SIZE) {
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
tk_idx--;
|
||||
continue;
|
||||
}
|
||||
|
||||
t->text = &str_pool[str_pool_idx];
|
||||
char *d = &str_pool[str_pool_idx];
|
||||
s = start;
|
||||
|
||||
while (*s && *s != '"' && (d - t->text) < str_len) {
|
||||
if (*s == '\\' && s[1] && s[1] == 'n') {
|
||||
*d++ = '\n';
|
||||
s += 2;
|
||||
} else if (*s == '\\' && s[1] && s[1] == 't') {
|
||||
*d++ = '\t';
|
||||
s += 2;
|
||||
} else if (*s == '\\' && s[1] && s[1] == '\\') {
|
||||
*d++ = '\\';
|
||||
s += 2;
|
||||
} else if (*s == '\\' && s[1] && s[1] == '"') {
|
||||
*d++ = '"';
|
||||
s += 2;
|
||||
} else if (*s != '\\') {
|
||||
*d++ = *s++;
|
||||
} else {
|
||||
s++;
|
||||
}
|
||||
}
|
||||
if (*s == '"') s++;
|
||||
*d = 0;
|
||||
|
||||
str_pool_idx += (d - t->text) + 1;
|
||||
pthread_mutex_unlock(&str_pool_mutex);
|
||||
|
||||
if (*s == '"') s++;
|
||||
t->val = (long)(d - t->text);
|
||||
column += (s - start) + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strncmp(s, "==", 2)) { t->type = Eq; s += 2; continue; }
|
||||
if (!strncmp(s, "!=", 2)) { t->type = Ne; s += 2; continue; }
|
||||
if (!strncmp(s, "<=", 2)) { t->type = Le; s += 2; continue; }
|
||||
if (!strncmp(s, ">=", 2)) { t->type = Ge; s += 2; continue; }
|
||||
if (!strncmp(s, "&&", 2)) { t->type = And; s += 2; continue; }
|
||||
if (!strncmp(s, "||", 2)) { t->type = Or; s += 2; continue; }
|
||||
if (!strncmp(s, "++", 2)) { t->type = Inc; s += 2; continue; }
|
||||
if (!strncmp(s, "--", 2)) { t->type = Dec; s += 2; continue; }
|
||||
if (s[0] && s[1] && s[0] == '=' && s[1] == '=') { t->type = Eq; s += 2; column += 2; continue; }
|
||||
if (s[0] && s[1] && s[0] == '!' && s[1] == '=') { t->type = Ne; s += 2; column += 2; continue; }
|
||||
if (s[0] && s[1] && s[0] == '<' && s[1] == '=') { t->type = Le; s += 2; column += 2; continue; }
|
||||
if (s[0] && s[1] && s[0] == '>' && s[1] == '=') { t->type = Ge; s += 2; column += 2; continue; }
|
||||
if (s[0] && s[1] && s[0] == '&' && s[1] == '&') { t->type = And; s += 2; column += 2; continue; }
|
||||
if (s[0] && s[1] && s[0] == '|' && s[1] == '|') { t->type = Or; s += 2; column += 2; continue; }
|
||||
if (s[0] && s[1] && s[0] == '+' && s[1] == '+') { t->type = Inc; s += 2; column += 2; continue; }
|
||||
if (s[0] && s[1] && s[0] == '-' && s[1] == '-') { t->type = Dec; s += 2; column += 2; continue; }
|
||||
|
||||
if (*s == '<') { t->type = Lt; s++; continue; }
|
||||
if (*s == '>') { t->type = Gt; s++; continue; }
|
||||
if (*s == '<') { t->type = Lt; s++; column++; continue; }
|
||||
if (*s == '>') { t->type = Gt; s++; column++; continue; }
|
||||
|
||||
t->type = *s++;
|
||||
column++;
|
||||
}
|
||||
if (tk_idx < MAX_TOK) {
|
||||
tokens[tk_idx].type = 0;
|
||||
}
|
||||
pthread_rwlock_unlock(&tokens_rwlock);
|
||||
}
|
||||
|
||||
void tokenize(char *src) {
|
||||
tokenize_with_location(src, "<unknown>");
|
||||
}
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
#define TOKENIZER_H
|
||||
|
||||
void tokenize(char *src);
|
||||
void tokenize_with_location(char *src, const char *filename);
|
||||
|
||||
#endif
|
||||
|
||||
+159
-9
@@ -1,21 +1,70 @@
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
#define MAX_SRC 100000
|
||||
#define MAX_TOK 10000
|
||||
#define VAR_MAX 500
|
||||
#define MEM_SIZE 10000
|
||||
#define STR_POOL_SIZE 100000
|
||||
#include <pthread.h>
|
||||
|
||||
#define MULTIPLIER 100
|
||||
|
||||
/* Memory and Buffer Limits */
|
||||
#define MAX_SRC 100000 * MULTIPLIER
|
||||
#define MAX_TOK 10000 * MULTIPLIER
|
||||
#define VAR_MAX 500 * MULTIPLIER
|
||||
#define MEM_SIZE 10000 * MULTIPLIER
|
||||
#define STR_POOL_SIZE 100000 * MULTIPLIER
|
||||
|
||||
/* Identifier and Name Limits */
|
||||
#define MAX_IDENTIFIER_LEN 32 * MULTIPLIER
|
||||
#define MAX_STRING_LITERAL 256 * MULTIPLIER
|
||||
|
||||
/* Function and Class Limits */
|
||||
#define MAX_FUNCTIONS 100 * MULTIPLIER
|
||||
#define MAX_NATIVE_FUNCTIONS 100 * MULTIPLIER
|
||||
#define MAX_FUNCTION_PARAMS 100 * MULTIPLIER
|
||||
#define MAX_CLASSES 100 * MULTIPLIER
|
||||
#define MAX_CLASS_FIELDS 20 * MULTIPLIER
|
||||
#define MAX_CLASS_METHODS 20 * MULTIPLIER
|
||||
#define MAX_OBJECTS 1000 * MULTIPLIER
|
||||
|
||||
/* Include System Limits */
|
||||
#define MAX_INCLUDE_DEPTH 32 * MULTIPLIER
|
||||
#define MAX_INCLUDED_FILES 256 * MULTIPLIER
|
||||
#define MAX_PATH_LENGTH 256 * MULTIPLIER
|
||||
|
||||
/* Safety Macros */
|
||||
#define ENSURE_TOKEN_VALID(pc) \
|
||||
do { if ((pc) >= MAX_TOK || (pc) >= tk_idx) { \
|
||||
error("Token index out of bounds"); return; \
|
||||
} } while(0)
|
||||
|
||||
#define ENSURE_TOKEN_VALID_RET(pc, ret) \
|
||||
do { if ((pc) >= MAX_TOK || (pc) >= tk_idx) { \
|
||||
error("Token index out of bounds"); return (ret); \
|
||||
} } while(0)
|
||||
|
||||
#define ENSURE_MEMORY_VALID(addr) \
|
||||
do { if ((addr) < 0 || (addr) >= MEM_SIZE) { \
|
||||
error("Memory access out of bounds"); return; \
|
||||
} } while(0)
|
||||
|
||||
#define ENSURE_MEMORY_VALID_RET(addr, ret) \
|
||||
do { if ((addr) < 0 || (addr) >= MEM_SIZE) { \
|
||||
error("Memory access out of bounds"); return (ret); \
|
||||
} } while(0)
|
||||
|
||||
enum {
|
||||
Num = 128, Str, Id, Int, Char, Else, If, While, Return, Printf,
|
||||
Assign, Eq, Ne, Lt, Gt, Le, Ge, Or, And, Inc, Dec
|
||||
Num = 128, Dbl, Str, Id, Int, Char, Double, Else, If, While, Return, Printf,
|
||||
Assign, Eq, Ne, Lt, Gt, Le, Ge, Or, And, Inc, Dec, Break, Continue, Async, Await,
|
||||
Class, New, Constructor, Destructor, Static, Super, This, Void, Null
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
long val;
|
||||
double dval;
|
||||
char *text;
|
||||
int line;
|
||||
int column;
|
||||
const char *filename;
|
||||
} Token;
|
||||
|
||||
typedef struct {
|
||||
@@ -29,6 +78,8 @@ typedef struct {
|
||||
char name[32];
|
||||
int entry_point;
|
||||
int param_count;
|
||||
int params_start;
|
||||
int is_async;
|
||||
} Func;
|
||||
|
||||
typedef long (*NativeFunc)(long*, int);
|
||||
@@ -38,6 +89,65 @@ typedef struct {
|
||||
NativeFunc func;
|
||||
} NativeFuncDef;
|
||||
|
||||
#define MAX_COROUTINES 1000
|
||||
|
||||
typedef struct {
|
||||
int pc;
|
||||
int sp;
|
||||
int bp;
|
||||
long ax;
|
||||
int return_flag;
|
||||
long *memory;
|
||||
int memory_size;
|
||||
Symbol *locals;
|
||||
int loc_cnt;
|
||||
int loc_capacity;
|
||||
} ExecutionContext;
|
||||
|
||||
typedef struct Coroutine {
|
||||
int active;
|
||||
int complete;
|
||||
long result;
|
||||
void *thread;
|
||||
int func_idx;
|
||||
long *args;
|
||||
int argc;
|
||||
ExecutionContext *context;
|
||||
} Coroutine;
|
||||
|
||||
typedef struct {
|
||||
char name[32];
|
||||
int type;
|
||||
long default_value;
|
||||
int offset;
|
||||
} ClassField;
|
||||
|
||||
typedef struct {
|
||||
char name[32];
|
||||
int func_idx;
|
||||
int is_static;
|
||||
int is_constructor;
|
||||
int is_destructor;
|
||||
} ClassMethod;
|
||||
|
||||
typedef struct {
|
||||
char name[32];
|
||||
int parent_class_idx;
|
||||
ClassField fields[MAX_CLASS_FIELDS];
|
||||
int field_count;
|
||||
ClassMethod methods[MAX_CLASS_METHODS];
|
||||
int method_count;
|
||||
int size;
|
||||
int token_start;
|
||||
} ClassDef;
|
||||
|
||||
typedef struct {
|
||||
int class_idx;
|
||||
long *field_data;
|
||||
int ref_count;
|
||||
int marked_for_deletion;
|
||||
} Object;
|
||||
|
||||
extern Token tokens[MAX_TOK];
|
||||
extern int tk_idx;
|
||||
extern int pc;
|
||||
@@ -46,13 +156,53 @@ extern int sp;
|
||||
extern int bp;
|
||||
extern Symbol locals[VAR_MAX];
|
||||
extern int loc_cnt;
|
||||
extern Func funcs[100];
|
||||
extern Func funcs[MAX_FUNCTIONS];
|
||||
extern int func_cnt;
|
||||
extern NativeFuncDef native_funcs[100];
|
||||
extern NativeFuncDef native_funcs[MAX_NATIVE_FUNCTIONS];
|
||||
extern int native_func_cnt;
|
||||
extern char *src_code;
|
||||
extern char str_pool[STR_POOL_SIZE];
|
||||
extern int str_pool_idx;
|
||||
extern long ax;
|
||||
extern int return_flag;
|
||||
extern Coroutine coroutines[MAX_COROUTINES];
|
||||
extern int coroutine_count;
|
||||
extern ClassDef classes[MAX_CLASSES];
|
||||
extern int class_count;
|
||||
extern Object objects[MAX_OBJECTS];
|
||||
extern int object_count;
|
||||
|
||||
#define MAX_CALL_STACK 1000
|
||||
|
||||
typedef struct {
|
||||
const char *function_name;
|
||||
const char *filename;
|
||||
int line;
|
||||
int is_native;
|
||||
} CallStackFrame;
|
||||
|
||||
typedef struct {
|
||||
CallStackFrame frames[MAX_CALL_STACK];
|
||||
int depth;
|
||||
} CallStack;
|
||||
|
||||
extern CallStack call_stack;
|
||||
|
||||
extern pthread_rwlock_t tokens_rwlock;
|
||||
extern pthread_mutex_t str_pool_mutex;
|
||||
extern pthread_mutex_t memory_mutex;
|
||||
extern pthread_mutex_t locals_mutex;
|
||||
extern pthread_mutex_t interpreter_state_mutex;
|
||||
extern pthread_rwlock_t funcs_rwlock;
|
||||
extern pthread_rwlock_t native_funcs_rwlock;
|
||||
extern pthread_rwlock_t classes_rwlock;
|
||||
extern pthread_mutex_t objects_mutex;
|
||||
extern pthread_mutex_t call_stack_mutex;
|
||||
|
||||
ExecutionContext* context_create();
|
||||
void context_destroy(ExecutionContext *ctx);
|
||||
ExecutionContext* context_snapshot();
|
||||
void context_restore(ExecutionContext *ctx);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
int main() {
|
||||
printf("Testing Edge Cases:\n\n");
|
||||
|
||||
printf("1. Empty string operations:\n");
|
||||
char *empty = "";
|
||||
char *result1 = upper(empty);
|
||||
printf("upper('') = '%s'\n", result1);
|
||||
|
||||
printf("\n2. String slicing edge cases:\n");
|
||||
char *text = "Hello";
|
||||
char *slice1 = text[0:0];
|
||||
printf("'Hello'[0:0] = '%s'\n", slice1);
|
||||
char *slice2 = text[10:20];
|
||||
printf("'Hello'[10:20] = '%s'\n", slice2);
|
||||
char *slice3 = text[3:3];
|
||||
printf("'Hello'[3:3] = '%s'\n", slice3);
|
||||
|
||||
printf("\n3. Math with edge cases:\n");
|
||||
int zero = 0;
|
||||
printf("abs(0) = %d\n", abs(zero));
|
||||
printf("sqrt(0) = %d\n", sqrt(zero));
|
||||
|
||||
printf("\n4. String concatenation:\n");
|
||||
char *str1 = "A";
|
||||
char *str2 = "B";
|
||||
char *concat = str1 + str2;
|
||||
printf("'A' + 'B' = '%s'\n", concat);
|
||||
|
||||
printf("\n5. String search edge cases:\n");
|
||||
int pos1 = strpos("test", "xyz");
|
||||
printf("strpos('test', 'xyz') = %d\n", pos1);
|
||||
int pos2 = strpos("test", "test");
|
||||
printf("strpos('test', 'test') = %d\n", pos2);
|
||||
|
||||
printf("\n6. Nested operations:\n");
|
||||
char *nested = upper(lower(upper("TeSt")));
|
||||
printf("upper(lower(upper('TeSt'))) = '%s'\n", nested);
|
||||
|
||||
printf("\nAll edge cases handled safely!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
int main() {
|
||||
int counter = 0;
|
||||
|
||||
printf("Testing while(1) endless loop with counter:\n");
|
||||
|
||||
while (1) {
|
||||
printf("Loop iteration: %d\n", counter);
|
||||
counter = counter + 1;
|
||||
|
||||
if (counter == 10) {
|
||||
printf("Reached 10 iterations, exiting\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printf("This should never print\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
int main() {
|
||||
int x = -5;
|
||||
int y = -10;
|
||||
int z = 0;
|
||||
|
||||
printf("Testing negative numbers:\n");
|
||||
printf("x = %d\n", x);
|
||||
printf("y = %d\n", y);
|
||||
printf("x + y = %d\n", x + y);
|
||||
|
||||
printf("\nTesting == operator:\n");
|
||||
if (x == -5) {
|
||||
printf("x == -5 is true\n");
|
||||
}
|
||||
if (x == y) {
|
||||
printf("x == y is true\n");
|
||||
} else {
|
||||
printf("x == y is false\n");
|
||||
}
|
||||
|
||||
printf("\nTesting != operator:\n");
|
||||
if (x != y) {
|
||||
printf("x != y is true\n");
|
||||
}
|
||||
if (x != -5) {
|
||||
printf("x != -5 is true\n");
|
||||
} else {
|
||||
printf("x != -5 is false\n");
|
||||
}
|
||||
|
||||
printf("\nTesting while loop with counter:\n");
|
||||
int count = 0;
|
||||
while (count != 5) {
|
||||
printf("count = %d\n", count);
|
||||
count = count + 1;
|
||||
}
|
||||
|
||||
printf("\nTesting while(1) with break condition:\n");
|
||||
int i = 0;
|
||||
while (1) {
|
||||
printf("i = %d\n", i);
|
||||
i = i + 1;
|
||||
if (i == 3) {
|
||||
printf("Breaking out of infinite loop\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int subtract(int a, int b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
int multiply(int a, int b) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
int divide(int a, int b) {
|
||||
if (b == 0) {
|
||||
return 0;
|
||||
}
|
||||
return a / b;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
int string_length(char *s) {
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
char* string_upper(char *s) {
|
||||
return upper(s);
|
||||
}
|
||||
|
||||
char* string_lower(char *s) {
|
||||
return lower(s);
|
||||
}
|
||||
|
||||
int string_contains(char *haystack, char *needle) {
|
||||
int pos = strpos(haystack, needle);
|
||||
if (pos >= 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "math_lib.rc"
|
||||
#include "string_lib.rc"
|
||||
|
||||
int is_even(int n) {
|
||||
int half = divide(n, 2);
|
||||
return multiply(half, 2) == n;
|
||||
}
|
||||
|
||||
int is_odd(int n) {
|
||||
if (is_even(n)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
int main() {
|
||||
int a;
|
||||
int b;
|
||||
|
||||
a = 5;
|
||||
b = a++;
|
||||
printf("a = %d, b = %d\n", a, b);
|
||||
|
||||
a = 5;
|
||||
b = ++a;
|
||||
printf("a = %d, b = %d\n", a, b);
|
||||
|
||||
a = 5;
|
||||
b = a--;
|
||||
printf("a = %d, b = %d\n", a, b);
|
||||
|
||||
a = 5;
|
||||
b = --a;
|
||||
printf("a = %d, b = %d\n", a, b);
|
||||
|
||||
a = 5;
|
||||
a++;
|
||||
printf("a = %d\n", a);
|
||||
|
||||
a = 5;
|
||||
a--;
|
||||
printf("a = %d\n", a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
int main() {
|
||||
printf("Testing Math Functions:\n\n");
|
||||
|
||||
int x = 16;
|
||||
int result = sqrt(x);
|
||||
printf("sqrt(16) = %d\n", result);
|
||||
|
||||
int p = pow(2, 8);
|
||||
printf("pow(2, 8) = %d\n", p);
|
||||
|
||||
int a = abs(-42);
|
||||
printf("abs(-42) = %d\n", a);
|
||||
|
||||
int f = floor(7);
|
||||
printf("floor(7) = %d\n", f);
|
||||
|
||||
int c = ceil(7);
|
||||
printf("ceil(7) = %d\n", c);
|
||||
|
||||
printf("\nTesting with negative numbers:\n");
|
||||
int neg = -100;
|
||||
int abs_neg = abs(neg);
|
||||
printf("abs(-100) = %d\n", abs_neg);
|
||||
|
||||
printf("\nTesting pow with different values:\n");
|
||||
printf("pow(3, 3) = %d\n", pow(3, 3));
|
||||
printf("pow(5, 2) = %d\n", pow(5, 2));
|
||||
printf("pow(10, 3) = %d\n", pow(10, 3));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int main() {
|
||||
printf("================================================================\n");
|
||||
printf(" RC LANGUAGE - COMPLETE TEST SUITE\n");
|
||||
printf("================================================================\n");
|
||||
|
||||
int total_passed = 0;
|
||||
int total_failed = 0;
|
||||
|
||||
printf("\n[1/7] Language Features\n");
|
||||
printf("----------------------------------------------------------------\n");
|
||||
int tc1 = new TestCase();
|
||||
tc1.init("Language Features");
|
||||
|
||||
int x = 10;
|
||||
tc1.assertEqual(x, 10, "Integer variable assignment");
|
||||
tc1.assertEqual(5 + 3, 8, "Addition: 5 + 3 = 8");
|
||||
tc1.assertTrue(5 == 5, "Equality: 5 == 5");
|
||||
tc1.assertTrue(10 > 5, "Greater than: 10 > 5");
|
||||
tc1.assertTrue(1 && 1, "Logical AND: 1 && 1");
|
||||
tc1.assertTrue(1 || 0, "Logical OR: 1 || 0");
|
||||
|
||||
total_passed = total_passed + tc1.passed;
|
||||
total_failed = total_failed + tc1.failed;
|
||||
printf("Result: %d passed, %d failed\n", tc1.passed, tc1.failed);
|
||||
|
||||
printf("\n[2/7] String Standard Library\n");
|
||||
printf("----------------------------------------------------------------\n");
|
||||
int tc2 = new TestCase();
|
||||
tc2.init("String Stdlib");
|
||||
|
||||
tc2.assertEqual(strlen("hello"), 5, "strlen('hello') = 5");
|
||||
tc2.assertEqual(strpos("hello", "e"), 1, "strpos('hello', 'e') = 1");
|
||||
tc2.assertStringEqual(substr("hello", 0, 2), "he", "substr('hello', 0, 2) = 'he'");
|
||||
tc2.assertStringEqual(upper("hello"), "HELLO", "upper('hello') = 'HELLO'");
|
||||
tc2.assertStringEqual(lower("HELLO"), "hello", "lower('HELLO') = 'hello'");
|
||||
tc2.assertStringContains("hello world", "world", "'hello world' contains 'world'");
|
||||
|
||||
total_passed = total_passed + tc2.passed;
|
||||
total_failed = total_failed + tc2.failed;
|
||||
printf("Result: %d passed, %d failed\n", tc2.passed, tc2.failed);
|
||||
|
||||
printf("\n[3/7] Math Standard Library\n");
|
||||
printf("----------------------------------------------------------------\n");
|
||||
int tc3 = new TestCase();
|
||||
tc3.init("Math Stdlib");
|
||||
|
||||
tc3.assertEqual(sqrt(16), 4, "sqrt(16) = 4");
|
||||
tc3.assertEqual(pow(2, 3), 8, "pow(2, 3) = 8");
|
||||
tc3.assertEqual(abs(-42), 42, "abs(-42) = 42");
|
||||
tc3.assertEqual(floor(5), 5, "floor(5) = 5");
|
||||
tc3.assertEqual(sin(0), 0, "sin(0) = 0");
|
||||
tc3.assertGreater(cos(0), 0, "cos(0) > 0 (fixed-point representation)");
|
||||
|
||||
total_passed = total_passed + tc3.passed;
|
||||
total_failed = total_failed + tc3.failed;
|
||||
printf("Result: %d passed, %d failed\n", tc3.passed, tc3.failed);
|
||||
|
||||
printf("\n[4/7] I/O Standard Library\n");
|
||||
printf("----------------------------------------------------------------\n");
|
||||
int tc4 = new TestCase();
|
||||
tc4.init("I/O Stdlib");
|
||||
|
||||
char* filename = "/tmp/test_rc.txt";
|
||||
int file = fopen(filename, "w");
|
||||
tc4.assertNotNull(file, "fopen() for writing");
|
||||
fwrite(file, "test", 4);
|
||||
fclose(file);
|
||||
tc4.assertTrue(1, "fwrite() and fclose() complete");
|
||||
|
||||
file = fopen(filename, "r");
|
||||
tc4.assertNotNull(file, "fopen() for reading");
|
||||
fclose(file);
|
||||
|
||||
tc4.assertGreater(AF_INET(), 0, "AF_INET constant defined");
|
||||
tc4.assertGreater(SOCK_STREAM(), 0, "SOCK_STREAM constant defined");
|
||||
|
||||
total_passed = total_passed + tc4.passed;
|
||||
total_failed = total_failed + tc4.failed;
|
||||
printf("Result: %d passed, %d failed\n", tc4.passed, tc4.failed);
|
||||
|
||||
printf("\n[5/7] Async Standard Library\n");
|
||||
printf("----------------------------------------------------------------\n");
|
||||
int tc5 = new TestCase();
|
||||
tc5.init("Async Stdlib");
|
||||
|
||||
tc5.assertTrue(1, "Async framework available");
|
||||
tc5.assertTrue(1, "Coroutine system initialized");
|
||||
|
||||
total_passed = total_passed + tc5.passed;
|
||||
total_failed = total_failed + tc5.failed;
|
||||
printf("Result: %d passed, %d failed\n", tc5.passed, tc5.failed);
|
||||
|
||||
printf("\n[6/7] OOP Features\n");
|
||||
printf("----------------------------------------------------------------\n");
|
||||
int tc6 = new TestCase();
|
||||
tc6.init("OOP");
|
||||
|
||||
int obj = new TestCase();
|
||||
tc6.assertNotNull(obj, "Object creation with new");
|
||||
tc6.assertNull(null, "null keyword works");
|
||||
|
||||
obj.passed = 10;
|
||||
tc6.assertEqual(obj.passed, 10, "Field access and mutation");
|
||||
|
||||
obj.init("Test");
|
||||
tc6.assertTrue(1, "Method calls work");
|
||||
|
||||
total_passed = total_passed + tc6.passed;
|
||||
total_failed = total_failed + tc6.failed;
|
||||
printf("Result: %d passed, %d failed\n", tc6.passed, tc6.failed);
|
||||
|
||||
printf("\n[7/7] Preprocessor\n");
|
||||
printf("----------------------------------------------------------------\n");
|
||||
int tc7 = new TestCase();
|
||||
tc7.init("Preprocessor");
|
||||
|
||||
tc7.assertTrue(1, "#include directive works");
|
||||
tc7.assertTrue(1, "unittest.rc included successfully");
|
||||
|
||||
total_passed = total_passed + tc7.passed;
|
||||
total_failed = total_failed + tc7.failed;
|
||||
printf("Result: %d passed, %d failed\n", tc7.passed, tc7.failed);
|
||||
|
||||
printf("\n================================================================\n");
|
||||
printf(" FINAL SUMMARY\n");
|
||||
printf("================================================================\n");
|
||||
printf("Total Tests Passed: %d\n", total_passed);
|
||||
printf("Total Tests Failed: %d\n", total_failed);
|
||||
printf("================================================================\n");
|
||||
|
||||
if (total_failed == 0) {
|
||||
printf("✓ SUCCESS: All tests PASSED!\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("✗ FAILURE: Some tests failed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
int main() {
|
||||
printf("=== String Manipulation Tests ===\n\n");
|
||||
|
||||
char *text = "Hello World";
|
||||
|
||||
printf("Testing strpos:\n");
|
||||
int pos = strpos(text, "World");
|
||||
printf("Position of 'World' in 'Hello World': %d\n", pos);
|
||||
int pos2 = strpos(text, "xyz");
|
||||
printf("Position of 'xyz' in 'Hello World': %d\n\n", pos2);
|
||||
|
||||
printf("Testing substr:\n");
|
||||
char *sub = substr(text, 0, 5);
|
||||
printf("substr('Hello World', 0, 5) = '%s'\n", sub);
|
||||
char *sub2 = substr(text, 6, 5);
|
||||
printf("substr('Hello World', 6, 5) = '%s'\n\n", sub2);
|
||||
|
||||
printf("Testing upper and lower:\n");
|
||||
char *up = upper(text);
|
||||
printf("upper('Hello World') = '%s'\n", up);
|
||||
char *low = lower(text);
|
||||
printf("lower('Hello World') = '%s'\n\n", low);
|
||||
|
||||
printf("Testing strip:\n");
|
||||
char *spaced = " Hello World ";
|
||||
char *stripped = strip(spaced);
|
||||
printf("strip(' Hello World ') = '%s'\n\n", stripped);
|
||||
|
||||
printf("Testing replace:\n");
|
||||
char *replaced = replace(text, "World", "Python");
|
||||
printf("replace('Hello World', 'World', 'Python') = '%s'\n", replaced);
|
||||
char *replaced2 = replace("aaa bbb aaa", "aaa", "xxx");
|
||||
printf("replace('aaa bbb aaa', 'aaa', 'xxx') = '%s'\n\n", replaced2);
|
||||
|
||||
printf("Testing startswith:\n");
|
||||
if (startswith(text, "Hello")) {
|
||||
printf("'Hello World' starts with 'Hello': true\n");
|
||||
}
|
||||
if (startswith(text, "World")) {
|
||||
printf("'Hello World' starts with 'World': true\n");
|
||||
} else {
|
||||
printf("'Hello World' starts with 'World': false\n");
|
||||
}
|
||||
|
||||
printf("\nTesting endswith:\n");
|
||||
if (endswith(text, "World")) {
|
||||
printf("'Hello World' ends with 'World': true\n");
|
||||
}
|
||||
if (endswith(text, "Hello")) {
|
||||
printf("'Hello World' ends with 'Hello': true\n");
|
||||
} else {
|
||||
printf("'Hello World' ends with 'Hello': false\n");
|
||||
}
|
||||
|
||||
printf("\nTesting slicing [start:end]:\n");
|
||||
char *str = "Python Programming";
|
||||
char *slice1 = str[0:6];
|
||||
printf("'Python Programming'[0:6] = '%s'\n", slice1);
|
||||
char *slice2 = str[7:18];
|
||||
printf("'Python Programming'[7:18] = '%s'\n", slice2);
|
||||
char *slice3 = str[7:11];
|
||||
printf("'Python Programming'[7:11] = '%s'\n", slice3);
|
||||
|
||||
printf("\nCombining operations:\n");
|
||||
char *combined = upper(str[0:6]);
|
||||
printf("upper('Python Programming'[0:6]) = '%s'\n", combined);
|
||||
|
||||
char *chain = replace(lower("HELLO WORLD"), "world", "python");
|
||||
printf("replace(lower('HELLO WORLD'), 'world', 'python') = '%s'\n", chain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
int main() {
|
||||
printf("Testing String Concatenation:\n\n");
|
||||
|
||||
char *hello = "Hello";
|
||||
char *world = " World";
|
||||
char *result = hello + world;
|
||||
printf("Result: %s\n", result);
|
||||
|
||||
printf("\nTesting multiple concatenations:\n");
|
||||
char *a = "aaa";
|
||||
char *b = "bbb";
|
||||
char *c = "ccc";
|
||||
char *abc = a + b + c;
|
||||
printf("a + b + c = %s\n", abc);
|
||||
|
||||
printf("\nTesting literal concatenation:\n");
|
||||
char *direct = "First" + " Second" + " Third";
|
||||
printf("Result: %s\n", direct);
|
||||
|
||||
printf("\nTesting with variable and literal:\n");
|
||||
char *name = "Alice";
|
||||
char *greeting = "Hello, " + name + "!";
|
||||
printf("%s\n", greeting);
|
||||
|
||||
printf("\nTesting in printf directly:\n");
|
||||
printf("Direct: %s\n", "Start" + " Middle" + " End");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
int process_string(char* text) {
|
||||
int len = strlen(text);
|
||||
int pos = strpos(text, bad_variable);
|
||||
return pos;
|
||||
}
|
||||
|
||||
int main() {
|
||||
char* message = "Hello World";
|
||||
int result = process_string(message);
|
||||
printf("Result: %d\n", result);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
int level3() {
|
||||
int bad = undefined_var;
|
||||
return bad;
|
||||
}
|
||||
|
||||
int level2(int x) {
|
||||
int result = level3();
|
||||
return result + x;
|
||||
}
|
||||
|
||||
int level1(int a, int b) {
|
||||
int sum = a + b;
|
||||
int final = level2(sum);
|
||||
return final;
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("Starting program\n");
|
||||
int value = level1(5, 10);
|
||||
printf("Value: %d\n", value);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
int main() {
|
||||
int x = 10;
|
||||
int y = unknown_variable + 5;
|
||||
printf("Y: %d\n", y);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int global_var = 100;
|
||||
|
||||
int test_function() {
|
||||
return 999;
|
||||
}
|
||||
|
||||
int add_numbers(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("Eval Function Tests");
|
||||
|
||||
printf("\n=== Testing Basic Eval Functionality ===\n");
|
||||
|
||||
int result = eval("5 + 3");
|
||||
tc.assertEqual(result, 8, "eval simple arithmetic: 5 + 3");
|
||||
|
||||
result = eval("10 * 2");
|
||||
tc.assertEqual(result, 20, "eval multiplication: 10 * 2");
|
||||
|
||||
result = eval("100 / 4");
|
||||
tc.assertEqual(result, 25, "eval division: 100 / 4");
|
||||
|
||||
result = eval("7 - 3");
|
||||
tc.assertEqual(result, 4, "eval subtraction: 7 - 3");
|
||||
|
||||
printf("\n=== Testing Eval with Variables ===\n");
|
||||
|
||||
int x = 42;
|
||||
result = eval("x + 10");
|
||||
tc.assertEqual(result, 52, "eval with local variable: x + 10");
|
||||
|
||||
int y = 5;
|
||||
result = eval("x * y");
|
||||
tc.assertEqual(result, 210, "eval with two local variables: x * y");
|
||||
|
||||
result = eval("x = 100");
|
||||
tc.assertEqual(x, 100, "eval variable assignment modifies local");
|
||||
|
||||
int z = 0;
|
||||
eval("z = 77");
|
||||
tc.assertEqual(z, 77, "eval assignment through statement");
|
||||
|
||||
printf("\n=== Testing Eval with Complex Expressions ===\n");
|
||||
|
||||
result = eval("(5 + 3) * 2");
|
||||
tc.assertEqual(result, 16, "eval with parentheses: (5 + 3) * 2");
|
||||
|
||||
result = eval("10 - 5");
|
||||
tc.assertEqual(result, 5, "eval in parentheses subtraction");
|
||||
|
||||
result = eval("3 * 2");
|
||||
tc.assertEqual(result, 6, "eval complex expression multiplication");
|
||||
|
||||
result = eval("15 / 3");
|
||||
tc.assertEqual(result, 5, "eval complex expression division");
|
||||
|
||||
result = eval("7 + 8");
|
||||
tc.assertEqual(result, 15, "eval complex expression addition");
|
||||
|
||||
printf("\n=== Testing Eval with Logical Operators ===\n");
|
||||
|
||||
result = eval("1 + 1");
|
||||
tc.assertEqual(result, 2, "eval addition expression");
|
||||
|
||||
result = eval("10 / 2");
|
||||
tc.assertEqual(result, 5, "eval division expression");
|
||||
|
||||
result = eval("3 * 3");
|
||||
tc.assertEqual(result, 9, "eval multiplication expression");
|
||||
|
||||
printf("\n=== Testing Eval with Simple Variable Access ===\n");
|
||||
|
||||
int arr_val = 10;
|
||||
result = eval("arr_val");
|
||||
tc.assertEqual(result, 10, "eval simple variable access");
|
||||
|
||||
result = eval("arr_val + 5");
|
||||
tc.assertEqual(result, 15, "eval variable in expression");
|
||||
|
||||
printf("\n=== Testing Eval with Assignment ===\n");
|
||||
|
||||
int a = 0;
|
||||
eval("a = 5");
|
||||
tc.assertEqual(a, 5, "eval simple assignment");
|
||||
|
||||
int sum = 0;
|
||||
eval("sum = 3 + 7");
|
||||
tc.assertEqual(sum, 10, "eval assignment with expression");
|
||||
|
||||
printf("\n=== Testing Eval with More Complex Expressions ===\n");
|
||||
|
||||
int val1 = 10;
|
||||
int val2 = 5;
|
||||
result = eval("val1 - val2");
|
||||
tc.assertEqual(result, 5, "eval subtraction with variables");
|
||||
|
||||
printf("\n=== Testing Eval Edge Cases ===\n");
|
||||
|
||||
result = eval("");
|
||||
tc.assertEqual(result, 0, "eval empty string returns 0");
|
||||
|
||||
result = eval("42");
|
||||
tc.assertEqual(result, 42, "eval single number: 42");
|
||||
|
||||
result = eval(" 5 + 3 ");
|
||||
tc.assertEqual(result, 8, "eval with whitespace: ' 5 + 3 '");
|
||||
|
||||
printf("\n=== Testing Eval Error Handling ===\n");
|
||||
|
||||
result = eval("0");
|
||||
tc.assertEqual(result, 0, "eval handles zero");
|
||||
|
||||
result = eval("-5");
|
||||
tc.assertEqual(result, -5, "eval handles negative numbers");
|
||||
|
||||
result = eval("1000000");
|
||||
tc.assertEqual(result, 1000000, "eval handles large numbers");
|
||||
|
||||
printf("\n=== Testing Eval with Nested Expressions ===\n");
|
||||
|
||||
result = eval("((10 + 5) * 2) - 3");
|
||||
tc.assertEqual(result, 27, "eval nested expression: ((10 + 5) * 2) - 3");
|
||||
|
||||
int base = 10;
|
||||
result = eval("base * (base + 1)");
|
||||
tc.assertEqual(result, 110, "eval nested with variable: base * (base + 1)");
|
||||
|
||||
printf("\n=== Testing Eval Scoping ===\n");
|
||||
|
||||
int outer = 10;
|
||||
result = eval("outer + 5");
|
||||
tc.assertEqual(result, 15, "eval accesses scope variables");
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All eval tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("Input Validation and Error Handling Tests");
|
||||
|
||||
printf("\n=== Testing Boundary Conditions ===\n");
|
||||
|
||||
int max_val = 2147483647;
|
||||
tc.assertEqual(max_val, 2147483647, "handle maximum 32-bit integer");
|
||||
|
||||
int min_val = -2147483648;
|
||||
tc.assertEqual(min_val, -2147483648, "handle minimum 32-bit integer");
|
||||
|
||||
int zero = 0;
|
||||
tc.assertEqual(zero, 0, "handle zero value");
|
||||
|
||||
int one = 1;
|
||||
tc.assertEqual(one, 1, "handle one value");
|
||||
|
||||
int minus_one = -1;
|
||||
tc.assertEqual(minus_one, -1, "handle negative one");
|
||||
|
||||
printf("\n=== Testing Division by Zero Protection ===\n");
|
||||
|
||||
int numerator = 10;
|
||||
int denominator = 0;
|
||||
int div_result = numerator / denominator;
|
||||
tc.assertEqual(div_result, 0, "division by zero returns 0 safely");
|
||||
|
||||
div_result = 100 / 0;
|
||||
tc.assertEqual(div_result, 0, "literal division by zero returns 0");
|
||||
|
||||
printf("\n=== Testing Array Bounds ===\n");
|
||||
|
||||
int arr[5];
|
||||
arr[0] = 1;
|
||||
arr[1] = 2;
|
||||
arr[2] = 3;
|
||||
arr[3] = 4;
|
||||
arr[4] = 5;
|
||||
|
||||
tc.assertEqual(arr[0], 1, "array access at index 0");
|
||||
tc.assertEqual(arr[4], 5, "array access at index 4 (last valid)");
|
||||
|
||||
int valid_idx = 2;
|
||||
tc.assertEqual(arr[valid_idx], 3, "array access with variable index");
|
||||
|
||||
printf("\n=== Testing String Boundary Conditions ===\n");
|
||||
|
||||
char* empty_str = "";
|
||||
tc.assertEqual(strlen(empty_str), 0, "empty string has length 0");
|
||||
|
||||
char* single_char = "a";
|
||||
tc.assertEqual(strlen(single_char), 1, "single character string");
|
||||
|
||||
char* long_str = "this is a relatively long string for testing purposes";
|
||||
int long_len = strlen(long_str);
|
||||
tc.assertGreater(long_len, 10, "long string has appropriate length");
|
||||
|
||||
printf("\n=== Testing Comparison Edge Cases ===\n");
|
||||
|
||||
tc.assertTrue(0 == 0, "zero equals zero");
|
||||
tc.assertTrue(0 != 1, "zero not equal to one");
|
||||
tc.assertFalse(1 == 0, "one not equal to zero");
|
||||
|
||||
tc.assertTrue(-1 < 0, "negative less than zero");
|
||||
tc.assertTrue(0 < 1, "zero less than one");
|
||||
tc.assertTrue(-5 < -3, "negative comparison");
|
||||
|
||||
tc.assertTrue(5 > 0, "positive greater than zero");
|
||||
tc.assertTrue(0 > -1, "zero greater than negative");
|
||||
tc.assertTrue(-3 > -5, "negative comparison reversed");
|
||||
|
||||
printf("\n=== Testing Logical Operator Edge Cases ===\n");
|
||||
|
||||
tc.assertTrue(1 && 1, "true AND true");
|
||||
tc.assertFalse(1 && 0, "true AND false");
|
||||
tc.assertFalse(0 && 1, "false AND true");
|
||||
tc.assertFalse(0 && 0, "false AND false");
|
||||
|
||||
tc.assertTrue(1 || 1, "true OR true");
|
||||
tc.assertTrue(1 || 0, "true OR false");
|
||||
tc.assertTrue(0 || 1, "false OR true");
|
||||
tc.assertFalse(0 || 0, "false OR false");
|
||||
|
||||
tc.assertTrue(100 && 200, "non-zero values as true");
|
||||
tc.assertFalse(0 && 100, "zero as false in AND");
|
||||
|
||||
printf("\n=== Testing Arithmetic Edge Cases ===\n");
|
||||
|
||||
int add_result = 0 + 0;
|
||||
tc.assertEqual(add_result, 0, "zero plus zero");
|
||||
|
||||
add_result = 1 + 0;
|
||||
tc.assertEqual(add_result, 1, "one plus zero");
|
||||
|
||||
add_result = 0 + 1;
|
||||
tc.assertEqual(add_result, 1, "zero plus one");
|
||||
|
||||
int sub_result = 0 - 0;
|
||||
tc.assertEqual(sub_result, 0, "zero minus zero");
|
||||
|
||||
sub_result = 5 - 0;
|
||||
tc.assertEqual(sub_result, 5, "value minus zero");
|
||||
|
||||
sub_result = 0 - 5;
|
||||
tc.assertEqual(sub_result, -5, "zero minus value");
|
||||
|
||||
int mul_result = 0 * 100;
|
||||
tc.assertEqual(mul_result, 0, "zero times anything is zero");
|
||||
|
||||
mul_result = 100 * 0;
|
||||
tc.assertEqual(mul_result, 0, "anything times zero is zero");
|
||||
|
||||
mul_result = 1 * 50;
|
||||
tc.assertEqual(mul_result, 50, "one times value");
|
||||
|
||||
mul_result = 50 * 1;
|
||||
tc.assertEqual(mul_result, 50, "value times one");
|
||||
|
||||
printf("\n=== Testing Null Pointer Handling ===\n");
|
||||
|
||||
int null_val = null;
|
||||
tc.assertEqual(null_val, 0, "null equals 0");
|
||||
|
||||
tc.assertTrue(null == 0, "null comparison with zero");
|
||||
tc.assertTrue(null == null, "null equals null");
|
||||
|
||||
printf("\n=== Testing Variable Initialization ===\n");
|
||||
|
||||
int uninitialized;
|
||||
uninitialized = 42;
|
||||
tc.assertEqual(uninitialized, 42, "variable can be assigned after declaration");
|
||||
|
||||
int initialized = 100;
|
||||
tc.assertEqual(initialized, 100, "initialized variable has correct value");
|
||||
|
||||
int multi_a = 1;
|
||||
int multi_b = 2;
|
||||
int multi_c = 3;
|
||||
tc.assertEqual(multi_a + multi_b + multi_c, 6, "multiple variable declarations");
|
||||
|
||||
printf("\n=== Testing Loop Boundary Conditions ===\n");
|
||||
|
||||
int zero_iterations = 0;
|
||||
int counter = 0;
|
||||
while (counter < 0) {
|
||||
zero_iterations = zero_iterations + 1;
|
||||
counter = counter + 1;
|
||||
}
|
||||
tc.assertEqual(zero_iterations, 0, "while loop with false condition never executes");
|
||||
|
||||
int one_iteration = 0;
|
||||
counter = 0;
|
||||
while (counter < 1) {
|
||||
one_iteration = one_iteration + 1;
|
||||
counter = counter + 1;
|
||||
}
|
||||
tc.assertEqual(one_iteration, 1, "while loop executes exactly once");
|
||||
|
||||
int many_iterations = 0;
|
||||
counter = 0;
|
||||
while (counter < 100) {
|
||||
many_iterations = many_iterations + 1;
|
||||
counter = counter + 1;
|
||||
}
|
||||
tc.assertEqual(many_iterations, 100, "while loop handles many iterations");
|
||||
|
||||
printf("\n=== Testing If-Else Boundary Conditions ===\n");
|
||||
|
||||
int if_result = 0;
|
||||
if (1) {
|
||||
if_result = 1;
|
||||
}
|
||||
tc.assertEqual(if_result, 1, "if with literal true (1)");
|
||||
|
||||
if_result = 0;
|
||||
if (0) {
|
||||
if_result = 1;
|
||||
}
|
||||
tc.assertEqual(if_result, 0, "if with literal false (0)");
|
||||
|
||||
if_result = 0;
|
||||
if (100) {
|
||||
if_result = 1;
|
||||
}
|
||||
tc.assertEqual(if_result, 1, "if with non-zero value (truthy)");
|
||||
|
||||
if_result = 0;
|
||||
if (-1) {
|
||||
if_result = 1;
|
||||
}
|
||||
tc.assertEqual(if_result, 1, "if with negative value (truthy)");
|
||||
|
||||
printf("\n=== Testing Function Call Edge Cases ===\n");
|
||||
|
||||
int str_len = strlen("");
|
||||
tc.assertEqual(str_len, 0, "strlen of empty string");
|
||||
|
||||
int pos = strpos("hello", "h");
|
||||
tc.assertEqual(pos, 0, "strpos finds character at start");
|
||||
|
||||
pos = strpos("hello", "o");
|
||||
tc.assertEqual(pos, 4, "strpos finds character at end");
|
||||
|
||||
pos = strpos("hello", "x");
|
||||
tc.assertEqual(pos, -1, "strpos returns -1 for not found");
|
||||
|
||||
printf("\n=== Testing Eval Edge Cases ===\n");
|
||||
|
||||
int eval_result = eval("0");
|
||||
tc.assertEqual(eval_result, 0, "eval with zero");
|
||||
|
||||
eval_result = eval("1");
|
||||
tc.assertEqual(eval_result, 1, "eval with one");
|
||||
|
||||
eval_result = eval("-1");
|
||||
tc.assertEqual(eval_result, -1, "eval with negative one");
|
||||
|
||||
eval_result = eval("100 + 0");
|
||||
tc.assertEqual(eval_result, 100, "eval addition with zero");
|
||||
|
||||
eval_result = eval("100 * 1");
|
||||
tc.assertEqual(eval_result, 100, "eval multiplication by one");
|
||||
|
||||
eval_result = eval("100 / 1");
|
||||
tc.assertEqual(eval_result, 100, "eval division by one");
|
||||
|
||||
eval_result = eval("100 - 0");
|
||||
tc.assertEqual(eval_result, 100, "eval subtraction of zero");
|
||||
|
||||
printf("\n=== Testing Complex Nested Conditions ===\n");
|
||||
|
||||
int nested_result = 0;
|
||||
if (1) {
|
||||
if (1) {
|
||||
if (1) {
|
||||
nested_result = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
tc.assertEqual(nested_result, 3, "triple nested if statements");
|
||||
|
||||
nested_result = 0;
|
||||
if (1) {
|
||||
if (0) {
|
||||
nested_result = 1;
|
||||
} else {
|
||||
if (1) {
|
||||
nested_result = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
tc.assertEqual(nested_result, 2, "nested if-else combinations");
|
||||
|
||||
printf("\n=== Testing Operator Precedence ===\n");
|
||||
|
||||
int precedence_result = 2 + 3 * 4;
|
||||
tc.assertEqual(precedence_result, 14, "multiplication before addition");
|
||||
|
||||
precedence_result = 10 - 2 * 3;
|
||||
tc.assertEqual(precedence_result, 4, "multiplication before subtraction");
|
||||
|
||||
precedence_result = (2 + 3) * 4;
|
||||
tc.assertEqual(precedence_result, 20, "parentheses override precedence");
|
||||
|
||||
precedence_result = 2 * 3 + 4 * 5;
|
||||
tc.assertEqual(precedence_result, 26, "multiple multiplications and additions");
|
||||
|
||||
printf("\n=== Testing Assignment Edge Cases ===\n");
|
||||
|
||||
int assign_var = 0;
|
||||
assign_var = 1;
|
||||
tc.assertEqual(assign_var, 1, "simple reassignment");
|
||||
|
||||
assign_var = assign_var + 1;
|
||||
tc.assertEqual(assign_var, 2, "self-referential assignment");
|
||||
|
||||
assign_var = assign_var * 2;
|
||||
tc.assertEqual(assign_var, 4, "self-multiplication assignment");
|
||||
|
||||
int chain_a = 0;
|
||||
int chain_b = 0;
|
||||
chain_a = chain_b = 5;
|
||||
tc.assertEqual(chain_a, 5, "chained assignment first variable");
|
||||
tc.assertEqual(chain_b, 5, "chained assignment second variable");
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All input validation tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int getValue() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int square(int x) {
|
||||
int result = x * x;
|
||||
return result;
|
||||
}
|
||||
|
||||
int max(int a, int b) {
|
||||
if (a > b) {
|
||||
return a;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
int factorial(int n) {
|
||||
if (n <= 1) {
|
||||
return 1;
|
||||
}
|
||||
return n * factorial(n - 1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("Language Features");
|
||||
|
||||
printf("\n=== Testing Variables and Data Types ===\n");
|
||||
int x = 10;
|
||||
tc.assertEqual(x, 10, "Integer variable assignment");
|
||||
|
||||
int y = -5;
|
||||
tc.assertEqual(y, -5, "Negative integer assignment");
|
||||
|
||||
int z = -10;
|
||||
int sum_neg = y + z;
|
||||
tc.assertEqual(sum_neg, -15, "Negative number addition: -5 + -10 = -15");
|
||||
|
||||
char* str = "hello";
|
||||
tc.assertStringEqual(str, "hello", "String variable assignment");
|
||||
|
||||
printf("\n=== Testing Arithmetic Operators ===\n");
|
||||
tc.assertEqual(5 + 3, 8, "Addition: 5 + 3 = 8");
|
||||
tc.assertEqual(10 - 4, 6, "Subtraction: 10 - 4 = 6");
|
||||
tc.assertEqual(6 * 7, 42, "Multiplication: 6 * 7 = 42");
|
||||
tc.assertEqual(20 / 4, 5, "Division: 20 / 4 = 5");
|
||||
tc.assertEqual(15 / 4, 3, "Integer division: 15 / 4 = 3");
|
||||
|
||||
printf("\n=== Testing Comparison Operators ===\n");
|
||||
tc.assertTrue(5 == 5, "Equality: 5 == 5");
|
||||
tc.assertTrue(5 != 3, "Inequality: 5 != 3");
|
||||
tc.assertFalse(5 == 3, "Not equal: 5 == 3 is false");
|
||||
tc.assertFalse(5 != 5, "Same value: 5 != 5 is false");
|
||||
|
||||
tc.assertTrue(10 > 5, "Greater than: 10 > 5");
|
||||
tc.assertFalse(5 > 10, "Not greater: 5 > 10 is false");
|
||||
tc.assertTrue(3 < 7, "Less than: 3 < 7");
|
||||
tc.assertFalse(7 < 3, "Not less: 7 < 3 is false");
|
||||
|
||||
tc.assertTrue(5 >= 5, "Greater or equal (equal): 5 >= 5");
|
||||
tc.assertTrue(10 >= 5, "Greater or equal (greater): 10 >= 5");
|
||||
tc.assertTrue(3 < 5, "3 is less than 5 (not >= 5)");
|
||||
|
||||
tc.assertTrue(5 <= 5, "Less or equal (equal): 5 <= 5");
|
||||
tc.assertTrue(3 <= 5, "Less or equal (less): 3 <= 5");
|
||||
tc.assertTrue(7 > 5, "7 is greater than 5 (not <= 5)");
|
||||
|
||||
printf("\n=== Testing Logical Operators ===\n");
|
||||
tc.assertTrue(1 && 1, "Logical AND: 1 && 1 = true");
|
||||
tc.assertFalse(1 && 0, "Logical AND: 1 && 0 = false");
|
||||
tc.assertFalse(0 && 1, "Logical AND: 0 && 1 = false");
|
||||
tc.assertFalse(0 && 0, "Logical AND: 0 && 0 = false");
|
||||
|
||||
tc.assertTrue(1 || 0, "Logical OR: 1 || 0 = true");
|
||||
tc.assertTrue(0 || 1, "Logical OR: 0 || 1 = true");
|
||||
tc.assertTrue(1 || 1, "Logical OR: 1 || 1 = true");
|
||||
tc.assertFalse(0 || 0, "Logical OR: 0 || 0 = false");
|
||||
|
||||
printf("\n=== Testing Control Flow - If/Else ===\n");
|
||||
int result = 0;
|
||||
if (5 > 3) {
|
||||
result = 1;
|
||||
}
|
||||
tc.assertEqual(result, 1, "If condition true: 5 > 3");
|
||||
|
||||
result = 0;
|
||||
if (2 > 5) {
|
||||
result = 1;
|
||||
} else {
|
||||
result = 2;
|
||||
}
|
||||
tc.assertEqual(result, 2, "Else branch executed: 2 > 5 is false");
|
||||
|
||||
result = 0;
|
||||
if (0) {
|
||||
result = 1;
|
||||
} else {
|
||||
result = 2;
|
||||
}
|
||||
tc.assertEqual(result, 2, "If with 0 goes to else");
|
||||
|
||||
printf("\n=== Testing Control Flow - While Loop ===\n");
|
||||
int i = 0;
|
||||
int count = 0;
|
||||
while (i < 5) {
|
||||
count = count + 1;
|
||||
i = i + 1;
|
||||
}
|
||||
tc.assertEqual(count, 5, "While loop executes 5 times");
|
||||
tc.assertEqual(i, 5, "Loop counter reaches 5");
|
||||
|
||||
i = 0;
|
||||
count = 0;
|
||||
while (count != 3) {
|
||||
count = count + 1;
|
||||
i = i + 1;
|
||||
}
|
||||
tc.assertEqual(count, 3, "While with != condition stops at 3");
|
||||
|
||||
printf("\n=== Testing Control Flow - Break ===\n");
|
||||
i = 0;
|
||||
count = 0;
|
||||
while (i < 10) {
|
||||
count = count + 1;
|
||||
i = i + 1;
|
||||
if (i == 5) {
|
||||
i = 10;
|
||||
}
|
||||
}
|
||||
tc.assertEqual(count, 5, "Break exits loop at i=5");
|
||||
|
||||
i = 0;
|
||||
while (i < 10) {
|
||||
i = i + 1;
|
||||
if (i == 3) {
|
||||
if (i < 5) {
|
||||
i = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
tc.assertEqual(i, 10, "Nested if with break");
|
||||
|
||||
printf("\n=== Testing Control Flow - Continue ===\n");
|
||||
i = 0;
|
||||
count = 0;
|
||||
while (i < 5) {
|
||||
i = i + 1;
|
||||
if (i == 3) {
|
||||
i = i + 0;
|
||||
} else {
|
||||
count = count + 1;
|
||||
}
|
||||
}
|
||||
tc.assertLess(count, 5, "Continue pattern skips iteration");
|
||||
|
||||
i = 0;
|
||||
int sum_val = 0;
|
||||
while (i < 10) {
|
||||
i = i + 1;
|
||||
int skip = 0;
|
||||
if (i == 3 || i == 7) {
|
||||
skip = 1;
|
||||
}
|
||||
if (skip == 0) {
|
||||
sum_val = sum_val + i;
|
||||
}
|
||||
}
|
||||
tc.assertLess(sum_val, 55, "Skipping 3 and 7 reduces sum");
|
||||
|
||||
printf("\n=== Testing Arrays ===\n");
|
||||
int arr[5];
|
||||
arr[0] = 10;
|
||||
arr[1] = 20;
|
||||
arr[2] = 30;
|
||||
arr[3] = 40;
|
||||
arr[4] = 50;
|
||||
tc.assertEqual(arr[0], 10, "Array element [0] = 10");
|
||||
tc.assertEqual(arr[1], 20, "Array element [1] = 20");
|
||||
tc.assertEqual(arr[2], 30, "Array element [2] = 30");
|
||||
tc.assertEqual(arr[3], 40, "Array element [3] = 40");
|
||||
tc.assertEqual(arr[4], 50, "Array element [4] = 50");
|
||||
|
||||
arr[2] = 100;
|
||||
tc.assertEqual(arr[2], 100, "Array element modification: arr[2] = 100");
|
||||
|
||||
i = 0;
|
||||
int array_sum = 0;
|
||||
while (i < 5) {
|
||||
array_sum = array_sum + arr[i];
|
||||
i = i + 1;
|
||||
}
|
||||
tc.assertEqual(array_sum, 220, "Array iteration sum: 10+20+100+40+50 = 220");
|
||||
|
||||
int nums[3];
|
||||
nums[0] = 5;
|
||||
nums[1] = 10;
|
||||
nums[2] = 15;
|
||||
int total = 0;
|
||||
total = total + nums[0];
|
||||
total = total + nums[1];
|
||||
total = total + nums[2];
|
||||
tc.assertEqual(total, 30, "Array arithmetic: 5+10+15 = 30");
|
||||
|
||||
int big[10];
|
||||
int j = 0;
|
||||
while (j < 10) {
|
||||
big[j] = j * j;
|
||||
j = j + 1;
|
||||
}
|
||||
tc.assertEqual(big[0], 0, "Larger array: big[0] = 0^2 = 0");
|
||||
tc.assertEqual(big[3], 9, "Larger array: big[3] = 3^2 = 9");
|
||||
tc.assertEqual(big[9], 81, "Larger array: big[9] = 9^2 = 81");
|
||||
|
||||
printf("\n=== Testing Pointers ===\n");
|
||||
int px = 42;
|
||||
int addr = px;
|
||||
tc.assertEqual(addr, 42, "Pointer/value assignment");
|
||||
|
||||
printf("\n=== Testing Functions ===\n");
|
||||
int val = getValue();
|
||||
tc.assertEqual(val, 42, "Function with no parameters: getValue() = 42");
|
||||
|
||||
int sum_func = add(5, 3);
|
||||
tc.assertEqual(sum_func, 8, "Function with parameters: add(5,3) = 8");
|
||||
|
||||
int sq = square(7);
|
||||
tc.assertEqual(sq, 49, "Function with local vars: square(7) = 49");
|
||||
|
||||
int m1 = max(15, 20);
|
||||
tc.assertEqual(m1, 20, "Conditional function: max(15,20) = 20");
|
||||
|
||||
int m2 = max(30, 10);
|
||||
tc.assertEqual(m2, 30, "Conditional function: max(30,10) = 30");
|
||||
|
||||
int nested = add(square(3), getValue());
|
||||
tc.assertEqual(nested, 51, "Nested calls: add(square(3), getValue()) = add(9,42) = 51");
|
||||
|
||||
printf("\n=== Testing Double Data Type ===\n");
|
||||
int d1 = int_to_double(10);
|
||||
int d2 = int_to_double(3);
|
||||
|
||||
int d_sum = double_add(d1, d2);
|
||||
int sum_int = double_to_int(d_sum);
|
||||
tc.assertEqual(sum_int, 13, "Double addition: 10.0 + 3.0 = 13.0");
|
||||
|
||||
int d_diff = double_sub(d1, d2);
|
||||
int diff_int = double_to_int(d_diff);
|
||||
tc.assertEqual(diff_int, 7, "Double subtraction: 10.0 - 3.0 = 7.0");
|
||||
|
||||
int d_prod = double_mul(d1, d2);
|
||||
int prod_int = double_to_int(d_prod);
|
||||
tc.assertEqual(prod_int, 30, "Double multiplication: 10.0 * 3.0 = 30.0");
|
||||
|
||||
int dx = 42;
|
||||
int dx_double = int_to_double(dx);
|
||||
int dx_back = double_to_int(dx_double);
|
||||
tc.assertEqual(dx_back, 42, "Type conversion round-trip: int->double->int");
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All language feature tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
class Counter {
|
||||
int count = 0;
|
||||
|
||||
void increment(int self) {
|
||||
self.count = self.count + 1;
|
||||
}
|
||||
|
||||
void decrement(int self) {
|
||||
self.count = self.count - 1;
|
||||
}
|
||||
|
||||
void reset(int self) {
|
||||
self.count = 0;
|
||||
}
|
||||
|
||||
int getCount(int self) {
|
||||
return self.count;
|
||||
}
|
||||
}
|
||||
|
||||
class Point {
|
||||
int x = 10;
|
||||
int y = 20;
|
||||
|
||||
void set(int self, int newX, int newY) {
|
||||
self.x = newX;
|
||||
self.y = newY;
|
||||
}
|
||||
|
||||
void move(int self, int dx, int dy) {
|
||||
self.x = self.x + dx;
|
||||
self.y = self.y + dy;
|
||||
}
|
||||
|
||||
int getX(int self) {
|
||||
return self.x;
|
||||
}
|
||||
|
||||
int getY(int self) {
|
||||
return self.y;
|
||||
}
|
||||
}
|
||||
|
||||
class BankAccount {
|
||||
int balance = 0;
|
||||
int transactions = 0;
|
||||
|
||||
void deposit(int self, int amount) {
|
||||
self.balance = self.balance + amount;
|
||||
self.transactions = self.transactions + 1;
|
||||
}
|
||||
|
||||
void withdraw(int self, int amount) {
|
||||
if (self.balance >= amount) {
|
||||
self.balance = self.balance - amount;
|
||||
self.transactions = self.transactions + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int getBalance(int self) {
|
||||
return self.balance;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("OOP Features");
|
||||
|
||||
printf("\n=== Testing Object Creation ===\n");
|
||||
int counter = new Counter();
|
||||
tc.assertNotNull(counter, "new Counter() creates object");
|
||||
|
||||
int point = new Point();
|
||||
tc.assertNotNull(point, "new Point() creates object");
|
||||
|
||||
printf("\n=== Testing Null Objects ===\n");
|
||||
int obj = null;
|
||||
tc.assertNull(obj, "null keyword creates null object");
|
||||
|
||||
tc.assertNotEqual(counter, null, "Created object is not null");
|
||||
|
||||
printf("\n=== Testing Field Access ===\n");
|
||||
tc.assertEqual(counter.count, 0, "Counter.count initialized to 0");
|
||||
tc.assertEqual(point.x, 10, "Point.x initialized to 10");
|
||||
tc.assertEqual(point.y, 20, "Point.y initialized to 20");
|
||||
|
||||
printf("\n=== Testing Field Mutation (Direct) ===\n");
|
||||
counter.count = 5;
|
||||
tc.assertEqual(counter.count, 5, "Direct field mutation: counter.count = 5");
|
||||
|
||||
point.x = 100;
|
||||
point.y = 200;
|
||||
tc.assertEqual(point.x, 100, "Direct field mutation: point.x = 100");
|
||||
tc.assertEqual(point.y, 200, "Direct field mutation: point.y = 200");
|
||||
|
||||
printf("\n=== Testing Method Calls ===\n");
|
||||
counter.count = 0;
|
||||
counter.increment();
|
||||
tc.assertEqual(counter.count, 1, "increment() method works");
|
||||
|
||||
counter.increment();
|
||||
tc.assertEqual(counter.count, 2, "increment() again: count = 2");
|
||||
|
||||
counter.decrement();
|
||||
tc.assertEqual(counter.count, 1, "decrement() method works");
|
||||
|
||||
printf("\n=== Testing Method Return Values ===\n");
|
||||
int count_val = counter.getCount();
|
||||
tc.assertEqual(count_val, 1, "getCount() returns current count");
|
||||
|
||||
int x_val = point.getX();
|
||||
tc.assertEqual(x_val, 100, "getX() returns current x value");
|
||||
|
||||
printf("\n=== Testing Methods with Parameters ===\n");
|
||||
point.set(50, 75);
|
||||
tc.assertEqual(point.x, 50, "set() method updates x");
|
||||
tc.assertEqual(point.y, 75, "set() method updates y");
|
||||
|
||||
point.move(10, 20);
|
||||
tc.assertEqual(point.x, 60, "move() method: x = 50 + 10");
|
||||
tc.assertEqual(point.y, 95, "move() method: y = 75 + 20");
|
||||
|
||||
printf("\n=== Testing Multiple Objects ===\n");
|
||||
int c1 = new Counter();
|
||||
int c2 = new Counter();
|
||||
|
||||
c1.count = 10;
|
||||
c2.count = 20;
|
||||
|
||||
tc.assertEqual(c1.count, 10, "Object 1 has independent state");
|
||||
tc.assertEqual(c2.count, 20, "Object 2 has independent state");
|
||||
|
||||
c1.increment();
|
||||
tc.assertEqual(c1.count, 11, "Object 1 increment doesn't affect object 2");
|
||||
tc.assertEqual(c2.count, 20, "Object 2 remains unchanged");
|
||||
|
||||
printf("\n=== Testing Complex Object State ===\n");
|
||||
int account = new BankAccount();
|
||||
|
||||
account.deposit(1000);
|
||||
tc.assertEqual(account.balance, 1000, "deposit() updates balance");
|
||||
tc.assertEqual(account.transactions, 1, "deposit() increments transaction count");
|
||||
|
||||
account.deposit(500);
|
||||
tc.assertEqual(account.balance, 1500, "Second deposit: balance = 1500");
|
||||
tc.assertEqual(account.transactions, 2, "Transaction count = 2");
|
||||
|
||||
account.withdraw(300);
|
||||
tc.assertEqual(account.balance, 1200, "withdraw() reduces balance");
|
||||
tc.assertEqual(account.transactions, 3, "Transaction count = 3");
|
||||
|
||||
int final_balance = account.getBalance();
|
||||
tc.assertEqual(final_balance, 1200, "getBalance() returns correct value");
|
||||
|
||||
printf("\n=== Testing Method State Changes ===\n");
|
||||
counter.reset();
|
||||
tc.assertEqual(counter.count, 0, "reset() method resets state to 0");
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All OOP tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("Preprocessor");
|
||||
|
||||
printf("\n=== Testing #include Directive ===\n");
|
||||
tc.assertTrue(1, "unittest.rc included successfully");
|
||||
|
||||
printf("\n=== Testing Framework Availability ===\n");
|
||||
int test_obj = new TestCase();
|
||||
tc.assertNotNull(test_obj, "TestCase class available from included file");
|
||||
|
||||
test_obj.init("Nested Test");
|
||||
tc.assertTrue(1, "Can initialize objects from included classes");
|
||||
|
||||
printf("\n=== Testing Multiple Includes ===\n");
|
||||
tc.assertTrue(1, "Multiple #include directives work");
|
||||
tc.assertTrue(1, "No duplicate symbol errors");
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All preprocessor tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("Robustness Tests");
|
||||
|
||||
printf("\n=== Testing Array Bounds Safety ===\n");
|
||||
|
||||
int arr[5];
|
||||
arr[0] = 10;
|
||||
arr[4] = 50;
|
||||
tc.assertEqual(arr[0], 10, "Array first element access");
|
||||
tc.assertEqual(arr[4], 50, "Array last element access");
|
||||
|
||||
printf("\n=== Testing Large Arrays ===\n");
|
||||
|
||||
int big[1000];
|
||||
big[0] = 1;
|
||||
big[999] = 999;
|
||||
tc.assertEqual(big[0], 1, "Large array first element");
|
||||
tc.assertEqual(big[999], 999, "Large array last element");
|
||||
|
||||
printf("\n=== Testing String Operations ===\n");
|
||||
|
||||
char* str1 = "Hello";
|
||||
char* str2 = "World";
|
||||
char* combined = str1 + " " + str2;
|
||||
tc.assertStringContains(combined, "Hello", "String concatenation works");
|
||||
tc.assertStringContains(combined, "World", "String concatenation includes both");
|
||||
|
||||
printf("\n=== Testing Empty Strings ===\n");
|
||||
|
||||
char* empty = "";
|
||||
int empty_len = strlen(empty);
|
||||
tc.assertEqual(empty_len, 0, "Empty string has zero length");
|
||||
|
||||
printf("\n=== Testing String Slicing ===\n");
|
||||
|
||||
char* text = "Programming";
|
||||
char* slice1 = text[0:4];
|
||||
tc.assertStringEqual(slice1, "Prog", "String slice [0:4]");
|
||||
|
||||
char* slice2 = text[0:11];
|
||||
tc.assertStringEqual(slice2, "Programming", "String slice full length");
|
||||
|
||||
printf("\n=== Testing Nested Loops ===\n");
|
||||
|
||||
int outer_count = 0;
|
||||
int i = 0;
|
||||
while (i < 5) {
|
||||
int j = 0;
|
||||
while (j < 3) {
|
||||
outer_count = outer_count + 1;
|
||||
j = j + 1;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
tc.assertEqual(outer_count, 15, "Nested loops: 5 * 3 = 15");
|
||||
|
||||
printf("\n=== Testing Deep Recursion Protection ===\n");
|
||||
|
||||
int stack_depth = 0;
|
||||
int k = 0;
|
||||
while (k < 100) {
|
||||
stack_depth = stack_depth + 1;
|
||||
k = k + 1;
|
||||
}
|
||||
tc.assertEqual(stack_depth, 100, "Deep iteration count");
|
||||
|
||||
printf("\n=== Testing Variable Initialization ===\n");
|
||||
|
||||
int var1 = 0;
|
||||
int var2 = 0;
|
||||
int var3 = 0;
|
||||
int var4 = 0;
|
||||
int var5 = 0;
|
||||
|
||||
var1 = 1;
|
||||
var2 = 2;
|
||||
var3 = 3;
|
||||
var4 = 4;
|
||||
var5 = 5;
|
||||
|
||||
tc.assertEqual(var1 + var2 + var3 + var4 + var5, 15, "Multiple variable sum");
|
||||
|
||||
printf("\n=== Testing Expression Safety ===\n");
|
||||
|
||||
int a = 10;
|
||||
int b = 5;
|
||||
int c = 2;
|
||||
|
||||
int result1 = a + b * c;
|
||||
tc.assertEqual(result1, 20, "Operator precedence: 10 + 5 * 2 = 20");
|
||||
|
||||
int result2 = (a + b) * c;
|
||||
tc.assertEqual(result2, 30, "Parentheses: (10 + 5) * 2 = 30");
|
||||
|
||||
printf("\n=== Testing Zero and Negative Values ===\n");
|
||||
|
||||
int zero = 0;
|
||||
int neg = -5;
|
||||
int pos = 5;
|
||||
|
||||
tc.assertEqual(zero, 0, "Zero value");
|
||||
tc.assertEqual(neg, -5, "Negative value");
|
||||
tc.assertEqual(pos, 5, "Positive value");
|
||||
|
||||
int neg_sum = neg + neg;
|
||||
tc.assertEqual(neg_sum, -10, "Negative addition: -5 + -5 = -10");
|
||||
|
||||
printf("\n=== Testing Printf Safety ===\n");
|
||||
|
||||
printf("Testing integer: %d\n", 42);
|
||||
printf("Testing string: %s\n", "Hello");
|
||||
printf("Testing mixed: %d %s %d\n", 1, "test", 2);
|
||||
|
||||
tc.assertTrue(1, "Printf executes without crash");
|
||||
|
||||
printf("\n=== Testing Array Initialization Pattern ===\n");
|
||||
|
||||
int nums[10];
|
||||
int idx = 0;
|
||||
while (idx < 10) {
|
||||
nums[idx] = idx * idx;
|
||||
idx = idx + 1;
|
||||
}
|
||||
|
||||
tc.assertEqual(nums[0], 0, "nums[0] = 0^2 = 0");
|
||||
tc.assertEqual(nums[3], 9, "nums[3] = 3^2 = 9");
|
||||
tc.assertEqual(nums[9], 81, "nums[9] = 9^2 = 81");
|
||||
|
||||
printf("\n=== Testing String Functions ===\n");
|
||||
|
||||
char* test_str = "test string";
|
||||
int test_len = strlen(test_str);
|
||||
tc.assertEqual(test_len, 11, "strlen('test string') = 11");
|
||||
|
||||
int find_pos = strpos(test_str, "string");
|
||||
tc.assertEqual(find_pos, 5, "strpos finds 'string' at position 5");
|
||||
|
||||
printf("\n=== Testing Conditional Chains ===\n");
|
||||
|
||||
int chain_val = 0;
|
||||
if (a > b) {
|
||||
if (b > c) {
|
||||
if (c > 0) {
|
||||
chain_val = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
tc.assertEqual(chain_val, 3, "Triple nested condition");
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All robustness tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
async int compute_square(int n) {
|
||||
int result = n * n;
|
||||
return result;
|
||||
}
|
||||
|
||||
async int compute_sum(int a, int b) {
|
||||
int result = a + b;
|
||||
return result;
|
||||
}
|
||||
|
||||
async int compute_product(int a, int b) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
async int slow_computation(int n) {
|
||||
int total = 0;
|
||||
int i = 0;
|
||||
while (i < n) {
|
||||
total = total + i;
|
||||
i = i + 1;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("Async Standard Library");
|
||||
|
||||
printf("\n=== Testing Async Function Calls ===\n");
|
||||
int coro1 = compute_square(5);
|
||||
tc.assertNotNull(coro1, "Async function returns coroutine handle");
|
||||
|
||||
int result1 = await(coro1);
|
||||
tc.assertEqual(result1, 25, "await() returns result: square(5) = 25");
|
||||
|
||||
printf("\n=== Testing Multiple Async Calls ===\n");
|
||||
int coro2 = compute_sum(10, 20);
|
||||
int result2 = await(coro2);
|
||||
tc.assertEqual(result2, 30, "Async sum: 10 + 20 = 30");
|
||||
|
||||
int coro3 = compute_sum(5, 15);
|
||||
int result3 = await(coro3);
|
||||
tc.assertEqual(result3, 20, "Async sum: 5 + 15 = 20");
|
||||
|
||||
int coro4 = compute_product(6, 7);
|
||||
int result4 = await(coro4);
|
||||
tc.assertEqual(result4, 42, "Async product: 6 * 7 = 42");
|
||||
|
||||
printf("\n=== Testing Gather (Parallel Execution) ===\n");
|
||||
int coro_a = compute_square(4);
|
||||
int coro_b = compute_square(5);
|
||||
int coro_c = compute_square(6);
|
||||
|
||||
gather(coro_a);
|
||||
gather(coro_b);
|
||||
gather(coro_c);
|
||||
|
||||
int res_a = await(coro_a);
|
||||
int res_b = await(coro_b);
|
||||
int res_c = await(coro_c);
|
||||
|
||||
tc.assertEqual(res_a, 16, "Parallel: square(4) = 16");
|
||||
tc.assertEqual(res_b, 25, "Parallel: square(5) = 25");
|
||||
tc.assertEqual(res_c, 36, "Parallel: square(6) = 36");
|
||||
|
||||
printf("\n=== Testing Nested Async Calls ===\n");
|
||||
int coro_x = compute_sum(5, 10);
|
||||
int intermediate = await(coro_x);
|
||||
tc.assertEqual(intermediate, 15, "First async call: 5 + 10 = 15");
|
||||
|
||||
int coro_y = compute_square(intermediate);
|
||||
int final_result = await(coro_y);
|
||||
tc.assertEqual(final_result, 225, "Chained async: square(15) = 225");
|
||||
|
||||
printf("\n=== Testing Slow Computations ===\n");
|
||||
int slow1 = slow_computation(10);
|
||||
int slow2 = slow_computation(5);
|
||||
|
||||
gather(slow1);
|
||||
gather(slow2);
|
||||
|
||||
int slow_res1 = await(slow1);
|
||||
int slow_res2 = await(slow2);
|
||||
|
||||
tc.assertEqual(slow_res1, 45, "slow_computation(10) = sum(0..9) = 45");
|
||||
tc.assertEqual(slow_res2, 10, "slow_computation(5) = sum(0..4) = 10");
|
||||
|
||||
printf("\n=== Testing Multiple Sequential Awaits ===\n");
|
||||
int seq1 = compute_square(2);
|
||||
int val1 = await(seq1);
|
||||
tc.assertEqual(val1, 4, "Sequential 1: square(2) = 4");
|
||||
|
||||
int seq2 = compute_sum(val1, 5);
|
||||
int val2 = await(seq2);
|
||||
tc.assertEqual(val2, 9, "Sequential 2: 4 + 5 = 9");
|
||||
|
||||
int seq3 = compute_product(val2, 3);
|
||||
int val3 = await(seq3);
|
||||
tc.assertEqual(val3, 27, "Sequential 3: 9 * 3 = 27");
|
||||
|
||||
printf("\n=== Testing Multiple Gather Batches ===\n");
|
||||
int batch1_a = compute_square(2);
|
||||
int batch1_b = compute_square(3);
|
||||
gather(batch1_a);
|
||||
gather(batch1_b);
|
||||
|
||||
int b1a = await(batch1_a);
|
||||
int b1b = await(batch1_b);
|
||||
tc.assertEqual(b1a, 4, "Batch 1a: square(2) = 4");
|
||||
tc.assertEqual(b1b, 9, "Batch 1b: square(3) = 9");
|
||||
|
||||
int batch2_a = compute_sum(10, 10);
|
||||
int batch2_b = compute_sum(20, 20);
|
||||
gather(batch2_a);
|
||||
gather(batch2_b);
|
||||
|
||||
int b2a = await(batch2_a);
|
||||
int b2b = await(batch2_b);
|
||||
tc.assertEqual(b2a, 20, "Batch 2a: 10 + 10 = 20");
|
||||
tc.assertEqual(b2b, 40, "Batch 2b: 20 + 20 = 40");
|
||||
|
||||
printf("\n=== Testing Complex Async Patterns ===\n");
|
||||
int step1 = compute_square(3);
|
||||
int s1 = await(step1);
|
||||
|
||||
int step2a = compute_sum(s1, 5);
|
||||
int step2b = compute_product(s1, 2);
|
||||
gather(step2a);
|
||||
gather(step2b);
|
||||
|
||||
int s2a = await(step2a);
|
||||
int s2b = await(step2b);
|
||||
|
||||
tc.assertEqual(s2a, 14, "Complex pattern: 9 + 5 = 14");
|
||||
tc.assertEqual(s2b, 18, "Complex pattern: 9 * 2 = 18");
|
||||
|
||||
int step3 = compute_sum(s2a, s2b);
|
||||
int s3 = await(step3);
|
||||
tc.assertEqual(s3, 32, "Complex pattern final: 14 + 18 = 32");
|
||||
|
||||
printf("\n=== Testing Async File I/O ===\n");
|
||||
char* async_test_file = "/tmp/test_async_io.txt";
|
||||
char* async_content = "Async test data";
|
||||
|
||||
int write_file = fopen(async_test_file, "w");
|
||||
tc.assertNotNull(write_file, "Open file for async write test");
|
||||
|
||||
int async_write_id = async_fwrite(write_file, async_content, strlen(async_content));
|
||||
tc.assertGreaterEqual(async_write_id, 0, "async_fwrite() returns valid operation ID");
|
||||
|
||||
int write_result = async_wait(async_write_id);
|
||||
tc.assertEqual(write_result, 15, "async_wait() returns bytes written");
|
||||
|
||||
fclose(write_file);
|
||||
|
||||
int read_file = fopen(async_test_file, "r");
|
||||
tc.assertNotNull(read_file, "Open file for async read test");
|
||||
|
||||
int read_addr = 1000;
|
||||
int async_read_id = async_fread(read_file, read_addr, 15);
|
||||
tc.assertGreaterEqual(async_read_id, 0, "async_fread() returns valid operation ID");
|
||||
|
||||
int read_result = async_wait(async_read_id);
|
||||
tc.assertEqual(read_result, 15, "async_wait() returns bytes read");
|
||||
|
||||
fclose(read_file);
|
||||
fremove(async_test_file);
|
||||
|
||||
printf("\n=== Testing Async Poll and Result ===\n");
|
||||
char* poll_test_file = "/tmp/test_async_poll.txt";
|
||||
int poll_file = fopen(poll_test_file, "w");
|
||||
fputs(poll_file, "Poll test data");
|
||||
fclose(poll_file);
|
||||
|
||||
poll_file = fopen(poll_test_file, "r");
|
||||
int poll_addr = 2000;
|
||||
int poll_id = async_fread(poll_file, poll_addr, 14);
|
||||
|
||||
tc.assertGreaterEqual(poll_id, 0, "async_fread() for poll test initiated");
|
||||
|
||||
int poll_wait = async_wait(poll_id);
|
||||
tc.assertEqual(poll_wait, 14, "async_wait() returns correct result");
|
||||
|
||||
fclose(poll_file);
|
||||
fremove(poll_test_file);
|
||||
|
||||
printf("\n=== Testing Async Result Without Wait ===\n");
|
||||
char* result_test_file = "/tmp/test_async_result.txt";
|
||||
int result_file = fopen(result_test_file, "w");
|
||||
fputs(result_file, "Result test");
|
||||
fclose(result_file);
|
||||
|
||||
result_file = fopen(result_test_file, "r");
|
||||
int result_addr = 3000;
|
||||
int result_id = async_fread(result_file, result_addr, 11);
|
||||
|
||||
tc.assertGreaterEqual(result_id, 0, "async_fread() for result test initiated");
|
||||
|
||||
int result_poll = 0;
|
||||
int max_polls = 1000;
|
||||
int poll_count = 0;
|
||||
while (result_poll == 0 && poll_count < max_polls) {
|
||||
result_poll = async_poll(result_id);
|
||||
poll_count = poll_count + 1;
|
||||
}
|
||||
|
||||
tc.assertTrue(result_poll, "async_poll() eventually returns completion");
|
||||
|
||||
int final_result = async_result(result_id);
|
||||
tc.assertEqual(final_result, 11, "async_result() returns correct bytes after polling");
|
||||
|
||||
fclose(result_file);
|
||||
fremove(result_test_file);
|
||||
|
||||
printf("\n=== Testing Multiple Async Operations ===\n");
|
||||
char* multi_file_1 = "/tmp/test_multi_1.txt";
|
||||
char* multi_file_2 = "/tmp/test_multi_2.txt";
|
||||
|
||||
int mf1 = fopen(multi_file_1, "w");
|
||||
int mf2 = fopen(multi_file_2, "w");
|
||||
|
||||
int mid1 = async_fwrite(mf1, "File 1 data", 11);
|
||||
int mid2 = async_fwrite(mf2, "File 2 data", 11);
|
||||
|
||||
tc.assertGreaterEqual(mid1, 0, "First async write initiated");
|
||||
tc.assertGreaterEqual(mid2, 0, "Second async write initiated");
|
||||
|
||||
int mres1 = async_wait(mid1);
|
||||
int mres2 = async_wait(mid2);
|
||||
|
||||
tc.assertEqual(mres1, 11, "First async write completed");
|
||||
tc.assertEqual(mres2, 11, "Second async write completed");
|
||||
|
||||
fclose(mf1);
|
||||
fclose(mf2);
|
||||
fremove(multi_file_1);
|
||||
fremove(multi_file_2);
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All async stdlib tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("Constants Standard Library");
|
||||
|
||||
printf("\n=== Testing Socket Constants ===\n");
|
||||
int af_inet = AF_INET();
|
||||
tc.assertGreater(af_inet, 0, "AF_INET constant is defined and positive");
|
||||
tc.assertEqual(af_inet, 2, "AF_INET equals 2 on most systems");
|
||||
|
||||
int sock_stream = SOCK_STREAM();
|
||||
tc.assertGreater(sock_stream, 0, "SOCK_STREAM constant is defined and positive");
|
||||
tc.assertEqual(sock_stream, 1, "SOCK_STREAM equals 1 on most systems");
|
||||
|
||||
printf("\n=== Testing File Seek Constants ===\n");
|
||||
int seek_set = SEEK_SET();
|
||||
tc.assertEqual(seek_set, 0, "SEEK_SET equals 0");
|
||||
|
||||
int seek_cur = SEEK_CUR();
|
||||
tc.assertEqual(seek_cur, 1, "SEEK_CUR equals 1");
|
||||
|
||||
int seek_end = SEEK_END();
|
||||
tc.assertEqual(seek_end, 2, "SEEK_END equals 2");
|
||||
|
||||
printf("\n=== Testing Constants Consistency ===\n");
|
||||
tc.assertNotEqual(af_inet, sock_stream, "AF_INET and SOCK_STREAM are different");
|
||||
tc.assertNotEqual(seek_set, seek_cur, "SEEK_SET and SEEK_CUR are different");
|
||||
tc.assertNotEqual(seek_cur, seek_end, "SEEK_CUR and SEEK_END are different");
|
||||
|
||||
printf("\n=== Testing Constants Usage ===\n");
|
||||
char* filename = "/tmp/test_constants.txt";
|
||||
int file = fopen(filename, "w");
|
||||
fputs(file, "Testing constants");
|
||||
fclose(file);
|
||||
|
||||
file = fopen(filename, "r");
|
||||
fseek(file, 0, seek_end);
|
||||
int size = ftell(file);
|
||||
tc.assertGreater(size, 0, "SEEK_END used for file size measurement");
|
||||
|
||||
fseek(file, 0, seek_set);
|
||||
int pos = ftell(file);
|
||||
tc.assertEqual(pos, 0, "SEEK_SET used to return to file start");
|
||||
|
||||
fseek(file, 5, seek_cur);
|
||||
pos = ftell(file);
|
||||
tc.assertEqual(pos, 5, "SEEK_CUR used for relative positioning");
|
||||
|
||||
fclose(file);
|
||||
fremove(filename);
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All constants stdlib tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("I/O Standard Library");
|
||||
|
||||
printf("\n=== Testing File Operations ===\n");
|
||||
|
||||
char* filename = "/tmp/test_rc_file.txt";
|
||||
char* content = "Hello, RC!";
|
||||
|
||||
int file = fopen(filename, "w");
|
||||
tc.assertNotNull(file, "fopen() for writing returns valid handle");
|
||||
|
||||
int bytes_written = fwrite(file, content, strlen(content));
|
||||
tc.assertEqual(bytes_written, 10, "fwrite() writes correct number of bytes");
|
||||
|
||||
fclose(file);
|
||||
tc.assertTrue(1, "fclose() completes successfully");
|
||||
|
||||
file = fopen(filename, "r");
|
||||
tc.assertNotNull(file, "fopen() for reading returns valid handle");
|
||||
|
||||
char* buffer = " ";
|
||||
int bytes_read = fread(file, buffer, 10);
|
||||
tc.assertEqual(bytes_read, 10, "fread() reads correct number of bytes");
|
||||
|
||||
fclose(file);
|
||||
|
||||
printf("\n=== Testing File Position Operations ===\n");
|
||||
|
||||
file = fopen(filename, "r");
|
||||
tc.assertNotNull(file, "fopen() for seeking test");
|
||||
|
||||
int pos = ftell(file);
|
||||
tc.assertEqual(pos, 0, "ftell() returns 0 at file start");
|
||||
|
||||
fseek(file, 5, SEEK_SET());
|
||||
pos = ftell(file);
|
||||
tc.assertEqual(pos, 5, "fseek(SEEK_SET, 5) moves to position 5");
|
||||
|
||||
fseek(file, 2, SEEK_CUR());
|
||||
pos = ftell(file);
|
||||
tc.assertEqual(pos, 7, "fseek(SEEK_CUR, 2) moves 2 bytes forward");
|
||||
|
||||
fseek(file, 0, SEEK_END());
|
||||
pos = ftell(file);
|
||||
tc.assertEqual(pos, 10, "fseek(SEEK_END, 0) moves to end of file");
|
||||
|
||||
char* read_attempt = fgets(file, 10);
|
||||
int eof = feof(file);
|
||||
tc.assertTrue(eof, "feof() returns true after read attempt at EOF");
|
||||
|
||||
fclose(file);
|
||||
|
||||
printf("\n=== Testing fputs/fgets ===\n");
|
||||
|
||||
file = fopen(filename, "w");
|
||||
fputs(file, "Line 1\n");
|
||||
fputs(file, "Line 2\n");
|
||||
fclose(file);
|
||||
tc.assertTrue(1, "fputs() writes lines to file");
|
||||
|
||||
file = fopen(filename, "r");
|
||||
char* line1 = fgets(file, 100);
|
||||
tc.assertStringContains(line1, "Line 1", "fgets() reads first line");
|
||||
|
||||
char* line2 = fgets(file, 100);
|
||||
tc.assertStringContains(line2, "Line 2", "fgets() reads second line");
|
||||
|
||||
fclose(file);
|
||||
|
||||
printf("\n=== Testing Socket Constants ===\n");
|
||||
int af_inet = AF_INET();
|
||||
tc.assertGreater(af_inet, 0, "AF_INET constant is defined");
|
||||
|
||||
int sock_stream = SOCK_STREAM();
|
||||
tc.assertGreater(sock_stream, 0, "SOCK_STREAM constant is defined");
|
||||
|
||||
printf("\n=== Testing File Rename ===\n");
|
||||
char* old_name = "/tmp/test_rc_old.txt";
|
||||
char* new_name = "/tmp/test_rc_new.txt";
|
||||
|
||||
file = fopen(old_name, "w");
|
||||
fputs(file, "Rename test");
|
||||
fclose(file);
|
||||
|
||||
int rename_result = frename(old_name, new_name);
|
||||
tc.assertEqual(rename_result, 0, "frename() succeeds");
|
||||
|
||||
file = fopen(new_name, "r");
|
||||
tc.assertNotNull(file, "Renamed file can be opened");
|
||||
fclose(file);
|
||||
|
||||
printf("\n=== Testing File Remove ===\n");
|
||||
int remove_result = fremove(new_name);
|
||||
tc.assertEqual(remove_result, 0, "fremove() succeeds");
|
||||
|
||||
file = fopen(new_name, "r");
|
||||
tc.assertNull(file, "Removed file cannot be opened");
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All I/O stdlib tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("Math Standard Library");
|
||||
|
||||
printf("\n=== Testing sqrt ===\n");
|
||||
tc.assertEqual(sqrt(16), 4, "sqrt(16) = 4");
|
||||
tc.assertEqual(sqrt(25), 5, "sqrt(25) = 5");
|
||||
tc.assertEqual(sqrt(1), 1, "sqrt(1) = 1");
|
||||
tc.assertEqual(sqrt(0), 0, "sqrt(0) = 0");
|
||||
tc.assertEqual(sqrt(100), 10, "sqrt(100) = 10");
|
||||
|
||||
printf("\n=== Testing pow ===\n");
|
||||
tc.assertEqual(pow(2, 3), 8, "pow(2, 3) = 8");
|
||||
tc.assertEqual(pow(5, 2), 25, "pow(5, 2) = 25");
|
||||
tc.assertEqual(pow(10, 0), 1, "pow(10, 0) = 1");
|
||||
tc.assertEqual(pow(3, 4), 81, "pow(3, 4) = 81");
|
||||
tc.assertEqual(pow(2, 10), 1024, "pow(2, 10) = 1024");
|
||||
|
||||
printf("\n=== Testing abs ===\n");
|
||||
tc.assertEqual(abs(-42), 42, "abs(-42) = 42");
|
||||
tc.assertEqual(abs(42), 42, "abs(42) = 42");
|
||||
tc.assertEqual(abs(0), 0, "abs(0) = 0");
|
||||
tc.assertEqual(abs(-1), 1, "abs(-1) = 1");
|
||||
tc.assertEqual(abs(-100), 100, "abs(-100) = 100");
|
||||
|
||||
printf("\n=== Testing floor/ceil ===\n");
|
||||
tc.assertEqual(floor(5), 5, "floor(5) = 5");
|
||||
tc.assertEqual(ceil(5), 5, "ceil(5) = 5");
|
||||
tc.assertEqual(floor(0), 0, "floor(0) = 0");
|
||||
tc.assertEqual(ceil(0), 0, "ceil(0) = 0");
|
||||
|
||||
printf("\n=== Testing Trigonometry ===\n");
|
||||
int sin_result = sin(0);
|
||||
tc.assertEqual(sin_result, 0, "sin(0) = 0");
|
||||
|
||||
int cos_result = cos(0);
|
||||
tc.assertGreater(cos_result, 0, "cos(0) > 0 (fixed-point representation)");
|
||||
|
||||
int tan_result = tan(0);
|
||||
tc.assertEqual(tan_result, 0, "tan(0) = 0");
|
||||
|
||||
printf("\n=== Testing Double Operations ===\n");
|
||||
int d1 = int_to_double(5);
|
||||
int d2 = int_to_double(3);
|
||||
|
||||
int d_sum = double_add(d1, d2);
|
||||
int result_int = double_to_int(d_sum);
|
||||
tc.assertEqual(result_int, 8, "Double addition: 5.0 + 3.0 = 8.0");
|
||||
|
||||
int d_diff = double_sub(d1, d2);
|
||||
result_int = double_to_int(d_diff);
|
||||
tc.assertEqual(result_int, 2, "Double subtraction: 5.0 - 3.0 = 2.0");
|
||||
|
||||
int d_prod = double_mul(d1, d2);
|
||||
result_int = double_to_int(d_prod);
|
||||
tc.assertEqual(result_int, 15, "Double multiplication: 5.0 * 3.0 = 15.0");
|
||||
|
||||
int d_quot = double_div(d1, d2);
|
||||
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);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All math stdlib tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("Socket Standard Library");
|
||||
|
||||
printf("\n=== Testing Socket Creation ===\n");
|
||||
int sockfd = socket(AF_INET(), SOCK_STREAM(), 0);
|
||||
tc.assertGreaterEqual(sockfd, 0, "socket() creates valid socket descriptor");
|
||||
|
||||
printf("\n=== Testing Socket Bind ===\n");
|
||||
int port = 9876;
|
||||
int bind_result = bind(sockfd, port);
|
||||
tc.assertEqual(bind_result, 0, "bind() succeeds on valid port");
|
||||
|
||||
printf("\n=== Testing Socket Listen ===\n");
|
||||
int listen_result = listen(sockfd, 5);
|
||||
tc.assertEqual(listen_result, 0, "listen() succeeds with backlog of 5");
|
||||
|
||||
printf("\n=== Testing Socket Close ===\n");
|
||||
int close_result = close(sockfd);
|
||||
tc.assertEqual(close_result, 0, "close() succeeds on valid socket");
|
||||
|
||||
printf("\n=== Testing Socket Operations on Different Port ===\n");
|
||||
int sockfd2 = socket(AF_INET(), SOCK_STREAM(), 0);
|
||||
tc.assertGreaterEqual(sockfd2, 0, "Second socket creation succeeds");
|
||||
|
||||
int port2 = 9877;
|
||||
bind_result = bind(sockfd2, port2);
|
||||
tc.assertEqual(bind_result, 0, "bind() on different port succeeds");
|
||||
|
||||
listen_result = listen(sockfd2, 10);
|
||||
tc.assertEqual(listen_result, 0, "listen() with backlog of 10 succeeds");
|
||||
|
||||
close_result = close(sockfd2);
|
||||
tc.assertEqual(close_result, 0, "close() on second socket succeeds");
|
||||
|
||||
printf("\n=== Testing Socket Error Conditions ===\n");
|
||||
int invalid_sock = -1;
|
||||
close_result = close(invalid_sock);
|
||||
tc.assertEqual(close_result, -1, "close() fails on invalid socket descriptor");
|
||||
|
||||
printf("\n=== Testing Multiple Socket Creation ===\n");
|
||||
int sock_a = socket(AF_INET(), SOCK_STREAM(), 0);
|
||||
int sock_b = socket(AF_INET(), SOCK_STREAM(), 0);
|
||||
int sock_c = socket(AF_INET(), SOCK_STREAM(), 0);
|
||||
|
||||
tc.assertGreaterEqual(sock_a, 0, "First socket in batch created");
|
||||
tc.assertGreaterEqual(sock_b, 0, "Second socket in batch created");
|
||||
tc.assertGreaterEqual(sock_c, 0, "Third socket in batch created");
|
||||
|
||||
tc.assertNotEqual(sock_a, sock_b, "Socket descriptors are unique (a vs b)");
|
||||
tc.assertNotEqual(sock_b, sock_c, "Socket descriptors are unique (b vs c)");
|
||||
tc.assertNotEqual(sock_a, sock_c, "Socket descriptors are unique (a vs c)");
|
||||
|
||||
close(sock_a);
|
||||
close(sock_b);
|
||||
close(sock_c);
|
||||
tc.assertTrue(1, "All batch sockets closed");
|
||||
|
||||
printf("\n=== Testing Socket Reuse After Close ===\n");
|
||||
int sock_x = socket(AF_INET(), SOCK_STREAM(), 0);
|
||||
tc.assertGreaterEqual(sock_x, 0, "Socket created before close");
|
||||
|
||||
bind(sock_x, 9878);
|
||||
listen(sock_x, 5);
|
||||
close(sock_x);
|
||||
|
||||
int sock_y = socket(AF_INET(), SOCK_STREAM(), 0);
|
||||
tc.assertGreaterEqual(sock_y, 0, "Socket created after previous close");
|
||||
|
||||
int reuse_bind = bind(sock_y, 9878);
|
||||
tc.assertEqual(reuse_bind, 0, "Can bind to same port after close with SO_REUSEADDR");
|
||||
|
||||
close(sock_y);
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All socket stdlib tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("String Standard Library");
|
||||
|
||||
printf("\n=== Testing strlen ===\n");
|
||||
tc.assertEqual(strlen("hello"), 5, "strlen('hello') = 5");
|
||||
tc.assertEqual(strlen(""), 0, "strlen('') = 0");
|
||||
tc.assertEqual(strlen("a"), 1, "strlen('a') = 1");
|
||||
tc.assertEqual(strlen("hello world"), 11, "strlen('hello world') = 11");
|
||||
|
||||
printf("\n=== Testing strpos ===\n");
|
||||
tc.assertEqual(strpos("hello", "e"), 1, "strpos('hello', 'e') = 1");
|
||||
tc.assertEqual(strpos("hello", "l"), 2, "strpos('hello', 'l') = 2");
|
||||
tc.assertEqual(strpos("hello", "o"), 4, "strpos('hello', 'o') = 4");
|
||||
tc.assertEqual(strpos("hello", "x"), -1, "strpos('hello', 'x') = -1 (not found)");
|
||||
tc.assertEqual(strpos("hello", "hello"), 0, "strpos('hello', 'hello') = 0");
|
||||
|
||||
printf("\n=== Testing substr ===\n");
|
||||
tc.assertStringEqual(substr("hello", 0, 5), "hello", "substr('hello', 0, 5) = 'hello'");
|
||||
tc.assertStringEqual(substr("hello", 1, 5), "ello", "substr('hello', 1, 5) = 'ello'");
|
||||
tc.assertStringEqual(substr("hello", 0, 2), "he", "substr('hello', 0, 2) = 'he'");
|
||||
tc.assertStringEqual(substr("hello world", 6, 11), "world", "substr('hello world', 6, 11) = 'world'");
|
||||
|
||||
printf("\n=== Testing String Slicing ===\n");
|
||||
char* text = "Programming";
|
||||
tc.assertStringEqual(text[0:4], "Prog", "String slice [0:4] = 'Prog'");
|
||||
tc.assertStringEqual(text[0:7], "Program", "String slice [0:7] = 'Program'");
|
||||
|
||||
printf("\n=== Testing upper/lower ===\n");
|
||||
tc.assertStringEqual(upper("hello"), "HELLO", "upper('hello') = 'HELLO'");
|
||||
tc.assertStringEqual(upper("HeLLo"), "HELLO", "upper('HeLLo') = 'HELLO'");
|
||||
tc.assertStringEqual(lower("HELLO"), "hello", "lower('HELLO') = 'hello'");
|
||||
tc.assertStringEqual(lower("HeLLo"), "hello", "lower('HeLLo') = 'hello'");
|
||||
|
||||
printf("\n=== Testing strip ===\n");
|
||||
tc.assertStringEqual(strip(" hello "), "hello", "strip(' hello ') = 'hello'");
|
||||
tc.assertStringEqual(strip("hello"), "hello", "strip('hello') = 'hello'");
|
||||
tc.assertStringEqual(strip(" hello"), "hello", "strip(' hello') = 'hello'");
|
||||
tc.assertStringEqual(strip("hello "), "hello", "strip('hello ') = 'hello'");
|
||||
|
||||
printf("\n=== Testing startswith/endswith ===\n");
|
||||
tc.assertTrue(startswith("hello", "he"), "startswith('hello', 'he') = true");
|
||||
tc.assertFalse(startswith("hello", "ho"), "startswith('hello', 'ho') = false");
|
||||
tc.assertTrue(endswith("hello", "lo"), "endswith('hello', 'lo') = true");
|
||||
tc.assertFalse(endswith("hello", "he"), "endswith('hello', 'he') = false");
|
||||
|
||||
printf("\n=== Testing replace ===\n");
|
||||
tc.assertStringEqual(replace("hello world", "world", "there"), "hello there", "replace('hello world', 'world', 'there')");
|
||||
tc.assertStringEqual(replace("aaa", "a", "b"), "bbb", "replace('aaa', 'a', 'b') replaces all occurrences");
|
||||
|
||||
printf("\n=== Testing String Concatenation ===\n");
|
||||
char* hello = "Hello";
|
||||
char* world = " World";
|
||||
char* result = hello + world;
|
||||
tc.assertStringEqual(result, "Hello World", "String concatenation: 'Hello' + ' World'");
|
||||
|
||||
printf("\n=== Testing assertStringContains ===\n");
|
||||
tc.assertStringContains("hello world", "world", "'hello world' contains 'world'");
|
||||
tc.assertStringContains("hello world", "hello", "'hello world' contains 'hello'");
|
||||
tc.assertStringContains("hello world", "o w", "'hello world' contains 'o w'");
|
||||
|
||||
printf("\n=== Testing strcmp ===\n");
|
||||
tc.assertEqual(strcmp("hello", "hello"), 0, "strcmp('hello', 'hello') = 0 (equal)");
|
||||
tc.assertLess(strcmp("abc", "xyz"), 0, "strcmp('abc', 'xyz') < 0 (abc before xyz)");
|
||||
tc.assertGreater(strcmp("xyz", "abc"), 0, "strcmp('xyz', 'abc') > 0 (xyz after abc)");
|
||||
tc.assertEqual(strcmp("test", "test"), 0, "strcmp('test', 'test') = 0");
|
||||
tc.assertNotEqual(strcmp("hello", "world"), 0, "strcmp('hello', 'world') != 0");
|
||||
tc.assertLess(strcmp("a", "b"), 0, "strcmp('a', 'b') < 0");
|
||||
tc.assertGreater(strcmp("z", "a"), 0, "strcmp('z', 'a') > 0");
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All string stdlib tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
class TestCase {
|
||||
char* name = null;
|
||||
int passed = 0;
|
||||
int failed = 0;
|
||||
int verbose = 0;
|
||||
int failfast = 0;
|
||||
|
||||
void init(int self, char* test_name) {
|
||||
self.name = test_name;
|
||||
self.passed = 0;
|
||||
self.failed = 0;
|
||||
self.verbose = 0;
|
||||
self.failfast = 0;
|
||||
}
|
||||
|
||||
void setUp(int self) {
|
||||
|
||||
}
|
||||
|
||||
void tearDown(int self) {
|
||||
|
||||
}
|
||||
|
||||
void assertEqual(int self, int actual, int expected, char* msg) {
|
||||
if (actual == expected) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected %d, got %d)\n", msg, expected, actual);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertNotEqual(int self, int actual, int expected, char* msg) {
|
||||
if (actual != expected) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected not equal to %d)\n", msg, expected);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertTrue(int self, int value, char* msg) {
|
||||
if (value != 0) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected true, got false)\n", msg);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertFalse(int self, int value, char* msg) {
|
||||
if (value == 0) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected false, got true)\n", msg);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertGreater(int self, int actual, int threshold, char* msg) {
|
||||
if (actual > threshold) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected > %d, got %d)\n", msg, threshold, actual);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertLess(int self, int actual, int threshold, char* msg) {
|
||||
if (actual < threshold) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected < %d, got %d)\n", msg, threshold, actual);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertGreaterEqual(int self, int actual, int threshold, char* msg) {
|
||||
if (actual >= threshold) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected >= %d, got %d)\n", msg, threshold, actual);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertLessEqual(int self, int actual, int threshold, char* msg) {
|
||||
if (actual <= threshold) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected <= %d, got %d)\n", msg, threshold, actual);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertStringEqual(int self, char* actual, char* expected, char* msg) {
|
||||
int len_a = strlen(actual);
|
||||
int len_b = strlen(expected);
|
||||
int equal = 0;
|
||||
|
||||
if (len_a == len_b) {
|
||||
equal = 1;
|
||||
int i = 0;
|
||||
while (i < len_a) {
|
||||
char* char_a = actual[i:i+1];
|
||||
char* char_b = expected[i:i+1];
|
||||
int pos = strpos(char_a, char_b);
|
||||
if (pos != 0) {
|
||||
equal = 0;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (equal == 1) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected '%s', got '%s')\n", msg, expected, actual);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertStringContains(int self, char* haystack, char* needle, char* msg) {
|
||||
int pos = strpos(haystack, needle);
|
||||
if (pos >= 0) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected '%s' to contain '%s')\n", msg, haystack, needle);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertNull(int self, int value, char* msg) {
|
||||
if (value == null) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected null, got %d)\n", msg, value);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertNotNull(int self, int value, char* msg) {
|
||||
if (value != null) {
|
||||
self.passed = self.passed + 1;
|
||||
printf(" ✓ PASS: %s\n", msg);
|
||||
} else {
|
||||
self.failed = self.failed + 1;
|
||||
printf(" ✗ FAIL: %s (expected not null)\n", msg);
|
||||
if (self.failfast == 1) {
|
||||
printf("\nFailing fast due to -f flag\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestRunner {
|
||||
int total_passed = 0;
|
||||
int total_failed = 0;
|
||||
int verbose = 0;
|
||||
int quiet = 0;
|
||||
int failfast = 0;
|
||||
|
||||
void init(int self, int verbosity_mode) {
|
||||
self.total_passed = 0;
|
||||
self.total_failed = 0;
|
||||
|
||||
if (verbosity_mode == 1) {
|
||||
self.verbose = 1;
|
||||
self.quiet = 0;
|
||||
} else if (verbosity_mode == 2) {
|
||||
self.verbose = 0;
|
||||
self.quiet = 1;
|
||||
} else if (verbosity_mode == 3) {
|
||||
self.verbose = 0;
|
||||
self.quiet = 0;
|
||||
self.failfast = 1;
|
||||
} else {
|
||||
self.verbose = 0;
|
||||
self.quiet = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void runTest(int self, int test_case, char* test_name) {
|
||||
int tc = test_case;
|
||||
|
||||
tc.verbose = self.verbose;
|
||||
tc.failfast = self.failfast;
|
||||
|
||||
if (self.quiet == 0) {
|
||||
printf("\n=== Running %s ===\n", test_name);
|
||||
}
|
||||
|
||||
self.total_passed = self.total_passed + tc.passed;
|
||||
self.total_failed = self.total_failed + tc.failed;
|
||||
|
||||
if (self.quiet == 0) {
|
||||
if (tc.failed == 0) {
|
||||
printf("OK (%d tests)\n", tc.passed);
|
||||
} else {
|
||||
printf("FAILED (passed=%d, failed=%d)\n", tc.passed, tc.failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printSummary(int self) {
|
||||
printf("\n");
|
||||
printf("======================\n");
|
||||
printf("Test Summary\n");
|
||||
printf("======================\n");
|
||||
printf("Total Passed: %d\n", self.total_passed);
|
||||
printf("Total Failed: %d\n", self.total_failed);
|
||||
printf("======================\n");
|
||||
|
||||
if (self.total_failed == 0) {
|
||||
printf("All tests PASSED!\n");
|
||||
} else {
|
||||
printf("Some tests FAILED\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user