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)
- Doubles (floating-point numbers)
- Character pointers (char*)
- Pointer operations (address-of &, dereference *)
- Array declarations and indexing
Control Flow
- if/else statements
- while loops (including infinite loops)
- break and continue statements
- 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
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
- 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
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 I/O (Multi-threaded)
- 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()
- AF_INET, SOCK_STREAM constants
String Slicing
char *str = "Hello World";
char *slice = str[0:5]; // "Hello"
Building
make # Build the interpreter
make clean # Remove build artifacts
make help # Show all available commands
The build process creates:
bin/rc- The interpreter executablebuild/- Object files (incremental compilation)
Usage
Execute RC programs with the .rc extension:
./bin/rc program.rc
Or use make targets:
make test # Run all tests
make run-feature-test # Run specific test
make run-http-simple # Run simple HTTP server example
Example Programs
Simple Program
int main() {
int x = 10;
int y = 20;
printf("Sum: %d\n", x + y);
return 0;
}
String Manipulation
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 Examples
Simple HTTP Server (examples/http_simple.rc)
Single-connection HTTP server that accepts one request and exits.
Persistent HTTP Server (examples/http_persistent.rc)
HTTP server with request counter that continuously accepts connections.
Multi-Connection HTTP Server (examples/http_multi.rc)
HTTP server that handles up to 100 consecutive connections.
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, 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
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:
make test
Run individual tests:
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.