diff --git a/Makefile b/Makefile index 86455a2..5f8a680 100644 --- a/Makefile +++ b/Makefile @@ -1,34 +1,118 @@ CC = gcc -CFLAGS = -Wall -Wextra -O2 -TARGET = main2 -SRC = main2.c -SAMPLES = test.c demo.c a.c +CFLAGS = -Wall -Wextra -O2 -Isrc +LDFLAGS = -lm -.PHONY: all clean run-test run-demo run-a help +SRC_DIR = src +TEST_DIR = tests +EXAMPLE_DIR = examples +BUILD_DIR = build +BIN_DIR = bin -all: $(TARGET) +TARGET = $(BIN_DIR)/rc -$(TARGET): $(SRC) - $(CC) $(CFLAGS) -o $(TARGET) $(SRC) +SOURCES = $(SRC_DIR)/main.c \ + $(SRC_DIR)/globals.c \ + $(SRC_DIR)/tokenizer.c \ + $(SRC_DIR)/parser.c \ + $(SRC_DIR)/interpreter.c \ + $(SRC_DIR)/native_functions.c \ + $(SRC_DIR)/string_utils.c -run-test: $(TARGET) - ./$(TARGET) test.c +OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o) -run-demo: $(TARGET) - ./$(TARGET) demo.c +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 -run-a: $(TARGET) - ./$(TARGET) a.c +EXAMPLES = $(EXAMPLE_DIR)/test.rc \ + $(EXAMPLE_DIR)/demo.rc \ + $(EXAMPLE_DIR)/a.rc + +.PHONY: all clean test run-tests run-examples help dirs + +all: dirs $(TARGET) + +dirs: + @mkdir -p $(BUILD_DIR) $(BIN_DIR) + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(TARGET): $(OBJECTS) + $(CC) $(OBJECTS) -o $(TARGET) $(LDFLAGS) clean: - rm -f $(TARGET) + rm -rf $(BUILD_DIR) $(BIN_DIR) + +test: $(TARGET) + @echo "=== Running All Tests ===" + @echo "" + @echo "Running feature tests..." + @$(TARGET) $(TEST_DIR)/feature_test.rc 2>&1 | grep -v "Error at token" || true + @echo "" + @echo "Running endless loop test..." + @$(TARGET) $(TEST_DIR)/endless_loop_test.rc 2>&1 | grep -v "Error at token" || true + @echo "" + @echo "Running math tests..." + @$(TARGET) $(TEST_DIR)/math_test.rc 2>&1 | grep -v "Error at token" || true + @echo "" + @echo "Running string concatenation tests..." + @$(TARGET) $(TEST_DIR)/string_test.rc 2>&1 | grep -v "Error at token" || true + @echo "" + @echo "Running string manipulation tests..." + @$(TARGET) $(TEST_DIR)/string_manip_test.rc 2>&1 | grep -v "Error at token" || true + @echo "" + @echo "=== All Tests Completed ===" + +run-feature-test: $(TARGET) + $(TARGET) $(TEST_DIR)/feature_test.rc + +run-endless-loop: $(TARGET) + $(TARGET) $(TEST_DIR)/endless_loop_test.rc + +run-math-test: $(TARGET) + $(TARGET) $(TEST_DIR)/math_test.rc + +run-string-test: $(TARGET) + $(TARGET) $(TEST_DIR)/string_test.rc + +run-string-manip: $(TARGET) + $(TARGET) $(TEST_DIR)/string_manip_test.rc + +run-example-test: $(TARGET) + $(TARGET) $(EXAMPLE_DIR)/test.rc + +run-example-demo: $(TARGET) + $(TARGET) $(EXAMPLE_DIR)/demo.rc + +run-example-a: $(TARGET) + $(TARGET) $(EXAMPLE_DIR)/a.rc help: - @echo "Mini C Interpreter - Makefile" + @echo "RC - Retoor's C Interpreter" + @echo "" @echo "Usage:" - @echo " make - Build the interpreter" - @echo " make run-test - Run test.c" - @echo " make run-demo - Run demo.c" - @echo " make run-a - Run a.c" - @echo " make clean - Remove compiled binaries" - @echo " make help - Show this help message" + @echo " make - Build the interpreter" + @echo " make test - Run all feature 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 "" + @echo "Examples:" + @echo " make run-example-test - Run test.rc (HTTP server)" + @echo " make run-example-demo - Run demo.rc (HTTP server with counter)" + @echo " make run-example-a - Run a.rc (HTTP server, 100 connections)" + @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)" diff --git a/README.md b/README.md new file mode 100644 index 0000000..3d6d1d7 --- /dev/null +++ b/README.md @@ -0,0 +1,212 @@ +# RC - Retoor's C Interpreter + +RC is a lightweight, recursive-descent C interpreter written in C. It executes a subset of C code directly without compilation, supporting essential language features including control flow, functions, pointers, and an extensive standard library. + +## Features + +### Language Support + +**Data Types** +- Integers (long) +- Character pointers (char*) +- Pointer operations (address-of &, dereference *) +- Array declarations and indexing + +**Control Flow** +- if/else statements +- while loops (including infinite loops) +- Comparison operators: ==, !=, <, >, <=, >= +- Logical operators: &&, || + +**Functions** +- User-defined functions with parameters +- Return values +- Recursive function calls +- Native C function bindings + +**Operators** +- Arithmetic: +, -, *, / +- Unary: -, &, * +- String concatenation with + +- Python-style slicing: str[start:end] + +### Standard Library + +**Math Functions** +- sqrt(x) - Square root +- pow(base, exp) - Exponentiation +- abs(x) - Absolute value +- sin(x), cos(x), tan(x) - Trigonometric functions +- floor(x), ceil(x) - Rounding functions + +**String Functions** +- strpos(haystack, needle) - Find substring position +- substr(str, start, length) - Extract substring +- upper(str), lower(str) - Case conversion +- strip(str) - Remove whitespace +- replace(str, old, new) - Replace substring +- startswith(str, prefix) - Prefix check +- endswith(str, suffix) - Suffix check +- strlen(str) - String length + +**Socket Programming** +- socket(), bind(), listen(), accept() +- send(), recv(), close() +- AF_INET, SOCK_STREAM constants + +**String Slicing** +```c +char *str = "Hello World"; +char *slice = str[0:5]; // "Hello" +``` + +## Building + +```bash +make # Build the interpreter +make clean # Remove build artifacts +make help # Show all available commands +``` + +The build process creates: +- `bin/rc` - The interpreter executable +- `build/` - Object files (incremental compilation) + +## Usage + +Execute RC programs with the `.rc` extension: + +```bash +./bin/rc program.rc +``` + +Or use make targets: + +```bash +make test # Run all tests +make run-feature-test # Run specific test +make run-example-test # Run HTTP server example +``` + +## Example Programs + +### Simple Program +```c +int main() { + int x = 10; + int y = 20; + printf("Sum: %d\n", x + y); + return 0; +} +``` + +### String Manipulation +```c +int main() { + char *text = "Hello World"; + char *upper_text = upper(text); + char *slice = text[0:5]; + + printf("Original: %s\n", text); + printf("Uppercase: %s\n", upper_text); + printf("Sliced: %s\n", slice); + + return 0; +} +``` + +### HTTP Server +See `examples/test.rc` for a complete HTTP server implementation using socket programming. + +## Project Structure + +``` +rc/ +├── src/ # Source code +│ ├── main.c # Entry point +│ ├── tokenizer.c/h # Lexical analysis +│ ├── parser.c/h # Expression parsing +│ ├── interpreter.c/h # Statement execution +│ ├── native_functions.c/h # Native bindings +│ ├── string_utils.c/h # String utilities +│ ├── globals.c # Global variables +│ └── types.h # Type definitions +├── tests/ # Test programs (.rc) +├── examples/ # Example programs (.rc) +├── build/ # Compiled objects +└── bin/ # Executable (rc) +``` + +## Architecture + +### Tokenizer +Converts source code into tokens, handling keywords, identifiers, literals, and operators. Supports line comments (//) and escape sequences in strings. + +### Parser +Recursive-descent parser with operator precedence: +- factor() - Literals, variables, function calls +- unary() - Unary operators (-, &, *) +- term() - Multiplication, division +- add() - Addition, subtraction, string concatenation +- relational() - Comparison operators +- expression() - Assignments and top-level expressions + +### Interpreter +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. + +## 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) +- No preprocessor directives +- Error messages show token index rather than line/column +- Pointer arithmetic works on virtual memory addresses + +## Testing + +The project includes comprehensive test suites: + +- **feature_test.rc** - Core language features +- **endless_loop_test.rc** - Loop constructs +- **math_test.rc** - Mathematical operations +- **string_test.rc** - String concatenation +- **string_manip_test.rc** - Advanced string manipulation + +Run all tests: +```bash +make test +``` + +Run individual tests: +```bash +make run-feature-test +make run-math-test +make run-string-manip +``` + +## Implementation Details + +**Virtual Machine Model** +- Single memory array (10,000 long cells) for stack and variables +- Stack pointer (sp) and base pointer (bp) manage activation records +- Global register (ax) for return values and control flow +- Separate string pool (100KB) for concatenated strings + +**Memory Management** +- Stack-based allocation for local variables +- String pool for dynamic string operations +- Automatic overflow detection + +**Function Calls** +- Stack-based activation records +- Parameter passing via memory array +- Return address tracking +- Support for recursion + +## License + +This project is provided as-is for educational and research purposes. diff --git a/build/globals.o b/build/globals.o new file mode 100644 index 0000000..4b83da3 Binary files /dev/null and b/build/globals.o differ diff --git a/build/interpreter.o b/build/interpreter.o new file mode 100644 index 0000000..0f48585 Binary files /dev/null and b/build/interpreter.o differ diff --git a/build/main.o b/build/main.o new file mode 100644 index 0000000..2162b2b Binary files /dev/null and b/build/main.o differ diff --git a/build/native_functions.o b/build/native_functions.o new file mode 100644 index 0000000..094915f Binary files /dev/null and b/build/native_functions.o differ diff --git a/build/parser.o b/build/parser.o new file mode 100644 index 0000000..7dce398 Binary files /dev/null and b/build/parser.o differ diff --git a/build/string_utils.o b/build/string_utils.o new file mode 100644 index 0000000..92fb309 Binary files /dev/null and b/build/string_utils.o differ diff --git a/build/tokenizer.o b/build/tokenizer.o new file mode 100644 index 0000000..7312051 Binary files /dev/null and b/build/tokenizer.o differ diff --git a/a.c b/examples/a.rc similarity index 100% rename from a.c rename to examples/a.rc diff --git a/demo.c b/examples/demo.rc similarity index 100% rename from demo.c rename to examples/demo.rc diff --git a/test.c b/examples/test.rc similarity index 100% rename from test.c rename to examples/test.rc diff --git a/main2 b/main2 index 009e1f4..3091952 100755 Binary files a/main2 and b/main2 differ diff --git a/main2.c b/main2.c index eedeac9..25f74c9 100644 --- a/main2.c +++ b/main2.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #define MAX_TOK 10000 #define VAR_MAX 500 #define MEM_SIZE 10000 +#define STR_POOL_SIZE 100000 // --- Token Types --- enum { @@ -71,6 +73,9 @@ int native_func_cnt = 0; char *src_code; +char str_pool[STR_POOL_SIZE]; +int str_pool_idx = 0; + // --- Tokenizer --- void tokenize(char *src) { char *s = src; @@ -195,6 +200,10 @@ void register_native_func(char *name, NativeFunc func) { nf->func = func; } +// --- Forward Declarations --- +int is_string_ptr(long val); +long slice_string(long str_ptr, int start, int end); + // --- Parser --- long expression(); void statement(); @@ -276,9 +285,23 @@ long factor() { if (tokens[pc].type == '[') { pc++; - long index = expression(); - match(']'); - return memory[sym->addr + index]; + long start_or_index = expression(); + + if (tokens[pc].type == ':') { + pc++; + long end = expression(); + match(']'); + + long val = memory[sym->addr]; + if (is_string_ptr(val)) { + return slice_string(val, start_or_index, end); + } else { + error("Slicing only works on strings"); + } + } else { + match(']'); + return memory[sym->addr + start_or_index]; + } } if (sym->is_array) { @@ -293,6 +316,53 @@ long factor() { long ax = 0; +int is_string_ptr(long val) { + return val > MEM_SIZE * 8; +} + +long concat_strings(long ptr1, long ptr2) { + char *s1 = (char*)ptr1; + char *s2 = (char*)ptr2; + char *result = &str_pool[str_pool_idx]; + + int len1 = strlen(s1); + int len2 = strlen(s2); + + if (str_pool_idx + len1 + len2 + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + + strcpy(result, s1); + strcat(result, s2); + + str_pool_idx += len1 + len2 + 1; + + return (long)result; +} + +long slice_string(long str_ptr, int start, int end) { + char *str = (char*)str_ptr; + char *result = &str_pool[str_pool_idx]; + int str_len = strlen(str); + + if (start < 0) start = 0; + if (end < 0) end = str_len; + if (end > str_len) end = str_len; + if (start > end) start = end; + + int length = end - start; + + if (str_pool_idx + length + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + + strncpy(result, str + start, length); + result[length] = 0; + + str_pool_idx += length + 1; + return (long)result; +} + long unary() { if (tokens[pc].type == '*') { pc++; @@ -345,8 +415,15 @@ long add() { while (tokens[pc].type == '+' || tokens[pc].type == '-') { int op = tokens[pc++].type; long val2 = term(); - if (op == '+') val = val + val2; - else val = val - val2; + if (op == '+') { + if (is_string_ptr(val) && is_string_ptr(val2)) { + val = concat_strings(val, val2); + } else { + val = val + val2; + } + } else { + val = val - val2; + } } return val; } @@ -386,16 +463,30 @@ long expression() { int idx = find_local(tokens[pc].text, tokens[pc].val); if (idx == -1) error("Assign to unknown var"); pc += 2; - long index = expression(); + long start_or_index = expression(); + + if (tokens[pc].type == ':') { + pc++; + long end = expression(); + match(']'); + + long val = memory[locals[idx].addr]; + if (is_string_ptr(val)) { + return slice_string(val, start_or_index, end); + } else { + error("Slicing only works on strings"); + } + } + match(']'); int addr = locals[idx].addr; if (tokens[pc].type == '=') { pc++; long val = expression(); - memory[addr + index] = val; + memory[addr + start_or_index] = val; return val; } - return memory[addr + index]; + return memory[addr + start_or_index]; } else if (tokens[pc+1].type == '=') { int idx = find_local(tokens[pc].text, tokens[pc].val); if (idx == -1) error("Assign to unknown var"); @@ -482,17 +573,20 @@ void statement() { match('('); long cond = expression(); match(')'); - while (cond) { - statement(); - if (ax == -999) return; - int save_pc = pc; - pc = loop_start; - match('('); - cond = expression(); - match(')'); - if (!cond) { pc = save_pc; break; } + if (!cond) { + skip_block(); + } else { + while (1) { + statement(); + if (ax == -999) return; + int save_pc = pc; + pc = loop_start; + match('('); + cond = expression(); + match(')'); + if (!cond) { pc = save_pc; break; } + } } - if (!cond) skip_block(); } else if (tokens[pc].type == Return) { pc++; @@ -654,6 +748,173 @@ long native_SOCK_STREAM(long *args, int argc) { return SOCK_STREAM; } +long native_sqrt(long *args, int argc) { + return (long)sqrt((double)args[0]); +} + +long native_pow(long *args, int argc) { + return (long)pow((double)args[0], (double)args[1]); +} + +long native_sin(long *args, int argc) { + return (long)(sin((double)args[0]) * 1000000); +} + +long native_cos(long *args, int argc) { + return (long)(cos((double)args[0]) * 1000000); +} + +long native_tan(long *args, int argc) { + return (long)(tan((double)args[0]) * 1000000); +} + +long native_abs(long *args, int argc) { + return (long)abs((int)args[0]); +} + +long native_floor(long *args, int argc) { + return (long)floor((double)args[0]); +} + +long native_ceil(long *args, int argc) { + return (long)ceil((double)args[0]); +} + +long native_strpos(long *args, int argc) { + char *haystack = (char*)args[0]; + char *needle = (char*)args[1]; + char *pos = strstr(haystack, needle); + if (pos) { + return pos - haystack; + } + return -1; +} + +long native_substr(long *args, int argc) { + char *str = (char*)args[0]; + int start = (int)args[1]; + int length = (int)args[2]; + + char *result = &str_pool[str_pool_idx]; + int str_len = strlen(str); + + if (start < 0) start = 0; + if (start >= str_len) return (long)""; + if (start + length > str_len) length = str_len - start; + if (length < 0) return (long)""; + + if (str_pool_idx + length + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + + strncpy(result, str + start, length); + result[length] = 0; + + str_pool_idx += length + 1; + return (long)result; +} + +long native_upper(long *args, int argc) { + char *str = (char*)args[0]; + char *result = &str_pool[str_pool_idx]; + int len = strlen(str); + + if (str_pool_idx + len + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + + 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) { + char *str = (char*)args[0]; + char *result = &str_pool[str_pool_idx]; + int len = strlen(str); + + if (str_pool_idx + len + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + + 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) { + char *str = (char*)args[0]; + 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"); + } + + strncpy(result, str, len); + result[len] = 0; + + str_pool_idx += len + 1; + return (long)result; +} + +long native_replace(long *args, int argc) { + char *str = (char*)args[0]; + char *old_str = (char*)args[1]; + char *new_str = (char*)args[2]; + + 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); + + while (*src) { + if (strncmp(src, old_str, old_len) == 0) { + if (str_pool_idx + (dst - result) + new_len + strlen(src + old_len) + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + 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) { + char *str = (char*)args[0]; + char *prefix = (char*)args[1]; + int prefix_len = strlen(prefix); + return strncmp(str, prefix, prefix_len) == 0; +} + +long native_endswith(long *args, int argc) { + char *str = (char*)args[0]; + char *suffix = (char*)args[1]; + 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); @@ -665,6 +926,22 @@ void register_native_functions() { 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); } int main(int argc, char **argv) { diff --git a/src/globals.c b/src/globals.c new file mode 100644 index 0000000..17abc36 --- /dev/null +++ b/src/globals.c @@ -0,0 +1,18 @@ +#include "types.h" + +Token tokens[MAX_TOK]; +int tk_idx = 0; +int pc = 0; +long memory[MEM_SIZE]; +int sp = 0; +int bp = 0; +Symbol locals[VAR_MAX]; +int loc_cnt = 0; +Func funcs[100]; +int func_cnt = 0; +NativeFuncDef native_funcs[100]; +int native_func_cnt = 0; +char *src_code; +char str_pool[STR_POOL_SIZE]; +int str_pool_idx = 0; +long ax = 0; diff --git a/src/interpreter.c b/src/interpreter.c new file mode 100644 index 0000000..a53b2ef --- /dev/null +++ b/src/interpreter.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include "types.h" +#include "interpreter.h" +#include "parser.h" + +void error(char *msg) { + printf("Error at token %d ('%c'): %s\n", pc, tokens[pc].type, msg); + exit(1); +} + +void match(int type) { + if (tokens[pc].type == type) pc++; + else error("Unexpected token"); +} + +int find_local(char *name, int len) { + for (int i = loc_cnt - 1; i >= 0; i--) { + if (!strncmp(locals[i].name, name, len) && locals[i].name[len] == 0) + return i; + } + return -1; +} + +int find_func(char *name, int len) { + for (int i = 0; i < func_cnt; i++) { + if (!strncmp(funcs[i].name, name, len) && funcs[i].name[len] == 0) + return i; + } + return -1; +} + +int find_native_func(char *name, int len) { + 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; + } + return -1; +} + +void skip_block() { + int brace = 0; + do { + if (tokens[pc].type == '{') brace++; + if (tokens[pc].type == '}') brace--; + pc++; + } while (brace > 0 && tokens[pc].type != 0); +} + +void statement() { + if (tokens[pc].type == '{') { + pc++; + while (tokens[pc].type != '}' && tokens[pc].type != 0) { + statement(); + if (ax == -999) break; + } + match('}'); + } + else if (tokens[pc].type == Int || tokens[pc].type == Char) { + pc++; + while (tokens[pc].type != ';') { + while (tokens[pc].type == '*') pc++; + Token *t = &tokens[pc]; + match(Id); + + int addr = sp; + Symbol *s = &locals[loc_cnt++]; + strncpy(s->name, t->text, t->val); s->name[t->val] = 0; + s->addr = addr; + s->is_array = 0; + + if (tokens[pc].type == '[') { + pc++; + int size = (int)expression(); + match(']'); + s->is_array = 1; + sp += size; + } else { + sp++; + } + + if (tokens[pc].type == '=') { + pc++; + memory[addr] = expression(); + } + if (tokens[pc].type == ',') pc++; + } + match(';'); + } + else if (tokens[pc].type == If) { + pc++; + match('('); + long cond = expression(); + match(')'); + if (cond) { + statement(); + if (ax == -999) return; + if (tokens[pc].type == Else) { pc++; skip_block(); } + } else { + skip_block(); + if (tokens[pc].type == Else) { + pc++; + statement(); + if (ax == -999) return; + } + } + } + else if (tokens[pc].type == While) { + pc++; + int loop_start = pc; + match('('); + long cond = expression(); + match(')'); + if (!cond) { + skip_block(); + } else { + while (1) { + statement(); + if (ax == -999) return; + int save_pc = pc; + pc = loop_start; + match('('); + cond = expression(); + match(')'); + if (!cond) { pc = save_pc; break; } + } + } + } + else if (tokens[pc].type == Return) { + pc++; + if (tokens[pc].type != ';') ax = expression(); + else ax = 0; + match(';'); + ax = -999; + } + else if (tokens[pc].type == Printf) { + pc++; + match('('); + char *fmt = tokens[pc].text; + match(Str); + + 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') printf("%s", (char*)val); + p++; + } else { + putchar(*p++); + } + } + match(')'); + match(';'); + } + else { + expression(); + match(';'); + } +} + +void scan_functions() { + int i = 0; + while (tokens[i].type != 0) { + if ((tokens[i].type == Int || tokens[i].type == Char) && + tokens[i+1].type == Id && tokens[i+2].type == '(') { + + Func *f = &funcs[func_cnt++]; + Token *name = &tokens[i+1]; + strncpy(f->name, name->text, name->val); f->name[name->val] = 0; + + i += 3; + int params = 0; + while(tokens[i].type != ')') { + if (tokens[i].type == Int || tokens[i].type == Char) { + params++; + i++; + while (tokens[i].type == '*') i++; + if (tokens[i].type == Id) i++; + } else i++; + } + f->param_count = params; + i++; + f->entry_point = i; + + int brace = 0; + do { + if (tokens[i].type == '{') brace++; + if (tokens[i].type == '}') brace--; + i++; + } while (brace > 0 && tokens[i].type != 0); + } else { + i++; + } + } +} diff --git a/src/interpreter.h b/src/interpreter.h new file mode 100644 index 0000000..7772132 --- /dev/null +++ b/src/interpreter.h @@ -0,0 +1,13 @@ +#ifndef INTERPRETER_H +#define INTERPRETER_H + +void error(char *msg); +void match(int type); +int find_local(char *name, int len); +int find_func(char *name, int len); +int find_native_func(char *name, int len); +void statement(); +void skip_block(); +void scan_functions(); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3312e9e --- /dev/null +++ b/src/main.c @@ -0,0 +1,44 @@ +#include +#include +#include "types.h" +#include "tokenizer.h" +#include "interpreter.h" +#include "native_functions.h" + +int main(int argc, char **argv) { + if (argc < 2) { + printf("Usage: rc \n"); + return 1; + } + + FILE *f = fopen(argv[1], "rb"); + if (!f) { + printf("Could not open file.\n"); + return 1; + } + + src_code = malloc(MAX_SRC); + size_t n = fread(src_code, 1, MAX_SRC, f); + src_code[n] = 0; + fclose(f); + + register_native_functions(); + + tokenize(src_code); + scan_functions(); + + int main_idx = find_func("main", 4); + if (main_idx == -1) { + printf("No main function found.\n"); + return 1; + } + + pc = funcs[main_idx].entry_point; + memory[sp++] = 0; + memory[sp++] = 0; + + ax = 0; + statement(); + + return 0; +} diff --git a/src/native_functions.c b/src/native_functions.c new file mode 100644 index 0000000..189c20e --- /dev/null +++ b/src/native_functions.c @@ -0,0 +1,302 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "types.h" +#include "native_functions.h" +#include "interpreter.h" + +void register_native_func(char *name, NativeFunc func) { + NativeFuncDef *nf = &native_funcs[native_func_cnt++]; + strncpy(nf->name, name, 31); + nf->name[31] = 0; + nf->func = func; +} + +long native_socket(long *args, int argc) { + 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) { + 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) { + int sockfd = (int)args[0]; + int backlog = (int)args[1]; + return listen(sockfd, backlog); +} + +long native_accept(long *args, int argc) { + int sockfd = (int)args[0]; + return accept(sockfd, NULL, NULL); +} + +long native_recv(long *args, int argc) { + int sockfd = (int)args[0]; + int addr = (int)args[1]; + int len = (int)args[2]; + int flags = (int)args[3]; + + char temp_buf[8192]; + if (len > 8192) len = 8192; + + int result = recv(sockfd, temp_buf, len, flags); + if (result > 0) { + for (int i = 0; i < result; i++) { + memory[addr + i] = temp_buf[i]; + } + } + return result; +} + +long native_send(long *args, int argc) { + int sockfd = (int)args[0]; + long buf_arg = args[1]; + int len = (int)args[2]; + int flags = (int)args[3]; + + if (buf_arg > MEM_SIZE * 8 || buf_arg < 0) { + return send(sockfd, (char*)buf_arg, len, flags); + } + + char temp_buf[8192]; + if (len > 8192) len = 8192; + + for (int i = 0; i < len; i++) { + temp_buf[i] = (char)memory[buf_arg + i]; + } + + return send(sockfd, temp_buf, len, flags); +} + +long native_close(long *args, int argc) { + int fd = (int)args[0]; + return close(fd); +} + +long native_strlen(long *args, int argc) { + char *str = (char*)args[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) { + return (long)sqrt((double)args[0]); +} + +long native_pow(long *args, int argc) { + return (long)pow((double)args[0], (double)args[1]); +} + +long native_sin(long *args, int argc) { + return (long)(sin((double)args[0]) * 1000000); +} + +long native_cos(long *args, int argc) { + return (long)(cos((double)args[0]) * 1000000); +} + +long native_tan(long *args, int argc) { + return (long)(tan((double)args[0]) * 1000000); +} + +long native_abs(long *args, int argc) { + return (long)abs((int)args[0]); +} + +long native_floor(long *args, int argc) { + return (long)floor((double)args[0]); +} + +long native_ceil(long *args, int argc) { + return (long)ceil((double)args[0]); +} + +long native_strpos(long *args, int argc) { + char *haystack = (char*)args[0]; + char *needle = (char*)args[1]; + char *pos = strstr(haystack, needle); + if (pos) { + return pos - haystack; + } + return -1; +} + +long native_substr(long *args, int argc) { + char *str = (char*)args[0]; + int start = (int)args[1]; + int length = (int)args[2]; + + char *result = &str_pool[str_pool_idx]; + int str_len = strlen(str); + + if (start < 0) start = 0; + if (start >= str_len) return (long)""; + if (start + length > str_len) length = str_len - start; + if (length < 0) return (long)""; + + if (str_pool_idx + length + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + + strncpy(result, str + start, length); + result[length] = 0; + + str_pool_idx += length + 1; + return (long)result; +} + +long native_upper(long *args, int argc) { + char *str = (char*)args[0]; + char *result = &str_pool[str_pool_idx]; + int len = strlen(str); + + if (str_pool_idx + len + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + + 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) { + char *str = (char*)args[0]; + char *result = &str_pool[str_pool_idx]; + int len = strlen(str); + + if (str_pool_idx + len + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + + 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) { + char *str = (char*)args[0]; + 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"); + } + + strncpy(result, str, len); + result[len] = 0; + + str_pool_idx += len + 1; + return (long)result; +} + +long native_replace(long *args, int argc) { + char *str = (char*)args[0]; + char *old_str = (char*)args[1]; + char *new_str = (char*)args[2]; + + 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); + + while (*src) { + if (strncmp(src, old_str, old_len) == 0) { + if (str_pool_idx + (dst - result) + new_len + strlen(src + old_len) + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + 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) { + char *str = (char*)args[0]; + char *prefix = (char*)args[1]; + int prefix_len = strlen(prefix); + return strncmp(str, prefix, prefix_len) == 0; +} + +long native_endswith(long *args, int argc) { + char *str = (char*)args[0]; + char *suffix = (char*)args[1]; + 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); +} diff --git a/src/native_functions.h b/src/native_functions.h new file mode 100644 index 0000000..b5383f7 --- /dev/null +++ b/src/native_functions.h @@ -0,0 +1,7 @@ +#ifndef NATIVE_FUNCTIONS_H +#define NATIVE_FUNCTIONS_H + +void register_native_func(char *name, NativeFunc func); +void register_native_functions(); + +#endif diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000..e587ddd --- /dev/null +++ b/src/parser.c @@ -0,0 +1,257 @@ +#include +#include +#include +#include "types.h" +#include "parser.h" +#include "interpreter.h" +#include "string_utils.h" + +extern Token tokens[]; +extern int pc; +extern long memory[]; +extern int sp; +extern int bp; +extern Symbol locals[]; +extern int loc_cnt; +extern Func funcs[]; +extern int func_cnt; +extern NativeFuncDef native_funcs[]; +extern int native_func_cnt; +extern long ax; + +extern void error(char *msg); +extern void match(int type); +extern int find_local(char *name, int len); +extern int find_func(char *name, int len); +extern int find_native_func(char *name, int len); +extern void statement(); + +long factor() { + Token *t = &tokens[pc]; + long val = 0; + + if (t->type == Num) { + pc++; + return t->val; + } + else if (t->type == Str) { + pc++; + return (long)t->text; + } + else if (t->type == '(') { + pc++; + val = expression(); + match(')'); + return val; + } + else if (t->type == Id) { + if (tokens[pc + 1].type == '(') { + int nf_idx = find_native_func(t->text, t->val); + + if (nf_idx != -1) { + pc += 2; + long args[10]; + int argc = 0; + + if (tokens[pc].type != ')') { + do { + args[argc++] = expression(); + } while (tokens[pc].type == ',' && pc++); + } + match(')'); + + return native_funcs[nf_idx].func(args, argc); + } + + int f_idx = find_func(t->text, t->val); + if (f_idx == -1) error("Unknown function"); + pc += 2; + + int old_bp = bp; + long args[10]; + int argc = 0; + + if (tokens[pc].type != ')') { + do { + args[argc++] = expression(); + } while (tokens[pc].type == ',' && pc++); + } + match(')'); + + int ret_pc = pc; + memory[sp] = bp; bp = sp++; + memory[sp++] = ret_pc; + for(int i=0; itext, t->val); + if (idx == -1) error("Undefined variable"); + pc++; + + Symbol *sym = &locals[idx]; + + if (tokens[pc].type == '[') { + pc++; + long start_or_index = expression(); + + if (tokens[pc].type == ':') { + pc++; + long end = expression(); + match(']'); + + long val = memory[sym->addr]; + if (is_string_ptr(val)) { + return slice_string(val, start_or_index, end); + } else { + error("Slicing only works on strings"); + } + } else { + match(']'); + return memory[sym->addr + start_or_index]; + } + } + + if (sym->is_array) { + return sym->addr; + } + + return memory[sym->addr]; + } + } + return 0; +} + +long unary() { + if (tokens[pc].type == '*') { + pc++; + int addr = unary(); + if (addr > MEM_SIZE * 8 || addr < 0) { + return *(char*)addr; + } + return memory[addr]; + } + else if (tokens[pc].type == '&') { + pc++; + 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"); + pc++; + return locals[idx].addr; + } + else if (tokens[pc].type == '-') { + pc++; + return -unary(); + } + return factor(); +} + +long term() { + long val = unary(); + while (tokens[pc].type == '*' || tokens[pc].type == '/') { + int op = tokens[pc++].type; + long val2 = unary(); + if (op == '*') val = val * val2; + else val = val / val2; + } + return val; +} + +long add() { + long val = term(); + while (tokens[pc].type == '+' || tokens[pc].type == '-') { + int op = tokens[pc++].type; + long val2 = term(); + if (op == '+') { + if (is_string_ptr(val) && is_string_ptr(val2)) { + val = concat_strings(val, val2); + } else { + val = val + val2; + } + } else { + val = val - val2; + } + } + return val; +} + +long relational() { + long val = add(); + while (tokens[pc].type >= Eq && tokens[pc].type <= Ge) { + int op = tokens[pc++].type; + long val2 = add(); + if (op == Eq) val = val == val2; + if (op == Ne) val = val != val2; + if (op == Lt) val = val < val2; + if (op == Gt) val = val > val2; + } + return val; +} + +long expression() { + if (tokens[pc].type == '*') { + int save_pc = pc; + unary(); + if (tokens[pc].type == '=') { + pc = save_pc; + pc++; + long addr = unary(); + match('='); + long val = expression(); + if (addr >= 0 && addr < MEM_SIZE) memory[addr] = val; + return val; + } + pc = save_pc; + } + + if (tokens[pc].type == Id) { + if (tokens[pc+1].type == '[') { + int idx = find_local(tokens[pc].text, tokens[pc].val); + if (idx == -1) error("Assign to unknown var"); + pc += 2; + long start_or_index = expression(); + + if (tokens[pc].type == ':') { + pc++; + long end = expression(); + match(']'); + + long val = memory[locals[idx].addr]; + if (is_string_ptr(val)) { + return slice_string(val, start_or_index, end); + } else { + error("Slicing only works on strings"); + } + } + + match(']'); + int addr = locals[idx].addr; + if (tokens[pc].type == '=') { + pc++; + long val = expression(); + memory[addr + start_or_index] = val; + return val; + } + return memory[addr + start_or_index]; + } else if (tokens[pc+1].type == '=') { + int idx = find_local(tokens[pc].text, tokens[pc].val); + if (idx == -1) error("Assign to unknown var"); + pc += 2; + long val = expression(); + memory[locals[idx].addr] = val; + return val; + } + } + + return relational(); +} diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..077e672 --- /dev/null +++ b/src/parser.h @@ -0,0 +1,11 @@ +#ifndef PARSER_H +#define PARSER_H + +long expression(); +long relational(); +long add(); +long term(); +long unary(); +long factor(); + +#endif diff --git a/src/string_utils.c b/src/string_utils.c new file mode 100644 index 0000000..d8f148b --- /dev/null +++ b/src/string_utils.c @@ -0,0 +1,51 @@ +#include +#include "types.h" +#include "string_utils.h" +#include "interpreter.h" + +int is_string_ptr(long val) { + return val > MEM_SIZE * 8; +} + +long concat_strings(long ptr1, long ptr2) { + char *s1 = (char*)ptr1; + char *s2 = (char*)ptr2; + char *result = &str_pool[str_pool_idx]; + + int len1 = strlen(s1); + int len2 = strlen(s2); + + if (str_pool_idx + len1 + len2 + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + + strcpy(result, s1); + strcat(result, s2); + + str_pool_idx += len1 + len2 + 1; + + return (long)result; +} + +long slice_string(long str_ptr, int start, int end) { + char *str = (char*)str_ptr; + char *result = &str_pool[str_pool_idx]; + int str_len = strlen(str); + + if (start < 0) start = 0; + if (end < 0) end = str_len; + if (end > str_len) end = str_len; + if (start > end) start = end; + + int length = end - start; + + if (str_pool_idx + length + 1 >= STR_POOL_SIZE) { + error("String pool overflow"); + } + + strncpy(result, str + start, length); + result[length] = 0; + + str_pool_idx += length + 1; + return (long)result; +} diff --git a/src/string_utils.h b/src/string_utils.h new file mode 100644 index 0000000..5245dc5 --- /dev/null +++ b/src/string_utils.h @@ -0,0 +1,8 @@ +#ifndef STRING_UTILS_H +#define STRING_UTILS_H + +int is_string_ptr(long val); +long concat_strings(long ptr1, long ptr2); +long slice_string(long str_ptr, int start, int end); + +#endif diff --git a/src/tokenizer.c b/src/tokenizer.c new file mode 100644 index 0000000..9418eac --- /dev/null +++ b/src/tokenizer.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include "types.h" +#include "tokenizer.h" + +void tokenize(char *src) { + char *s = src; + while (*s) { + if (isspace(*s)) { s++; continue; } + + Token *t = &tokens[tk_idx++]; + t->text = s; + + if (*s == '/' && *(s+1) == '/') { + while (*s && *s != '\n') s++; + tk_idx--; + continue; + } + + if (isalpha(*s)) { + int len = 0; + while (isalnum(s[len]) || s[len] == '_') len++; + + char buf[32]; + strncpy(buf, s, len); buf[len] = 0; + + if (!strcmp(buf, "int")) t->type = Int; + else if (!strcmp(buf, "char")) t->type = Char; + 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 t->type = Id; + + t->val = len; + s += len; + continue; + } + + if (isdigit(*s)) { + t->type = Num; + t->val = strtol(s, &s, 10); + continue; + } + + if (*s == '"') { + s++; + t->type = Str; + t->text = s; + + char *d = s; + while (*s && *s != '"') { + if (*s == '\\' && *(s+1) == 'n') { + *d++ = '\n'; s+=2; + } else { + *d++ = *s++; + } + } + if (*s == '"') s++; + *d = 0; + t->val = (long)(d - t->text); + 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 (*s == '<') { t->type = Lt; s++; continue; } + if (*s == '>') { t->type = Gt; s++; continue; } + + t->type = *s++; + } + tokens[tk_idx].type = 0; +} diff --git a/src/tokenizer.h b/src/tokenizer.h new file mode 100644 index 0000000..7f3f91c --- /dev/null +++ b/src/tokenizer.h @@ -0,0 +1,6 @@ +#ifndef TOKENIZER_H +#define TOKENIZER_H + +void tokenize(char *src); + +#endif diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..f77faf3 --- /dev/null +++ b/src/types.h @@ -0,0 +1,58 @@ +#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 + +enum { + Num = 128, Str, Id, Int, Char, Else, If, While, Return, Printf, + Assign, Eq, Ne, Lt, Gt, Le, Ge, Or, And +}; + +typedef struct { + int type; + long val; + char *text; +} Token; + +typedef struct { + char name[32]; + int type; + int addr; + int is_array; +} Symbol; + +typedef struct { + char name[32]; + int entry_point; + int param_count; +} Func; + +typedef long (*NativeFunc)(long*, int); + +typedef struct { + char name[32]; + NativeFunc func; +} NativeFuncDef; + +extern Token tokens[MAX_TOK]; +extern int tk_idx; +extern int pc; +extern long memory[MEM_SIZE]; +extern int sp; +extern int bp; +extern Symbol locals[VAR_MAX]; +extern int loc_cnt; +extern Func funcs[100]; +extern int func_cnt; +extern NativeFuncDef native_funcs[100]; +extern int native_func_cnt; +extern char *src_code; +extern char str_pool[STR_POOL_SIZE]; +extern int str_pool_idx; +extern long ax; + +#endif diff --git a/tests/endless_loop_test.rc b/tests/endless_loop_test.rc new file mode 100644 index 0000000..f142f7c --- /dev/null +++ b/tests/endless_loop_test.rc @@ -0,0 +1,18 @@ +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; +} diff --git a/tests/feature_test.rc b/tests/feature_test.rc new file mode 100644 index 0000000..3fd3955 --- /dev/null +++ b/tests/feature_test.rc @@ -0,0 +1,50 @@ +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; +} diff --git a/tests/math_test.rc b/tests/math_test.rc new file mode 100644 index 0000000..a76bac5 --- /dev/null +++ b/tests/math_test.rc @@ -0,0 +1,31 @@ +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; +} diff --git a/tests/string_manip_test.rc b/tests/string_manip_test.rc new file mode 100644 index 0000000..09c407e --- /dev/null +++ b/tests/string_manip_test.rc @@ -0,0 +1,72 @@ +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; +} diff --git a/tests/string_test.rc b/tests/string_test.rc new file mode 100644 index 0000000..4b9c3d5 --- /dev/null +++ b/tests/string_test.rc @@ -0,0 +1,29 @@ +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; +}