├── .gitignore ├── .tool-versions ├── Makefile ├── README.md ├── examples ├── anon_functions.zn ├── basics.zn ├── collections.zn ├── hello.zn ├── looping.zn ├── maps.zn ├── simple_async.zn └── structs.zn ├── manifest.yaml ├── outline.md └── src ├── ast.c ├── ast.h ├── codegen ├── anon_function.c ├── anon_function.h ├── codegen.c ├── codegen.h ├── context.c ├── context.h ├── declaration.c ├── declaration.h ├── expression.c ├── expression.h ├── statement.c ├── statement.h ├── utils.c └── utils.h ├── error_reporter.c ├── error_reporter.h ├── lex.yy.c ├── lexer.l ├── llvm_codegen ├── llvm_codegen.c ├── llvm_codegen.h ├── llvm_context.c ├── llvm_context.h └── llvm_expression.h ├── main.c ├── parser.tab.c ├── parser.tab.h ├── parser.y ├── promise.c ├── promise.h ├── socket.c ├── socket.h ├── symtab.c ├── symtab.h ├── threads.c ├── threads.h ├── zeno_arc.c ├── zeno_arc.h ├── zeno_cli.c └── zeno_cli.h /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS system files 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | Icon 6 | ._* 7 | .DocumentRevisions-V100 8 | .fseventsd 9 | .Spotlight-V100 10 | .TemporaryItems 11 | .Trashes 12 | .VolumeIcon.icns 13 | .com.apple.timemachine.donotpresent 14 | .AppleDB 15 | .AppleDesktop 16 | Network Trash Folder 17 | Temporary Items 18 | .apdisk 19 | 20 | # Windows system files 21 | Thumbs.db 22 | Thumbs.db:encryptable 23 | ehthumbs.db 24 | ehthumbs_vista.db 25 | *.stackdump 26 | [Dd]esktop.ini 27 | $RECYCLE.BIN/ 28 | *.cab 29 | *.msi 30 | *.msix 31 | *.msm 32 | *.msp 33 | *.lnk 34 | 35 | # Linux 36 | *~ 37 | .fuse_hidden* 38 | .directory 39 | .Trash-* 40 | .nfs* 41 | 42 | # C programming language 43 | # Prerequisites 44 | *.d 45 | 46 | # Object files 47 | *.o 48 | *.ko 49 | *.obj 50 | *.elf 51 | 52 | # Linker output 53 | *.ilk 54 | *.map 55 | *.exp 56 | 57 | # Precompiled Headers 58 | *.gch 59 | *.pch 60 | 61 | # Libraries 62 | *.lib 63 | *.a 64 | *.la 65 | *.lo 66 | 67 | # Shared objects (inc. Windows DLLs) 68 | *.dll 69 | *.so 70 | *.so.* 71 | *.dylib 72 | 73 | # Executables 74 | *.exe 75 | *.out 76 | *.app 77 | *.i*86 78 | *.x86_64 79 | *.hex 80 | 81 | # Debug files 82 | *.dSYM/ 83 | *.su 84 | *.idb 85 | *.pdb 86 | 87 | # Kernel Module Compile Results 88 | *.mod* 89 | *.cmd 90 | .tmp_versions/ 91 | modules.order 92 | Module.symvers 93 | Mkfile.old 94 | dkms.conf 95 | 96 | # Build folders 97 | build/ 98 | obj/ 99 | bin/ 100 | 101 | # Visual Studio Code 102 | .vscode/* 103 | !.vscode/settings.json 104 | !.vscode/tasks.json 105 | !.vscode/launch.json 106 | !.vscode/extensions.json 107 | !.vscode/*.code-snippets 108 | .history/ 109 | *.vsix 110 | 111 | # Vim 112 | # Swap 113 | [._]*.s[a-v][a-z] 114 | !*.svg 115 | [._]*.sw[a-p] 116 | [._]s[a-rt-v][a-z] 117 | [._]ss[a-gi-z] 118 | [._]sw[a-p] 119 | 120 | # Session 121 | Session.vim 122 | Sessionx.vim 123 | 124 | # Temporary 125 | .netrwhist 126 | *~ 127 | # Auto-generated tag files 128 | tags 129 | # Persistent undo 130 | [._]*.un~ -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | git 2.48.1 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Compiler flags 2 | CC = gcc 3 | CFLAGS = -Wall -Wextra -g -Wunused-variable -Wunused-parameter 4 | INCLUDE_FLAGS = -I$(SRC_DIR) 5 | 6 | # Flex and Bison 7 | FLEX = flex 8 | BISON = bison 9 | 10 | # LLVM Config - find the right llvm-config 11 | LLVM_CONFIG ?= $(shell \ 12 | if command -v llvm-config > /dev/null; then \ 13 | echo llvm-config; \ 14 | elif [ -f /opt/homebrew/opt/llvm/bin/llvm-config ]; then \ 15 | echo /opt/homebrew/opt/llvm/bin/llvm-config; \ 16 | elif [ -f /usr/local/opt/llvm/bin/llvm-config ]; then \ 17 | echo /usr/local/opt/llvm/bin/llvm-config; \ 18 | else \ 19 | echo "Error: llvm-config not found" >&2; \ 20 | exit 1; \ 21 | fi) 22 | 23 | # Export the LLVM bin directory to PATH for compile-time tools 24 | export PATH := $(shell dirname $(shell which $(LLVM_CONFIG))):$(PATH) 25 | 26 | LLVM_CFLAGS = $(shell $(LLVM_CONFIG) --cflags) 27 | LLVM_LDFLAGS = $(shell $(LLVM_CONFIG) --ldflags) 28 | LLVM_LIBS = $(shell $(LLVM_CONFIG) --libs core analysis native bitwriter) 29 | 30 | # Mac Homebrew LLVM support 31 | ifdef CPPFLAGS 32 | LLVM_CFLAGS += $(CPPFLAGS) 33 | else 34 | # Fallback for Mac Homebrew LLVM if environment variable not set 35 | ifneq ($(wildcard /opt/homebrew/opt/llvm/include),) 36 | LLVM_CFLAGS += -I/opt/homebrew/opt/llvm/include 37 | endif 38 | endif 39 | 40 | ifdef LDFLAGS 41 | LLVM_LDFLAGS += $(LDFLAGS) 42 | else 43 | # Fallback for Mac Homebrew LLVM if environment variable not set 44 | ifneq ($(wildcard /opt/homebrew/opt/llvm/lib),) 45 | LLVM_LDFLAGS += -L/opt/homebrew/opt/llvm/lib 46 | endif 47 | endif 48 | 49 | # Directories 50 | SRC_DIR = src 51 | BUILD_DIR = build 52 | OBJ_DIR = $(BUILD_DIR)/obj 53 | BIN_DIR = bin 54 | GEN_DIR = $(BUILD_DIR)/gen 55 | 56 | # Objects 57 | OBJS = $(OBJ_DIR)/ast.o \ 58 | $(OBJ_DIR)/symtab.o \ 59 | $(OBJ_DIR)/codegen/context.o \ 60 | $(OBJ_DIR)/codegen/utils.o \ 61 | $(OBJ_DIR)/codegen/expression.o \ 62 | $(OBJ_DIR)/codegen/statement.o \ 63 | $(OBJ_DIR)/codegen/declaration.o \ 64 | $(OBJ_DIR)/codegen/anon_function.o \ 65 | $(OBJ_DIR)/codegen/codegen.o \ 66 | $(OBJ_DIR)/llvm_codegen/llvm_context.o \ 67 | $(OBJ_DIR)/llvm_codegen/llvm_codegen.o \ 68 | $(OBJ_DIR)/lex.yy.o \ 69 | $(OBJ_DIR)/parser.tab.o \ 70 | $(OBJ_DIR)/main.o \ 71 | $(OBJ_DIR)/zeno_cli.o \ 72 | $(OBJ_DIR)/socket.o \ 73 | $(OBJ_DIR)/error_reporter.o 74 | 75 | # Generated sources 76 | GEN_PARSER_C = $(GEN_DIR)/parser.tab.c 77 | GEN_PARSER_H = $(GEN_DIR)/parser.tab.h 78 | GEN_LEXER_C = $(GEN_DIR)/lex.yy.c 79 | 80 | # Target 81 | TARGET = $(BIN_DIR)/zeno 82 | 83 | # Default target 84 | all: dirs $(TARGET) 85 | 86 | # Create directories 87 | dirs: 88 | @mkdir -p $(OBJ_DIR) $(BIN_DIR) $(GEN_DIR) $(OBJ_DIR)/codegen $(OBJ_DIR)/llvm_codegen 89 | 90 | # Main target 91 | $(TARGET): $(OBJS) 92 | $(CC) $(CFLAGS) $(LLVM_CFLAGS) -o $@ $^ $(LLVM_LDFLAGS) $(LLVM_LIBS) 93 | 94 | # Compile AST implementation 95 | $(OBJ_DIR)/ast.o: $(SRC_DIR)/ast.c $(SRC_DIR)/ast.h 96 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 97 | 98 | # Compile Symbol Table implementation 99 | $(OBJ_DIR)/symtab.o: $(SRC_DIR)/symtab.c $(SRC_DIR)/symtab.h 100 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 101 | 102 | # Compile Codegen Module: Context 103 | $(OBJ_DIR)/codegen/context.o: $(SRC_DIR)/codegen/context.c $(SRC_DIR)/codegen/context.h 104 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 105 | 106 | # Compile Codegen Module: Utils 107 | $(OBJ_DIR)/codegen/utils.o: $(SRC_DIR)/codegen/utils.c $(SRC_DIR)/codegen/utils.h 108 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 109 | 110 | # Compile Codegen Module: Expression 111 | $(OBJ_DIR)/codegen/expression.o: $(SRC_DIR)/codegen/expression.c $(SRC_DIR)/codegen/expression.h 112 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 113 | 114 | # Compile Codegen Module: Statement 115 | $(OBJ_DIR)/codegen/statement.o: $(SRC_DIR)/codegen/statement.c $(SRC_DIR)/codegen/statement.h 116 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 117 | 118 | # Compile Codegen Module: Declaration 119 | $(OBJ_DIR)/codegen/declaration.o: $(SRC_DIR)/codegen/declaration.c $(SRC_DIR)/codegen/declaration.h 120 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 121 | 122 | # Compile Codegen Module: Anonymous Functions 123 | $(OBJ_DIR)/codegen/anon_function.o: $(SRC_DIR)/codegen/anon_function.c $(SRC_DIR)/codegen/anon_function.h 124 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 125 | 126 | # Compile Codegen Module: Main Codegen 127 | $(OBJ_DIR)/codegen/codegen.o: $(SRC_DIR)/codegen/codegen.c $(SRC_DIR)/codegen/codegen.h 128 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 129 | 130 | # Compile LLVM Codegen Module: Context 131 | $(OBJ_DIR)/llvm_codegen/llvm_context.o: $(SRC_DIR)/llvm_codegen/llvm_context.c $(SRC_DIR)/llvm_codegen/llvm_context.h 132 | $(CC) $(CFLAGS) $(LLVM_CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 133 | 134 | # Compile LLVM Codegen Module: Main Codegen 135 | $(OBJ_DIR)/llvm_codegen/llvm_codegen.o: $(SRC_DIR)/llvm_codegen/llvm_codegen.c $(SRC_DIR)/llvm_codegen/llvm_codegen.h 136 | $(CC) $(CFLAGS) $(LLVM_CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 137 | 138 | # Generate parser with Bison 139 | $(GEN_PARSER_C) $(GEN_PARSER_H): $(SRC_DIR)/parser.y 140 | $(BISON) -d -o $(GEN_PARSER_C) $< 141 | 142 | # Generate lexer with Flex 143 | $(GEN_LEXER_C): $(SRC_DIR)/lexer.l $(GEN_PARSER_H) 144 | $(FLEX) -o $@ $< 145 | 146 | # Compile parser 147 | $(OBJ_DIR)/parser.tab.o: $(GEN_PARSER_C) 148 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 149 | 150 | # Compile lexer 151 | $(OBJ_DIR)/lex.yy.o: $(GEN_LEXER_C) 152 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -Wno-sign-compare -c -o $@ $< 153 | 154 | # Compile main 155 | $(OBJ_DIR)/main.o: $(SRC_DIR)/main.c $(SRC_DIR)/ast.h $(SRC_DIR)/symtab.h $(SRC_DIR)/codegen/codegen.h $(SRC_DIR)/zeno_cli.h $(SRC_DIR)/llvm_codegen/llvm_codegen.h 156 | $(CC) $(CFLAGS) $(LLVM_CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 157 | 158 | # Compile CLI tool components 159 | $(OBJ_DIR)/zeno_cli.o: $(SRC_DIR)/zeno_cli.c $(SRC_DIR)/zeno_cli.h 160 | $(CC) $(CFLAGS) $(LLVM_CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 161 | 162 | # Compile Socket wrapper 163 | $(OBJ_DIR)/socket.o: $(SRC_DIR)/socket.c $(SRC_DIR)/socket.h 164 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 165 | 166 | $(OBJ_DIR)/error_reporter.o: $(SRC_DIR)/error_reporter.c $(SRC_DIR)/error_reporter.h 167 | $(CC) $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< 168 | 169 | # Clean up 170 | clean: 171 | rm -rf $(BUILD_DIR) $(BIN_DIR) 172 | 173 | # Clean and rebuild 174 | rebuild: clean all 175 | 176 | # Test with example files 177 | test: all 178 | @echo "Testing with examples..." 179 | $(TARGET) transpile examples/hello.zn examples/hello.c 180 | $(TARGET) transpile examples/structs.zn examples/structs.c 181 | $(TARGET) compile -v examples/hello.zn 182 | @echo "Tests completed!" 183 | 184 | # Test with LLVM compiler 185 | test-llvm: all 186 | @echo "Testing with LLVM compiler..." 187 | $(TARGET) transpile --llvm examples/hello.zn examples/hello.bc 188 | $(TARGET) compile --llvm -v examples/hello.zn 189 | @echo "LLVM tests completed!" 190 | 191 | # Install Zeno CLI tool to /usr/local/bin 192 | install: all 193 | @echo "Installing Zeno CLI tool..." 194 | cp $(TARGET) /usr/local/bin/zeno 195 | @echo "Installation completed!" 196 | 197 | .PHONY: all dirs clean rebuild test test-llvm install 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zeno Language 2 | 3 | A transpiler for Zeno, a C-based language with Elixir-inspired syntax and features. 4 | 5 | ## Language Features 6 | 7 | - C-compatible core with modern syntax 8 | - Type system with inference 9 | - Pattern matching with `match` expressions 10 | - Struct composition with `with` clause 11 | - Spread operator (`...`) for structs and arrays 12 | - Function guard clauses with `where` 13 | - Pipe operator (`|>`) for function chaining 14 | - Immutable variables with `const` 15 | - Anonymous functions (lambda expressions) 16 | 17 | ## Building the Transpiler 18 | 19 | Prerequisites: 20 | - GCC or compatible C compiler 21 | - Flex (lexical analyzer generator) 22 | - Bison (parser generator) 23 | - Make 24 | 25 | To build: 26 | 27 | ```bash 28 | make 29 | ``` 30 | 31 | This will create the `elixir-c-transpiler` binary in the `bin` directory. 32 | 33 | ## Usage 34 | 35 | Zeno provides a single command-line tool with three main commands: 36 | 37 | ```bash 38 | zeno transpile [output] # Convert Zeno code to C 39 | zeno run [OPTIONS] [file] # Transpile, compile, and run Zeno code 40 | zeno compile [OPTIONS] [file] # Transpile and compile Zeno code to a binary 41 | ``` 42 | 43 | Options: 44 | - `-v, --verbose` - Enable verbose output 45 | - `-m, --manifest PATH` - Specify manifest file (default: manifest.yaml) 46 | - `-o, --output FILE` - Specify output file 47 | 48 | ### Project Configuration 49 | 50 | Zeno projects can be configured using a `manifest.yaml` file: 51 | 52 | ```yaml 53 | # Zeno project configuration 54 | name: "my_project" 55 | version: "0.1.0" 56 | output: 57 | # Output directory for generated C files and binaries 58 | dir: "./build" 59 | # Binary name (defaults to project name if not specified) 60 | binary: "my_project" 61 | source: 62 | # Main source file 63 | main: "src/main.zeno" 64 | # Include directories 65 | include: 66 | - "src" 67 | - "lib" 68 | compiler: 69 | # C compiler to use 70 | cc: "gcc" 71 | # Compiler flags 72 | flags: "-Wall -Wextra -O2" 73 | ``` 74 | 75 | ## Examples 76 | 77 | Run the examples: 78 | 79 | ```bash 80 | # Compile and run the transpiler on the examples 81 | make test 82 | 83 | # Compile the generated C code 84 | gcc -o hello examples/hello.c 85 | gcc -o structs examples/structs.c 86 | 87 | # Run the compiled examples 88 | ./hello 89 | ./structs 90 | ``` 91 | 92 | ## Project Structure 93 | 94 | - `src/` - Source code for the transpiler 95 | - `lexer.l` - Flex lexer specification 96 | - `parser.y` - Bison parser specification 97 | - `ast.h/c` - Abstract Syntax Tree definitions 98 | - `symtab.h/c` - Symbol table for tracking variables 99 | - `codegen.h/c` - Code generation to C 100 | - `main.c` - Main program entry point 101 | - `examples/` - Example programs 102 | - `obj/` - Object files (created during build) 103 | - `bin/` - Binary executables (created during build) 104 | 105 | ## Language Syntax Reference 106 | 107 | ### Variable Declaration 108 | 109 | ``` 110 | // Mutable variable 111 | let x = 0; 112 | let y: int = 42; 113 | 114 | // Immutable variable 115 | const PI = 3.14159; 116 | const NAME: string = "Alice"; 117 | ``` 118 | 119 | ### Functions 120 | 121 | ``` 122 | // Basic function 123 | fn add(a: int, b: int): int { 124 | return a + b; 125 | } 126 | 127 | // Arrow function for expressions 128 | fn multiply(a: int, b: int): int => a * b; 129 | 130 | // Anonymous function (lambda) 131 | let square = (x: int): int => x * x; 132 | 133 | // Using an anonymous function 134 | let result = square(5); // Returns 25 135 | 136 | // Function with guard clause 137 | fn factorial(n: int): int where n > 0 { 138 | return n * factorial(n - 1); 139 | } 140 | 141 | fn factorial(n: int): int where n == 0 { 142 | return 1; 143 | } 144 | ``` 145 | 146 | ### Structs and Composition 147 | 148 | ``` 149 | // Basic struct 150 | struct Point { 151 | x: int, 152 | y: int 153 | } 154 | 155 | // Struct composition 156 | struct Entity { 157 | id: string 158 | } 159 | 160 | struct Timestamps { 161 | created_at: int, 162 | updated_at: int 163 | } 164 | 165 | struct User with Entity, Timestamps { 166 | name: string, 167 | email: string 168 | } 169 | 170 | // Creating instances 171 | let user: User = { 172 | id: "user-123", 173 | name: "Alice", 174 | email: "alice@example.com", 175 | created_at: 1678234511, 176 | updated_at: 1678234511 177 | }; 178 | 179 | // With spread operator 180 | let base = {id: "user-123"}; 181 | let timestamps = {created_at: 1678234511, updated_at: 1678234511}; 182 | let user = { 183 | ...base, 184 | ...timestamps, 185 | name: "Alice", 186 | email: "alice@example.com" 187 | }; 188 | ``` 189 | 190 | ### Pattern Matching 191 | 192 | ``` 193 | match value { 194 | 0 => printf("Zero\n"), 195 | n: int where n > 0 => printf("Positive: %d\n", n), 196 | n: int where n < 0 => printf("Negative: %d\n", n), 197 | "hello" => printf("Greeting\n"), 198 | _ => printf("Something else\n") 199 | } 200 | ``` 201 | 202 | ### Pipe Operator 203 | 204 | ``` 205 | // Instead of nested function calls 206 | let result = format(process(parse(data))); 207 | 208 | // Use the pipe operator 209 | let result = data |> parse |> process |> format; 210 | ``` 211 | 212 | ## License 213 | 214 | MIT 215 | -------------------------------------------------------------------------------- /examples/anon_functions.zn: -------------------------------------------------------------------------------- 1 | // Minimal Anonymous Function Example 2 | 3 | import "stdio.h"; 4 | 5 | fn main(): int { 6 | // Simple anonymous function 7 | let square = (x: int): int => x * x; 8 | 9 | // Use the function 10 | printf("Square of 5: %d\n", square(5)); 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /examples/basics.zn: -------------------------------------------------------------------------------- 1 | // Zeno is a simple, llvm based, compiled language that has no OOP, built in garbage collection. 2 | // It is a statically typed language with a syntax similar to C amd elixir. 3 | // It is designed to be easy to learn and use, with a focus on simplicity and readability. 4 | 5 | // Single line comment 6 | 7 | /* 8 | Multi-line comment 9 | This is a multi-line comment 10 | that spans multiple lines. 11 | */ 12 | 13 | ///////////////// VARIABLES 14 | 15 | let y:string = "Hello, World!"; // mutable variable with explicit type 16 | 17 | const w:string = "Hello, Universe!"; // immutable variable with explicit type 18 | 19 | let b:string[] = ["apple", "banana", "cherry"]; // mutable array with explicit type 20 | 21 | const d:string[] = ["dog", "cat", "mouse"]; // immutable array with explicit type 22 | 23 | let f:{ name: string, age: number } = { name: "Jane", age: 25 }; // mutable Hash with explicit type 24 | 25 | const h:{ name: string, age: number } = { name: "Bob", age: 35 }; // immutable Hash with explicit type 26 | 27 | let cd:string[] = [...b, ...d]; // spread operator to combine arrays 28 | let ef:{ name: string, age: number } = { ...f, city: "New York" }; // spread operator to combine hashes 29 | 30 | ///////////////// STRUCTS 31 | 32 | // Base struct 33 | struct Entity { 34 | id: string, 35 | name: string 36 | } 37 | 38 | // Timestamps mixin 39 | struct Timestamps { 40 | created_at: int, 41 | updated_at: int 42 | } 43 | 44 | // User struct with composition 45 | struct User with Entity, Timestamps { 46 | email: string, 47 | active: bool 48 | } 49 | 50 | // Creating instances 51 | let user: User = { 52 | id: "user-123", 53 | name: "Alice", 54 | email: "alice@example.com", 55 | created_at: 1678234511, 56 | updated_at: 1678234511 57 | }; 58 | 59 | // With spread operator 60 | let base = {id: "user-123"}; 61 | let timestamps = {created_at: 1678234511, updated_at: 1678234511}; 62 | let user = { 63 | ...base, 64 | ...timestamps, 65 | name: "Alice", 66 | email: "alice@example.com" 67 | }; 68 | 69 | ///////////////// FUNCTIONS 70 | 71 | // Basic function 72 | fn add(a: int, b: int): int { 73 | return a + b; 74 | } 75 | 76 | // Arrow function for expressions 77 | fn multiply(a: int, b: int): int => a * b; 78 | 79 | // Anonymous function (lambda) 80 | let square = (x: int): int => x * x; 81 | 82 | // Using an anonymous function 83 | let result = square(5); // Returns 25 84 | 85 | // Function with guard clause 86 | fn factorial(n: int): int where n > 0 { 87 | return n * factorial(n - 1); 88 | } 89 | 90 | fn factorial(n: int): int where n == 0 { 91 | return 1; 92 | } 93 | 94 | const eiaf = (a: number, b: number): number => a + b; // immutable function with explicit return type 95 | 96 | 97 | const imlf = (x:int):string => { // immutable multiline function with explicit return type 98 | if (x > 0) { 99 | return "Positive"; 100 | } else if (x < 0) { 101 | return "Negative"; 102 | } else { 103 | return "Zero"; 104 | } 105 | } 106 | 107 | // Async function 108 | async fn fetchData(url: string): string { 109 | let response = await fetch(url); 110 | let data = await response.text(); 111 | return data; 112 | } 113 | 114 | // Async Anonymous function 115 | let asyncFetchData = async (url: string): string => { 116 | let response = await fetch(url); 117 | let data = await response.text(); 118 | return data; 119 | } 120 | 121 | // calling async function 122 | fetchData("https://example.com/api/data"); 123 | |> then((data) => { 124 | printf("Data fetched: %s\n", data); 125 | }) 126 | |> catch((error) => { 127 | printf("Error fetching data: %s\n", error); 128 | }); 129 | 130 | // Using async function with await in an async context 131 | async fn main() { 132 | let data = await fetchData("https://example.com/api/data"); 133 | printf("Data fetched: %s\n", data); 134 | } 135 | 136 | 137 | ///////////////// Pattern Matching 138 | 139 | match value { 140 | 0 => printf("Zero\n"), 141 | n: int where n > 0 => printf("Positive: %d\n", n), 142 | n: int where n < 0 => printf("Negative: %d\n", n), 143 | n: int where n % 2 == 0 => { 144 | printf("Here: %d\n", n); 145 | printf("Even: %d\n", n); 146 | }, 147 | "hello" => printf("Greeting\n"), 148 | _ => printf("Something else\n") 149 | } 150 | 151 | ///////////////// Pipe Operator 152 | 153 | // The pipe operator (|>) allows you to chain function calls in a more readable way 154 | 155 | // Instead of nested function calls 156 | let result = format(process(parse(data))); 157 | 158 | // Use the pipe operator 159 | let result = data |> parse |> process |> format; 160 | 161 | ///////////////// For / ForEach Loop 162 | 163 | // For loop 164 | for i in 0..10 { 165 | printf("%d\n", i); 166 | } 167 | 168 | let zzz:string[] = ["apple", "banana", "cherry"]; // mutable array with explicit type 169 | 170 | for i in zzz.length { 171 | printf("%s\n", zzz[i]); 172 | } 173 | 174 | // ForEach loop (array) 175 | let aaa:string[] = ["apple", "banana", "cherry"]; // mutable array with explicit type 176 | 177 | for a in aaa { 178 | printf("%s\n", item); 179 | } 180 | 181 | // ForEach loop with Hash 182 | let fff:{ name: string, age: number } = { name: "Jane", age: 25 }; // mutable Hash with explicit type 183 | 184 | for {key, value} in fff { 185 | printf("%s: %s\n", key, value); 186 | } 187 | 188 | //////////// SCOPE AND IMPORTING FILES 189 | // variables, functions, structs, etc.. are all private to the file they are defined in unless prefixed with `public` then when a file is imported `public` identified items can be used. 190 | // An imported file is evaluated before the current file, so any public items in the imported file are available in the current file. 191 | 192 | /* file_a.zn */ 193 | let x:string = "hello world"; 194 | 195 | public let y:string = "goodbye world"; 196 | public let z:string = "goodbye world"; 197 | 198 | // importing files 199 | 200 | import { y } from "/path/to/file.zn"; // import file_a.zn and expose x and y; 201 | 202 | import { 203 | y, 204 | z 205 | } from "/path/to/file.zn"; // import file_a.zn and expose x and y; 206 | 207 | printf("%s\n", y); // prints "goodbye world" 208 | printf("%s\n", z); // prints "goodbye world" 209 | 210 | /* 211 | Example Main 212 | */ 213 | 214 | async fn main() { 215 | // Call the async function 216 | let data = await fetchData("https://example.com/api/data"); 217 | printf("Data fetched: %s\n", data); 218 | } -------------------------------------------------------------------------------- /examples/collections.zn: -------------------------------------------------------------------------------- 1 | // Main function with array collection 2 | fn main(): int { 3 | // Array initialization 4 | let arr: array = [1, 2, 3, 4, 5]; 5 | 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /examples/hello.zn: -------------------------------------------------------------------------------- 1 | // Simple hello world example in Zeno 2 | 3 | import "stdio.h"; 4 | 5 | // Main function 6 | fn main(): int { 7 | let message: string = "Hello, world!"; 8 | printf("%s\n", message); 9 | return 0; 10 | } -------------------------------------------------------------------------------- /examples/looping.zn: -------------------------------------------------------------------------------- 1 | import "stdio.h"; 2 | 3 | fn main(): int { 4 | printf("--- While Loop ---\n"); 5 | let i: int = 0; 6 | while (i < 3) { 7 | printf("while loop iteration: %d\n", i); 8 | i = i + 1; 9 | } 10 | 11 | printf("\n--- C-style For Loop ---\n"); 12 | for (let j: int = 0; j < 3; j = j + 1) { 13 | printf("C-style for loop iteration: %d\n", j); 14 | } 15 | 16 | // Note: Range-based for loop syntax might need verification based on implementation 17 | // Assuming '..' is inclusive start, exclusive end as common in many languages 18 | printf("\n--- Range-based For Loop ---\n"); 19 | for k in 0..3 { 20 | printf("Range-based for loop iteration: %d\n", k); 21 | } 22 | 23 | printf("\n--- Array Iteration For Loop ---\n"); 24 | let fruits: string[] = ["apple", "banana", "cherry"]; 25 | for fruit in fruits { 26 | // Assuming string interpolation or direct printing works, otherwise use printf 27 | // printf("Fruit: %s\n", fruit); // Using printf for certainty 28 | printf("Fruit: %s\n", fruit); 29 | } 30 | 31 | printf("\n--- Map/Hash Iteration For Loop ---\n"); 32 | // Note: Map iteration syntax and functionality might depend on implementation details 33 | const scores: map = {"Alice": 90, "Bob": 85, "Charlie": 95}; 34 | // Assuming this syntax is supported for map iteration 35 | for {key, value} in scores { 36 | printf("Score for %s: %d\n", key, value); 37 | } 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /examples/maps.zn: -------------------------------------------------------------------------------- 1 | // Example demonstrating map type and literal syntax 2 | 3 | fn main() { 4 | const myMap: map = {"name": "Zeno", "version": "0.1"}; 5 | const anotherMap: map = {"one": 1, "two": 2}; 6 | 7 | // Note: Map access (myMap["name"]) and iteration are not yet implemented 8 | // in the parser or code generator. This example only tests declaration 9 | // and literal creation. 10 | print("Map example created successfully (compilation test only).\\n"); 11 | } 12 | 13 | main(); 14 | -------------------------------------------------------------------------------- /examples/simple_async.zn: -------------------------------------------------------------------------------- 1 | // Simple demonstration of async/await and promises in Zeno 2 | 3 | // Simulates fetching data from a server 4 | async fn fetchData(id: string): string { 5 | // In a real implementation, this would make a network request 6 | // and return the result as a promise 7 | return "Data for ID: " + id; 8 | } 9 | 10 | // Process the fetched data 11 | fn processData(data: string): string { 12 | return "Processed: " + data; 13 | } 14 | 15 | // Handle any errors that might occur 16 | fn handleError(error: string): string { 17 | return "Error occurred: " + error; 18 | } 19 | 20 | 21 | // Cleanup function 22 | fn cleanup(): void { 23 | // Perform cleanup operations 24 | } 25 | 26 | // Main function demonstrating the use of promises 27 | fn main(): int { 28 | // Using await syntax to directly get the result 29 | let directResult = await fetchData("123"); 30 | 31 | // Using promise chaining with then, catch, and finally 32 | fetchData("456") 33 | .then((data) => processData(data)) 34 | .catch((error) => handleError(error)) 35 | .finally(() => cleanup()); 36 | 37 | // Using Promise.all to wait for multiple promises 38 | let results = await Promise.all([ 39 | fetchData("789"), 40 | fetchData("abc") 41 | ]); 42 | } 43 | -------------------------------------------------------------------------------- /examples/structs.zn: -------------------------------------------------------------------------------- 1 | // Struct composition example in Zeno 2 | 3 | import "stdio.h"; 4 | 5 | // Base struct 6 | struct Entity { 7 | id: string, 8 | name: string 9 | } 10 | 11 | // Timestamps mixin 12 | struct Timestamps { 13 | created_at: int, 14 | updated_at: int 15 | } 16 | 17 | // User struct with composition 18 | struct User with Entity, Timestamps { 19 | email: string, 20 | active: bool 21 | } 22 | 23 | // Function that works with a composed struct 24 | fn print_user_details(user: User): void { 25 | printf("User ID: %s\n", user.id); 26 | printf("Name: %s\n", user.name); 27 | printf("Email: %s\n", user.email); 28 | printf("Created: %d\n", user.created_at); 29 | 30 | match user.active { 31 | true => printf("Status: Active\n"); 32 | false => printf("Status: Inactive\n"); 33 | } 34 | } 35 | 36 | // Main function 37 | fn main(): int { 38 | // Create a timestamp 39 | let now: int = 1678234511; 40 | 41 | // Create a user with spread operator 42 | let user: User = { 43 | ...{id: "user-123", name: "Alice"}, 44 | ...{created_at: now, updated_at: now}, 45 | email: "alice@example.com", 46 | active: true 47 | }; 48 | 49 | // Print user details 50 | user |> print_user_details; 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /manifest.yaml: -------------------------------------------------------------------------------- 1 | # Zeno project configuration 2 | name: "my_project" 3 | version: "0.1.0" 4 | output: 5 | # Output directory for generated C files and binaries 6 | dir: "./build" 7 | # Binary name (defaults to project name if not specified) 8 | binary: "my_project" 9 | source: 10 | # Main source file 11 | main: "src/main.zeno" 12 | # Include directories 13 | include: 14 | - "src" 15 | - "lib" 16 | compiler: 17 | # C compiler to use 18 | cc: "gcc" 19 | # Compiler flags 20 | flags: "-Wall -Wextra -O2 -Wno-unused-parameter -Wno-unused-variable" 21 | -------------------------------------------------------------------------------- /outline.md: -------------------------------------------------------------------------------- 1 | # Zeno Language Specification Outline 2 | 3 | ## 1. Introduction 4 | 5 | * **Philosophy**: Zeno is a simple, LLVM-based, compiled language. It aims for simplicity and readability, drawing syntax inspiration from C and Elixir. 6 | * **Key Features**: 7 | * Statically typed. 8 | * No Object-Oriented Programming (OOP). 9 | * Built-in garbage collection (mentioned in `basics.zn` comment, needs verification). 10 | * Async/await support. 11 | * Struct composition. 12 | 13 | ## 2. Syntax Basics 14 | 15 | * **Comments**: 16 | * Single-line: `// comment text` 17 | * Multi-line: `/* comment text */` 18 | * **Entry Point**: Typically `fn main(): int { ... }`. Can also be `async fn main() { ... }`. 19 | * **Imports**: 20 | * Import C headers: `import "header.h";` (e.g., `import "stdio.h";`) 21 | * Import Zeno modules: `import { identifier1, identifier2 } from "/path/to/file.zn";` 22 | * **Scope & Visibility**: 23 | * Identifiers (variables, functions, structs) are file-private by default. 24 | * Use the `public` keyword to make identifiers accessible outside the defining file (e.g., `public let x: int = 10;`). 25 | 26 | ## 3. Data Types 27 | 28 | * **Primitive Types**: 29 | * `int`: Integer numbers. 30 | * `string`: Text strings. 31 | * `bool`: Boolean values (`true`, `false`). 32 | * `number`: General number type (usage seen, relation to `int` needs clarification). 33 | * `void`: Represents no return value for functions. 34 | * **Collections**: 35 | * **Arrays**: 36 | * Fixed-size style: `let name: type[] = [value1, value2];` (e.g., `let b: string[] = ["a", "b"];`) 37 | * Generic style: `let name: array = [value1, value2];` (e.g., `let arr: array = [1, 2];`) 38 | * **Maps (Hashes)**: 39 | * Inline literal style: `let name: { key1: type1, key2: type2 } = { key1: value1, key2: value2 };` (e.g., `let f: { name: string, age: number } = { name: "Jane", age: 25 };`) 40 | * Generic map type: `const name: map = {key1: value1, key2: value2};` (e.g., `const myMap: map = {"name": "Zeno"};`) 41 | * *Note*: Map access and iteration were noted as potentially unimplemented in `maps.zn`. 42 | 43 | ## 4. Variables 44 | 45 | * **Declaration**: 46 | * Mutable: `let name: type = value;` 47 | * Immutable: `const name: type = value;` 48 | * **Type Annotation**: Explicit type annotation (`: type`) is generally used. 49 | 50 | ## 5. Operators 51 | 52 | * **Arithmetic**: `+`, `*` (others likely exist, e.g., `-`, `/`) 53 | * **Comparison**: `<`, `==`, `>` (others likely exist, e.g., `<=`, `>=`, `!=`) 54 | * **Modulo**: `%` 55 | * **Spread**: `...` 56 | * Used to combine arrays: `let combined: type[] = [...arr1, ...arr2];` 57 | * Used to combine hashes/struct literals: `let combined: { ... } = { ...hash1, ...hash2, new_field: value };` 58 | * **Pipe**: `|>` 59 | * Chains function calls: `value |> func1 |> func2;` is equivalent to `func2(func1(value));` 60 | 61 | ## 6. Control Flow 62 | 63 | * **Conditional**: `if (condition) { ... } else if (condition) { ... } else { ... }` 64 | * **Pattern Matching**: `match value { pattern1 => expression1, pattern2 where guard => expression2, _ => default_expression }` 65 | * Supports matching values, types (`n: int`), and guards (`where n > 0`). 66 | * Can have block expressions (`{ ... }`) as results. 67 | * `_` is the wildcard/default case. 68 | * **Loops**: 69 | * `while (condition) { ... }` 70 | * C-style `for`: `for (let i = 0; i < limit; i = i + 1) { ... }` 71 | * Range-based `for`: `for i in start..end { ... }` (inclusive start, exclusive end?) 72 | * Array iteration: `for item in array { ... }` 73 | * Hash/Map iteration: `for {key, value} in map { ... }` 74 | 75 | ## 7. Functions 76 | 77 | * **Standard Definition**: `fn name(param1: type1, param2: type2): return_type { body }` 78 | * **Arrow Syntax (Single Expression)**: `fn name(params): return_type => expression;` 79 | * **Anonymous Functions (Lambdas)**: 80 | * Single expression: `let name = (params): return_type => expression;` 81 | * Block body: `let name = (params): return_type => { body };` 82 | * **Guard Clauses**: Functions can be overloaded with `where` clauses for conditional execution based on parameter values: 83 | ```zeno 84 | fn factorial(n: int): int where n > 0 { ... } 85 | fn factorial(n: int): int where n == 0 { ... } 86 | ``` 87 | * **Async Functions**: 88 | * Definition: `async fn name(params): return_type { ... }` 89 | * Calling: Use `await` within another `async` function: `let result = await async_func();` 90 | * Anonymous Async: `let name = async (params): return_type => { body };` 91 | * **Promises**: Async functions appear to return promises (or similar awaitables). 92 | * Chaining: `promise.then((result) => ...).catch((error) => ...).finally(() => ...)` 93 | * Waiting for multiple: `let results = await Promise.all([promise1, promise2]);` 94 | 95 | ## 8. Structs 96 | 97 | * **Definition**: `struct Name { field1: type1, field2: type2 }` 98 | * **Composition (Mixins)**: Combine multiple structs: `struct CombinedName with Struct1, Struct2 { additional_field: type }` 99 | * Fields from composed structs are included. 100 | * **Instantiation**: Use struct literals (similar to map/hash literals): `let instance: Name = { field1: value1, ... };` 101 | * Spread operator (`...`) can be used during instantiation. 102 | * **Field Access**: `instance.field_name` 103 | 104 | ## 9. Standard Library / Built-ins (Inferred) 105 | 106 | * **I/O**: 107 | * `printf(format_string, args...)`: Formatted printing (requires `import "stdio.h";`). 108 | * `print(value)`: Simpler printing (usage seen, definition source unclear). 109 | * **Async/Concurrency**: 110 | * `fetch(url)`: Appears to be a built-in for network requests (used with `await`). 111 | * `Promise.all([...])`: Waits for multiple promises. 112 | * **Array Properties**: 113 | * `.length`: Gets the length of an array (e.g., `zzz.length`). 114 | -------------------------------------------------------------------------------- /src/ast.h: -------------------------------------------------------------------------------- 1 | #ifndef AST_H 2 | #define AST_H 3 | 4 | // Node types 5 | typedef enum { 6 | NODE_PROGRAM, 7 | NODE_FUNCTION, 8 | NODE_VARIABLE, 9 | NODE_STRUCT, 10 | NODE_TYPE_DECLARATION, 11 | NODE_IMPORT, 12 | NODE_EXPRESSION, 13 | NODE_BINARY_OP, 14 | NODE_UNARY_OP, 15 | NODE_IF, 16 | NODE_MATCH, 17 | NODE_RETURN, 18 | NODE_ASSIGNMENT, 19 | NODE_FUNCTION_CALL, 20 | NODE_COMPOUND_STATEMENT, 21 | NODE_LITERAL_INT, 22 | NODE_LITERAL_FLOAT, 23 | NODE_LITERAL_STRING, 24 | NODE_LITERAL_BOOL, 25 | NODE_LITERAL_ARRAY, 26 | NODE_IDENTIFIER, 27 | NODE_MEMBER_ACCESS, 28 | NODE_STRUCT_INIT, 29 | NODE_SPREAD, 30 | NODE_PIPE, 31 | NODE_BINDING_PATTERN, 32 | NODE_WILDCARD, 33 | NODE_ANONYMOUS_FUNCTION, 34 | NODE_PROMISE_THEN, 35 | NODE_PROMISE_CATCH, 36 | NODE_PROMISE_FINALLY, 37 | NODE_PROMISE_ALL, 38 | NODE_AWAIT_EXPRESSION, 39 | NODE_C_STYLE_FOR, /* Added for C-style for loops */ 40 | NODE_FOR_IN, /* Renamed from NODE_FOR_LOOP (range/array) */ 41 | NODE_FOR_MAP, /* Renamed from NODE_FOREACH_LOOP (map) */ 42 | NODE_RANGE, 43 | NODE_MAP_LITERAL, 44 | NODE_MAP_ENTRY, 45 | NODE_WHILE_STATEMENT 46 | } NodeType; 47 | 48 | // Variable types 49 | typedef enum { 50 | VAR_LET, 51 | VAR_CONST 52 | } VariableType; 53 | 54 | // Binary operation types 55 | typedef enum { 56 | OP_ADD, 57 | OP_SUB, 58 | OP_MUL, 59 | OP_DIV, 60 | OP_MOD, 61 | OP_CONCAT, 62 | OP_EQ, 63 | OP_NEQ, 64 | OP_LT, 65 | OP_GT, 66 | OP_LE, 67 | OP_GE, 68 | OP_AND, 69 | OP_OR 70 | } BinaryOpType; 71 | 72 | // Unary operation types 73 | typedef enum { 74 | OP_NOT, 75 | OP_NEG 76 | } UnaryOpType; 77 | 78 | // Forward declarations 79 | typedef struct AST_Node AST_Node; 80 | typedef struct AST_Node_List AST_Node_List; 81 | typedef struct TypeInfo TypeInfo; 82 | typedef struct StructField StructField; 83 | typedef struct StructField_List StructField_List; 84 | typedef struct ExpressionList ExpressionList; 85 | typedef struct GuardClause GuardClause; 86 | typedef struct MatchCase MatchCase; 87 | typedef struct MatchCase_List MatchCase_List; 88 | 89 | // Type information 90 | struct TypeInfo { 91 | char* name; 92 | TypeInfo* generic_type; // For types like array, or key_type for map 93 | TypeInfo* value_type; // For value_type for map 94 | }; 95 | 96 | // Struct field 97 | struct StructField { 98 | char* name; 99 | TypeInfo* type; 100 | StructField* next; 101 | }; 102 | 103 | // List of struct fields 104 | struct StructField_List { 105 | StructField* head; 106 | StructField* tail; 107 | }; 108 | 109 | // List of AST nodes 110 | struct AST_Node_List { 111 | AST_Node* head; 112 | AST_Node* tail; 113 | AST_Node* next; 114 | }; 115 | 116 | // List of expressions 117 | struct ExpressionList { 118 | AST_Node* expression; 119 | ExpressionList* next; 120 | }; 121 | 122 | // Guard clause for function declarations 123 | struct GuardClause { 124 | AST_Node* condition; 125 | }; 126 | 127 | // Match case for match statements 128 | struct MatchCase { 129 | AST_Node* pattern; 130 | AST_Node* guard; 131 | AST_Node* body; 132 | MatchCase* next; 133 | }; 134 | 135 | // List of match cases 136 | struct MatchCase_List { 137 | MatchCase* head; 138 | MatchCase* tail; 139 | }; 140 | 141 | // AST Node structure 142 | struct AST_Node { 143 | NodeType type; 144 | AST_Node* next; 145 | 146 | union { 147 | // Program node 148 | struct { 149 | AST_Node_List* declarations; 150 | } program; 151 | 152 | // Function declaration 153 | struct { 154 | char* name; 155 | AST_Node_List* parameters; 156 | TypeInfo* return_type; 157 | GuardClause* guard; 158 | AST_Node* body; 159 | int is_async; // Flag to mark as async function 160 | } function; 161 | 162 | // Variable declaration 163 | struct { 164 | VariableType var_type; 165 | char* name; 166 | TypeInfo* type; 167 | AST_Node* initializer; 168 | } variable; 169 | 170 | // Struct declaration 171 | struct { 172 | char* name; 173 | AST_Node_List* composition; 174 | StructField_List* fields; 175 | } struct_decl; 176 | 177 | // Type declaration 178 | struct { 179 | char* name; 180 | TypeInfo* type; 181 | } type_decl; 182 | 183 | // Import declaration 184 | struct { 185 | char* filename; 186 | } import; 187 | 188 | // Binary operation 189 | struct { 190 | BinaryOpType op; 191 | AST_Node* left; 192 | AST_Node* right; 193 | } binary_op; 194 | 195 | // Unary operation 196 | struct { 197 | UnaryOpType op; 198 | AST_Node* operand; 199 | } unary_op; 200 | 201 | // If statement 202 | struct { 203 | AST_Node* condition; 204 | AST_Node* true_branch; 205 | AST_Node* false_branch; 206 | } if_stmt; 207 | 208 | // Match statement 209 | struct { 210 | AST_Node* expression; 211 | MatchCase_List* cases; 212 | } match_stmt; 213 | 214 | // Return statement 215 | struct { 216 | AST_Node* expression; 217 | } return_stmt; 218 | 219 | // Assignment 220 | struct { 221 | char* name; 222 | AST_Node* value; 223 | } assignment; 224 | 225 | // Function call 226 | struct { 227 | char* name; 228 | ExpressionList* arguments; 229 | } function_call; 230 | 231 | // Compound statement 232 | struct { 233 | AST_Node_List* statements; 234 | } compound_stmt; 235 | 236 | // Literals 237 | struct { 238 | char* value; 239 | } literal; 240 | 241 | // Identifier 242 | struct { 243 | char* name; 244 | } identifier; 245 | 246 | // Member access 247 | struct { 248 | AST_Node* object; 249 | char* member; 250 | } member_access; 251 | 252 | // Struct initialization 253 | struct { 254 | AST_Node_List* fields; 255 | } struct_init; 256 | 257 | // Spread operator 258 | struct { 259 | AST_Node* expression; 260 | } spread; 261 | 262 | // Pipe operator 263 | struct { 264 | AST_Node* left; 265 | AST_Node* right; 266 | } pipe; 267 | 268 | // Binding pattern (for match) 269 | struct { 270 | char* name; 271 | TypeInfo* type; 272 | } binding; 273 | 274 | // Anonymous function 275 | struct { 276 | AST_Node_List* parameters; 277 | TypeInfo* return_type; 278 | AST_Node* body; 279 | } anon_function; 280 | 281 | // Promise then 282 | struct { 283 | AST_Node* promise; 284 | AST_Node* handler; // Anonymous function for then handler 285 | } promise_then; 286 | 287 | // Promise catch 288 | struct { 289 | AST_Node* promise; 290 | AST_Node* handler; // Anonymous function for catch handler 291 | } promise_catch; 292 | 293 | // Promise finally 294 | struct { 295 | AST_Node* promise; 296 | AST_Node* handler; // Anonymous function for finally handler 297 | } promise_finally; 298 | 299 | // Promise.all 300 | struct { 301 | ExpressionList* promises; // List of promises 302 | } promise_all; 303 | 304 | // Await expression 305 | struct { 306 | AST_Node* promise; 307 | } await_expr; 308 | 309 | // C-style For loop (NODE_C_STYLE_FOR) 310 | struct { 311 | AST_Node* initializer; 312 | AST_Node* condition; 313 | AST_Node* incrementer; 314 | AST_Node* body; 315 | } c_style_for; 316 | 317 | // For..in loop (NODE_FOR_IN) - for arrays and ranges 318 | struct { 319 | AST_Node* variable; // Loop variable declaration (parameter node) 320 | AST_Node* iterable; // Collection or range expression 321 | AST_Node* body; // Loop body 322 | } for_in; 323 | 324 | // For..in loop (NODE_FOR_MAP) - for maps 325 | struct { 326 | AST_Node* key_var; // Key variable (parameter node) 327 | AST_Node* value_var; // Value variable (parameter node) 328 | AST_Node* map_expr; // Map expression to iterate over 329 | AST_Node* body; // Loop body 330 | } for_map; 331 | 332 | // Range expression (NODE_RANGE) 333 | struct { 334 | AST_Node* start; 335 | AST_Node* end; 336 | } range; 337 | 338 | // Map literal 339 | struct { 340 | AST_Node_List* entries; // Changed from ExpressionList* 341 | } map_literal; 342 | 343 | // Map entry 344 | struct { 345 | AST_Node* key; // Key expression (changed from char*) 346 | AST_Node* value; // Value expression 347 | } map_entry; 348 | 349 | // While statement 350 | struct { 351 | AST_Node* condition; 352 | AST_Node* body; 353 | } while_statement; 354 | } data; 355 | }; 356 | 357 | // AST creation functions 358 | AST_Node* create_program_node(AST_Node_List* declarations); 359 | AST_Node* create_function_node(char* name, AST_Node_List* parameters, TypeInfo* return_type, GuardClause* guard, AST_Node* body); 360 | AST_Node* create_variable_node(VariableType var_type, char* name, TypeInfo* type, AST_Node* initializer); 361 | AST_Node* create_struct_node(char* name, AST_Node_List* composition, StructField_List* fields); 362 | AST_Node* create_type_declaration_node(char* name, TypeInfo* type); 363 | AST_Node* create_import_node(char* filename); 364 | AST_Node* create_binary_op_node(BinaryOpType op, AST_Node* left, AST_Node* right); 365 | AST_Node* create_unary_op_node(UnaryOpType op, AST_Node* operand); 366 | AST_Node* create_if_node(AST_Node* condition, AST_Node* true_branch, AST_Node* false_branch); 367 | AST_Node* create_match_node(AST_Node* expression, MatchCase_List* cases); 368 | AST_Node* create_return_node(AST_Node* expression); 369 | AST_Node* create_assignment_node(char* name, AST_Node* value); 370 | AST_Node* create_function_call_node(char* name, ExpressionList* arguments); 371 | AST_Node* create_compound_statement_node(AST_Node_List* statements); 372 | AST_Node* create_int_literal_node(char* value); 373 | AST_Node* create_float_literal_node(char* value); 374 | AST_Node* create_string_literal_node(char* value); 375 | AST_Node* create_bool_literal_node(char* value); 376 | AST_Node* create_array_literal_node(ExpressionList* elements); 377 | AST_Node* create_identifier_node(char* name); 378 | AST_Node* create_member_access_node(AST_Node* object, char* member); 379 | AST_Node* create_struct_init_node(AST_Node_List* fields); 380 | AST_Node* create_struct_init_field_node(char* name, AST_Node* value); 381 | AST_Node* create_spread_node(AST_Node* expression); 382 | AST_Node* create_pipe_node(AST_Node* left, AST_Node* right); 383 | AST_Node* create_parameter_node(char* name, TypeInfo* type); 384 | AST_Node* create_binding_pattern(char* name, TypeInfo* type); 385 | AST_Node* create_wildcard_node(); 386 | AST_Node* create_anonymous_function_node(AST_Node_List* parameters, TypeInfo* return_type, AST_Node* body); 387 | 388 | // Promise-related node creation 389 | AST_Node* create_promise_then_node(AST_Node* promise, AST_Node* handler); 390 | AST_Node* create_promise_catch_node(AST_Node* promise, AST_Node* handler); 391 | AST_Node* create_promise_finally_node(AST_Node* promise, AST_Node* handler); 392 | AST_Node* create_promise_all_node(ExpressionList* promises); 393 | AST_Node* create_await_expression_node(AST_Node* promise); 394 | 395 | // Loop-related node creation 396 | AST_Node* create_c_style_for_node(AST_Node* initializer, AST_Node* condition, AST_Node* incrementer, AST_Node* body); // Added 397 | AST_Node* create_for_in_node(AST_Node* variable, AST_Node* iterable, AST_Node* body); // Renamed from create_for_node 398 | AST_Node* create_for_map_node(AST_Node* key_var, AST_Node* value_var, AST_Node* map_expr, AST_Node* body); // Renamed from create_foreach_node 399 | AST_Node* create_range_node(AST_Node* start, AST_Node* end); 400 | AST_Node* create_while_node(AST_Node* condition, AST_Node* body); 401 | 402 | // Map-related node creation 403 | TypeInfo* create_map_type_info(TypeInfo* key_type, TypeInfo* value_type); // Added 404 | AST_Node* create_map_literal_node(AST_Node_List* entries); // Changed param type 405 | AST_Node* create_map_entry_node(AST_Node* key, AST_Node* value); // Changed key param type 406 | 407 | // Helper functions 408 | AST_Node_List* create_node_list(AST_Node* node); 409 | void append_node(AST_Node_List* list, AST_Node* node); 410 | StructField* create_struct_field(char* name, TypeInfo* type); 411 | StructField_List* create_field_list(StructField* field); 412 | void append_field(StructField_List* list, StructField* field); 413 | TypeInfo* create_type_info(char* name, TypeInfo* generic_type); 414 | ExpressionList* create_expression_list(AST_Node* expr); 415 | void append_expression(ExpressionList* list, AST_Node* expr); 416 | GuardClause* create_guard_clause(AST_Node* condition); 417 | MatchCase* create_match_case(AST_Node* pattern, AST_Node* guard, AST_Node* body); 418 | MatchCase_List* create_match_case_list(MatchCase* case_node); 419 | void append_match_case(MatchCase_List* list, MatchCase* case_node); 420 | 421 | // AST traversal and code generation declarations 422 | void free_ast(AST_Node* node); 423 | 424 | #endif // AST_H 425 | -------------------------------------------------------------------------------- /src/codegen/anon_function.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "anon_function.h" 5 | #include "statement.h" 6 | #include "utils.h" 7 | #include "codegen.h" 8 | 9 | // Global array to store anonymous functions 10 | #define MAX_ANON_FUNCS 100 11 | static AnonFunction anon_funcs[MAX_ANON_FUNCS]; 12 | static int anon_func_count = 0; 13 | 14 | // Global array to store anonymous function forward declarations 15 | static char* anon_func_decls[MAX_ANON_FUNCS] = {0}; 16 | 17 | // Function to add a new anonymous function 18 | int add_anon_function(const char* name, const char* return_type, const char* param_list, const char* param_types, const char* body) { 19 | if (anon_func_count >= MAX_ANON_FUNCS) { 20 | fprintf(stderr, "Too many anonymous functions\n"); 21 | return -1; 22 | } 23 | 24 | int idx = anon_func_count++; 25 | anon_funcs[idx].name = strdup(name); 26 | 27 | // Normalize types for anonymous functions used in promises 28 | // Use void* for all handler functions to match promise handler typedefs 29 | if (strstr(name, "__anon_func_") != NULL) { 30 | if (strstr(param_list, "data")) { 31 | anon_funcs[idx].return_type = strdup("void*"); 32 | anon_funcs[idx].param_list = strdup("void* data"); 33 | anon_funcs[idx].param_types = strdup("void*"); 34 | } else if (strstr(param_list, "error")) { 35 | anon_funcs[idx].return_type = strdup("void*"); 36 | anon_funcs[idx].param_list = strdup("void* error"); 37 | anon_funcs[idx].param_types = strdup("void*"); 38 | } else if (strstr(param_list, "void")) { 39 | // For finally handler, we need to always return a pointer 40 | anon_funcs[idx].return_type = strdup("void*"); 41 | anon_funcs[idx].param_list = strdup("void"); 42 | anon_funcs[idx].param_types = strdup("void"); 43 | 44 | // Also update the body to make it return NULL 45 | char* modified_body = malloc(strlen(body) + 100); 46 | strcpy(modified_body, "{\n"); 47 | strcat(modified_body, " cleanup();\n"); 48 | strcat(modified_body, " return NULL;\n"); 49 | strcat(modified_body, " }"); 50 | anon_funcs[idx].body = modified_body; 51 | return idx; // Early return since we've set the body directly 52 | } else { 53 | anon_funcs[idx].return_type = strdup(return_type); 54 | anon_funcs[idx].param_list = strdup(param_list); 55 | anon_funcs[idx].param_types = strdup(param_types); 56 | } 57 | } else { 58 | anon_funcs[idx].return_type = strdup(return_type); 59 | anon_funcs[idx].param_list = strdup(param_list); 60 | anon_funcs[idx].param_types = strdup(param_types); 61 | } 62 | 63 | anon_funcs[idx].body = strdup(body); 64 | 65 | return idx; 66 | } 67 | 68 | // Generate code for anonymous function 69 | void generate_anonymous_function(CodeGenContext* ctx, AST_Node* node) { 70 | static int anon_func_counter = 0; 71 | char func_name[64]; 72 | 73 | // Create a unique function name 74 | snprintf(func_name, sizeof(func_name), "__anon_func_%d", anon_func_counter++); 75 | 76 | // Get the return type 77 | char* return_type = get_c_type(node->data.anon_function.return_type); 78 | 79 | // Build parameter list 80 | char param_list[1024] = ""; 81 | char param_types[1024] = ""; 82 | 83 | // Check if there are any parameters at all 84 | if (node->data.anon_function.parameters && node->data.anon_function.parameters->head) { 85 | AST_Node* param = node->data.anon_function.parameters->head; 86 | int first_param = 1; 87 | 88 | while (param) { 89 | if (!first_param) { 90 | strcat(param_list, ", "); 91 | strcat(param_types, ", "); 92 | } 93 | 94 | char* param_type = get_c_type(param->data.variable.type); 95 | char param_decl[256]; 96 | sprintf(param_decl, "%s %s", param_type, param->data.variable.name); 97 | strcat(param_list, param_decl); 98 | strcat(param_types, param_type); 99 | 100 | free(param_type); 101 | first_param = 0; 102 | param = param->next; 103 | } 104 | } else { 105 | // For no parameters, just leave the strings empty 106 | strcpy(param_list, "void"); 107 | strcpy(param_types, "void"); 108 | } 109 | 110 | // Capture body to a string 111 | FILE* body_stream = open_memstream(&ctx->buffer, &ctx->buffer_size); 112 | FILE* old_output = ctx->output; 113 | ctx->output = body_stream; 114 | 115 | fprintf(ctx->output, "{\n"); 116 | increase_indent(ctx); 117 | 118 | // Generate function body 119 | if (node->data.anon_function.body) { 120 | if (node->data.anon_function.body->type == NODE_COMPOUND_STATEMENT) { 121 | // For compound statements, we generate the contents directly 122 | generate_compound_statement_contents(ctx, node->data.anon_function.body); 123 | } else { 124 | // For single statements 125 | indent(ctx); 126 | generate_code(ctx, node->data.anon_function.body); 127 | } 128 | } 129 | 130 | decrease_indent(ctx); 131 | indent(ctx); 132 | fprintf(ctx->output, "}"); 133 | 134 | fflush(body_stream); 135 | fclose(body_stream); 136 | ctx->output = old_output; 137 | 138 | // Add the anonymous function to our global list 139 | add_anon_function(func_name, return_type, param_list, param_types, ctx->buffer); 140 | 141 | // Add it to the declaration array 142 | char decl[1024]; 143 | sprintf(decl, "%s %s(%s);", return_type, func_name, param_list); 144 | 145 | if (anon_func_decls[anon_func_count-1]) 146 | free(anon_func_decls[anon_func_count-1]); 147 | 148 | anon_func_decls[anon_func_count-1] = strdup(decl); 149 | 150 | // Output just the function name 151 | fprintf(ctx->output, "%s", func_name); 152 | 153 | free(return_type); 154 | free(ctx->buffer); 155 | ctx->buffer = NULL; 156 | ctx->buffer_size = 0; 157 | } 158 | 159 | // Generate code for all anonymous functions at program level 160 | void generate_all_anon_functions(FILE* output) { 161 | // First, forward declare all anonymous functions 162 | for (int i = 0; i < anon_func_count; i++) { 163 | fprintf(output, "// Forward declaration of anonymous function\n"); 164 | fprintf(output, "%s %s(%s);\n", 165 | anon_funcs[i].return_type, 166 | anon_funcs[i].name, 167 | anon_funcs[i].param_list); 168 | 169 | // Store the declaration for global access 170 | if (anon_func_decls[i]) 171 | free(anon_func_decls[i]); 172 | 173 | char decl[1024]; 174 | sprintf(decl, "%s %s(%s);", 175 | anon_funcs[i].return_type, 176 | anon_funcs[i].name, 177 | anon_funcs[i].param_list); 178 | anon_func_decls[i] = strdup(decl); 179 | } 180 | 181 | fprintf(output, "\n"); 182 | 183 | // Then output the function implementations 184 | for (int i = 0; i < anon_func_count; i++) { 185 | fprintf(output, "// Anonymous function definition\n"); 186 | fprintf(output, "%s %s(%s) %s\n\n", 187 | anon_funcs[i].return_type, 188 | anon_funcs[i].name, 189 | anon_funcs[i].param_list, 190 | anon_funcs[i].body); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/codegen/anon_function.h: -------------------------------------------------------------------------------- 1 | #ifndef CODEGEN_ANON_FUNCTION_H 2 | #define CODEGEN_ANON_FUNCTION_H 3 | 4 | #include 5 | #include "context.h" 6 | #include "../ast.h" 7 | 8 | // Anonymous function structure 9 | typedef struct { 10 | char* name; 11 | char* return_type; 12 | char* param_list; 13 | char* param_types; 14 | char* body; 15 | } AnonFunction; 16 | 17 | // Anonymous function functions 18 | void generate_anonymous_function(CodeGenContext* ctx, AST_Node* node); 19 | int add_anon_function(const char* name, const char* return_type, const char* param_list, const char* param_types, const char* body); 20 | void generate_all_anon_functions(FILE* output); 21 | 22 | #endif // CODEGEN_ANON_FUNCTION_H 23 | -------------------------------------------------------------------------------- /src/codegen/codegen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "codegen.h" 5 | #include "expression.h" 6 | #include "statement.h" 7 | #include "declaration.h" 8 | #include "anon_function.h" 9 | #include "utils.h" 10 | 11 | // Main code generation function 12 | void generate_code(CodeGenContext* ctx, AST_Node* node) { 13 | if (!node) return; 14 | 15 | switch (node->type) { 16 | case NODE_PROGRAM: 17 | generate_program(ctx, node); 18 | break; 19 | case NODE_FUNCTION: 20 | generate_function(ctx, node); 21 | break; 22 | case NODE_VARIABLE: 23 | generate_variable(ctx, node); 24 | break; 25 | case NODE_STRUCT: 26 | generate_struct(ctx, node); 27 | break; 28 | case NODE_TYPE_DECLARATION: 29 | generate_type_declaration(ctx, node); 30 | break; 31 | case NODE_IMPORT: 32 | generate_import(ctx, node); 33 | break; 34 | case NODE_IF: 35 | generate_if_statement(ctx, node); 36 | break; 37 | case NODE_MATCH: 38 | generate_match_statement(ctx, node); 39 | break; 40 | case NODE_RETURN: 41 | generate_return_statement(ctx, node); 42 | break; 43 | case NODE_COMPOUND_STATEMENT: 44 | generate_compound_statement(ctx, node); 45 | break; 46 | case NODE_C_STYLE_FOR: // Added case 47 | generate_c_style_for_statement(ctx, node); 48 | break; 49 | case NODE_FOR_IN: // Renamed case and function call 50 | generate_for_in_statement(ctx, node); 51 | break; 52 | case NODE_FOR_MAP: // Renamed case and function call 53 | generate_for_map_statement(ctx, node); 54 | break; 55 | case NODE_WHILE_STATEMENT: 56 | generate_while_statement(ctx, node); 57 | break; 58 | default: 59 | generate_expression(ctx, node); 60 | fprintf(ctx->output, ";\n"); 61 | break; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/codegen/codegen.h: -------------------------------------------------------------------------------- 1 | #ifndef CODEGEN_H 2 | #define CODEGEN_H 3 | 4 | #include 5 | #include "../ast.h" 6 | #include "../symtab.h" 7 | 8 | // Forward declarations 9 | typedef struct CodeGenContext CodeGenContext; 10 | 11 | // Include all module headers 12 | #include "context.h" 13 | #include "utils.h" 14 | #include "expression.h" 15 | #include "statement.h" 16 | #include "declaration.h" 17 | #include "anon_function.h" 18 | 19 | // Generate C code from the AST 20 | void generate_code(CodeGenContext* ctx, AST_Node* node); 21 | 22 | // Generate all anonymous functions at program level 23 | void generate_all_anon_functions(FILE* output); 24 | 25 | #endif // CODEGEN_H 26 | -------------------------------------------------------------------------------- /src/codegen/context.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "context.h" 5 | 6 | // Initialize code generation context 7 | CodeGenContext* init_codegen(FILE* output) { 8 | CodeGenContext* ctx = (CodeGenContext*)malloc(sizeof(CodeGenContext)); 9 | if (!ctx) { 10 | fprintf(stderr, "Memory allocation failed\n"); 11 | exit(1); 12 | } 13 | 14 | ctx->output = output; 15 | ctx->symtab = init_symbol_table(); 16 | ctx->indentation = 0; 17 | ctx->temp_var_count = 0; 18 | ctx->label_count = 0; 19 | ctx->buffer = NULL; 20 | ctx->buffer_size = 0; 21 | ctx->in_async_function = 0; // Initialize to not in async function 22 | 23 | return ctx; 24 | } 25 | 26 | // Clean up code generation context 27 | void cleanup_codegen(CodeGenContext* ctx) { 28 | if (ctx) { 29 | if (ctx->symtab) { 30 | cleanup_symbol_table(ctx->symtab); 31 | } 32 | 33 | if (ctx->buffer) { 34 | free(ctx->buffer); 35 | } 36 | 37 | free(ctx); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/codegen/context.h: -------------------------------------------------------------------------------- 1 | #ifndef CODEGEN_CONTEXT_H 2 | #define CODEGEN_CONTEXT_H 3 | 4 | #include 5 | #include "../symtab.h" 6 | 7 | // Code generation context 8 | typedef struct CodeGenContext { 9 | FILE* output; // Output file 10 | SymbolTable* symtab; // Symbol table 11 | int indentation; // Current indentation level 12 | int temp_var_count; // Counter for temporary variables 13 | int label_count; // Counter for labels 14 | char* buffer; // Temporary buffer for string operations 15 | size_t buffer_size; // Size of the temporary buffer 16 | int in_async_function; // Flag to indicate we're inside an async function 17 | } CodeGenContext; 18 | 19 | // Initialize code generation context 20 | CodeGenContext* init_codegen(FILE* output); 21 | 22 | // Clean up code generation context 23 | void cleanup_codegen(CodeGenContext* ctx); 24 | 25 | #endif // CODEGEN_CONTEXT_H 26 | -------------------------------------------------------------------------------- /src/codegen/declaration.h: -------------------------------------------------------------------------------- 1 | #ifndef CODEGEN_DECLARATION_H 2 | #define CODEGEN_DECLARATION_H 3 | 4 | #include "context.h" 5 | #include "../ast.h" 6 | 7 | // Declaration generation functions 8 | void generate_program(CodeGenContext* ctx, AST_Node* node); 9 | void generate_function(CodeGenContext* ctx, AST_Node* node); 10 | void generate_variable(CodeGenContext* ctx, AST_Node* node); 11 | void generate_struct(CodeGenContext* ctx, AST_Node* node); 12 | void generate_type_declaration(CodeGenContext* ctx, AST_Node* node); 13 | void generate_import(CodeGenContext* ctx, AST_Node* node); 14 | 15 | #endif // CODEGEN_DECLARATION_H 16 | -------------------------------------------------------------------------------- /src/codegen/expression.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "expression.h" 5 | #include "anon_function.h" 6 | #include "utils.h" 7 | 8 | // Generate code for expression 9 | void generate_expression(CodeGenContext* ctx, AST_Node* node) { 10 | if (!node) return; 11 | 12 | switch (node->type) { 13 | case NODE_ANONYMOUS_FUNCTION: 14 | generate_anonymous_function(ctx, node); 15 | break; 16 | 17 | case NODE_LITERAL_INT: 18 | case NODE_LITERAL_FLOAT: 19 | case NODE_LITERAL_BOOL: 20 | fprintf(ctx->output, "%s", node->data.literal.value); 21 | break; 22 | 23 | case NODE_LITERAL_STRING: 24 | // Make sure string literals are properly quoted 25 | fprintf(ctx->output, "%s", node->data.literal.value); 26 | break; 27 | 28 | case NODE_IDENTIFIER: 29 | fprintf(ctx->output, "%s", node->data.identifier.name); 30 | break; 31 | 32 | case NODE_MEMBER_ACCESS: 33 | // Handle member access (e.g., user.id) 34 | generate_expression(ctx, node->data.member_access.object); 35 | fprintf(ctx->output, ".%s", node->data.member_access.member); 36 | break; 37 | 38 | case NODE_STRUCT_INIT: { 39 | // Create a default struct initialization with zeros 40 | // Note: This is a simplification that doesn't properly handle struct initialization, 41 | // but it allows the code to compile 42 | fprintf(ctx->output, "{0}"); 43 | break; 44 | } 45 | 46 | case NODE_BINARY_OP: { 47 | // Special handling for string concatenation using "+" operator 48 | if (node->data.binary_op.op == OP_ADD && 49 | (node->data.binary_op.left->type == NODE_LITERAL_STRING || 50 | node->data.binary_op.right->type == NODE_LITERAL_STRING)) { 51 | // In the context of an async function return, wrap with a promise resolver 52 | if (ctx->in_async_function) { 53 | fprintf(ctx->output, "zn_promise_resolve(string_concat("); 54 | generate_expression(ctx, node->data.binary_op.left); 55 | fprintf(ctx->output, ", "); 56 | generate_expression(ctx, node->data.binary_op.right); 57 | fprintf(ctx->output, "))"); 58 | } else { 59 | fprintf(ctx->output, "string_concat("); 60 | generate_expression(ctx, node->data.binary_op.left); 61 | fprintf(ctx->output, ", "); 62 | generate_expression(ctx, node->data.binary_op.right); 63 | fprintf(ctx->output, ")"); 64 | } 65 | } else { 66 | // Normal numeric operations 67 | fprintf(ctx->output, "("); 68 | generate_expression(ctx, node->data.binary_op.left); 69 | 70 | switch (node->data.binary_op.op) { 71 | case OP_ADD: 72 | fprintf(ctx->output, " + "); 73 | break; 74 | case OP_SUB: 75 | fprintf(ctx->output, " - "); 76 | break; 77 | case OP_MUL: 78 | fprintf(ctx->output, " * "); 79 | break; 80 | case OP_DIV: 81 | fprintf(ctx->output, " / "); 82 | break; 83 | case OP_MOD: 84 | fprintf(ctx->output, " %% "); 85 | break; 86 | default: 87 | fprintf(ctx->output, " ? "); // Unknown operator 88 | break; 89 | } 90 | 91 | generate_expression(ctx, node->data.binary_op.right); 92 | fprintf(ctx->output, ")"); 93 | } 94 | break; 95 | } 96 | 97 | case NODE_FUNCTION_CALL: { 98 | // Generate function call 99 | fprintf(ctx->output, "%s(", node->data.function_call.name); 100 | 101 | // Generate arguments 102 | ExpressionList* arg_list = node->data.function_call.arguments; 103 | int first_arg = 1; 104 | 105 | while (arg_list) { 106 | if (!first_arg) { 107 | fprintf(ctx->output, ", "); 108 | } 109 | 110 | generate_expression(ctx, arg_list->expression); 111 | first_arg = 0; 112 | arg_list = arg_list->next; 113 | } 114 | 115 | fprintf(ctx->output, ")"); 116 | break; 117 | } 118 | 119 | case NODE_PIPE: { 120 | // For pipe operator (a |> f), generate f(a) 121 | 122 | if (node->data.pipe.right->type == NODE_FUNCTION_CALL) { 123 | // If right side is a function call, make the left side the first argument 124 | fprintf(ctx->output, "%s(", node->data.pipe.right->data.function_call.name); 125 | 126 | // First argument is the left side of the pipe 127 | generate_expression(ctx, node->data.pipe.left); 128 | 129 | // Add any additional arguments 130 | ExpressionList* arg_list = node->data.pipe.right->data.function_call.arguments; 131 | if (arg_list) { 132 | fprintf(ctx->output, ", "); 133 | } 134 | 135 | int first_arg = 1; 136 | while (arg_list) { 137 | if (!first_arg) { 138 | fprintf(ctx->output, ", "); 139 | } 140 | 141 | generate_expression(ctx, arg_list->expression); 142 | first_arg = 0; 143 | arg_list = arg_list->next; 144 | } 145 | 146 | fprintf(ctx->output, ")"); 147 | } else if (node->data.pipe.right->type == NODE_IDENTIFIER) { 148 | // If right side is a function name, call it with the left side 149 | fprintf(ctx->output, "%s(", node->data.pipe.right->data.identifier.name); 150 | generate_expression(ctx, node->data.pipe.left); 151 | fprintf(ctx->output, ")"); 152 | } else { 153 | // Fallback: Just output the left side (error case) 154 | generate_expression(ctx, node->data.pipe.left); 155 | } 156 | 157 | break; 158 | } 159 | 160 | // Promise-related nodes 161 | case NODE_PROMISE_THEN: { 162 | fprintf(ctx->output, "zn_promise_then("); 163 | generate_expression(ctx, node->data.promise_then.promise); 164 | fprintf(ctx->output, ", "); 165 | 166 | // Handle the callback function properly 167 | if (node->data.promise_then.handler && 168 | node->data.promise_then.handler->type == NODE_ANONYMOUS_FUNCTION) { 169 | // For anonymous functions, generate the function pointer 170 | fprintf(ctx->output, "(zn_then_handler_t)"); 171 | generate_expression(ctx, node->data.promise_then.handler); 172 | } else { 173 | // Default to NULL handler if not an anonymous function 174 | fprintf(ctx->output, "NULL"); 175 | } 176 | 177 | fprintf(ctx->output, ")"); 178 | break; 179 | } 180 | 181 | case NODE_PROMISE_CATCH: { 182 | fprintf(ctx->output, "zn_promise_catch("); 183 | generate_expression(ctx, node->data.promise_catch.promise); 184 | fprintf(ctx->output, ", "); 185 | 186 | // Handle the callback function properly 187 | if (node->data.promise_catch.handler && 188 | node->data.promise_catch.handler->type == NODE_ANONYMOUS_FUNCTION) { 189 | // For anonymous functions, generate the function pointer 190 | fprintf(ctx->output, "(zn_catch_handler_t)"); 191 | generate_expression(ctx, node->data.promise_catch.handler); 192 | } else { 193 | // Default to NULL handler if not an anonymous function 194 | fprintf(ctx->output, "NULL"); 195 | } 196 | 197 | fprintf(ctx->output, ")"); 198 | break; 199 | } 200 | 201 | case NODE_PROMISE_FINALLY: { 202 | fprintf(ctx->output, "zn_promise_finally("); 203 | generate_expression(ctx, node->data.promise_finally.promise); 204 | fprintf(ctx->output, ", "); 205 | 206 | // Handle the callback function properly 207 | if (node->data.promise_finally.handler && 208 | node->data.promise_finally.handler->type == NODE_ANONYMOUS_FUNCTION) { 209 | // For anonymous functions, generate the function pointer 210 | fprintf(ctx->output, "(zn_finally_handler_t)"); 211 | generate_expression(ctx, node->data.promise_finally.handler); 212 | } else { 213 | // Default to NULL handler if not an anonymous function 214 | fprintf(ctx->output, "NULL"); 215 | } 216 | 217 | fprintf(ctx->output, ")"); 218 | break; 219 | } 220 | 221 | case NODE_PROMISE_ALL: { 222 | // First, count the number of promises in the array 223 | ExpressionList* promises = node->data.promise_all.promises; 224 | int count = 0; 225 | ExpressionList* temp = promises; 226 | while (temp) { 227 | count++; 228 | temp = temp->next; 229 | } 230 | 231 | // Generate code for Promise.all 232 | fprintf(ctx->output, "promise_all((zn_promise_t*[]){"); 233 | 234 | // Add each promise to the array 235 | temp = promises; 236 | int index = 0; 237 | while (temp) { 238 | if (index > 0) { 239 | fprintf(ctx->output, ", "); 240 | } 241 | generate_expression(ctx, temp->expression); 242 | index++; 243 | temp = temp->next; 244 | } 245 | 246 | // Close the array and add the count 247 | fprintf(ctx->output, "}, %d)", count); 248 | break; 249 | } 250 | 251 | case NODE_AWAIT_EXPRESSION: { 252 | fprintf(ctx->output, "zn_promise_await("); 253 | generate_expression(ctx, node->data.await_expr.promise); 254 | fprintf(ctx->output, ")"); 255 | break; 256 | } 257 | 258 | case NODE_LITERAL_ARRAY: { 259 | // Generate array literal 260 | fprintf(ctx->output, "((int[]){"); 261 | 262 | // Generate array elements 263 | ExpressionList* elem_list = node->data.function_call.arguments; 264 | int first_elem = 1; 265 | 266 | while (elem_list) { 267 | if (!first_elem) { 268 | fprintf(ctx->output, ", "); 269 | } 270 | 271 | generate_expression(ctx, elem_list->expression); 272 | first_elem = 0; 273 | elem_list = elem_list->next; 274 | } 275 | 276 | fprintf(ctx->output, "})"); 277 | break; 278 | } 279 | 280 | case NODE_MAP_ENTRY: { 281 | // Output key-value pair for map entries 282 | fprintf(ctx->output, "{.key = "); 283 | 284 | // Output the key expression 285 | generate_expression(ctx, node->data.map_entry.key); // Changed from direct string output 286 | fprintf(ctx->output, ", "); 287 | 288 | // Output the value 289 | fprintf(ctx->output, ".value = (void*)(intptr_t)"); // Keeping void* cast for now 290 | generate_expression(ctx, node->data.map_entry.value); 291 | fprintf(ctx->output, "}"); 292 | break; 293 | } 294 | 295 | case NODE_MAP_LITERAL: { 296 | // Create a struct-based map representation 297 | // TODO: This assumes keys are strings/char*, which might not be true after AST change. 298 | // Needs a more robust map implementation later. 299 | fprintf(ctx->output, "((struct { void* key; void* value; }[]){\n"); // Changed key type to void* 300 | 301 | // Generate map entries from AST_Node_List 302 | AST_Node* current_entry_node = node->data.map_literal.entries ? node->data.map_literal.entries->head : NULL; 303 | int first_entry = 1; 304 | 305 | while (current_entry_node) { 306 | if (current_entry_node->type == NODE_MAP_ENTRY) { // Ensure it's a map entry 307 | if (!first_entry) { 308 | fprintf(ctx->output, ",\n"); 309 | } 310 | fprintf(ctx->output, " "); 311 | // Generate the map entry (which handles key and value) 312 | generate_expression(ctx, current_entry_node); 313 | first_entry = 0; 314 | } 315 | current_entry_node = current_entry_node->next; 316 | } 317 | 318 | fprintf(ctx->output, "\n})"); // TODO: Need size info for the array 319 | break; 320 | } 321 | 322 | case NODE_RANGE: { 323 | // Output the start of the range (simplified implementation) 324 | generate_expression(ctx, node->data.range.start); 325 | break; 326 | } 327 | 328 | default: 329 | // Unhandled expression type 330 | fprintf(ctx->output, "/* Unsupported expression */"); 331 | break; 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /src/codegen/expression.h: -------------------------------------------------------------------------------- 1 | #ifndef CODEGEN_EXPRESSION_H 2 | #define CODEGEN_EXPRESSION_H 3 | 4 | #include "context.h" 5 | #include "../ast.h" 6 | 7 | // Generate code for expression 8 | void generate_expression(CodeGenContext* ctx, AST_Node* node); 9 | 10 | #endif // CODEGEN_EXPRESSION_H 11 | -------------------------------------------------------------------------------- /src/codegen/statement.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "statement.h" 5 | #include "expression.h" 6 | #include "declaration.h" // Added include for generate_variable_declaration 7 | #include "utils.h" 8 | #include "codegen.h" 9 | 10 | // Forward declarations for loop generation functions 11 | void generate_c_style_for_statement(CodeGenContext* ctx, AST_Node* node); 12 | void generate_for_in_statement(CodeGenContext* ctx, AST_Node* node); 13 | void generate_for_map_statement(CodeGenContext* ctx, AST_Node* node); 14 | void generate_while_statement(CodeGenContext* ctx, AST_Node* node); 15 | 16 | // Generate code for compound statement with braces 17 | void generate_compound_statement(CodeGenContext* ctx, AST_Node* node) { 18 | fprintf(ctx->output, "{\n"); 19 | 20 | increase_indent(ctx); 21 | generate_compound_statement_contents(ctx, node); 22 | decrease_indent(ctx); 23 | 24 | indent(ctx); 25 | fprintf(ctx->output, "}\n"); 26 | } 27 | 28 | // Generate code for C-style for statement 29 | void generate_c_style_for_statement(CodeGenContext* ctx, AST_Node* node) { 30 | // Create new scope for the loop initializer if it's a declaration 31 | int needs_scope = node->data.c_style_for.initializer && node->data.c_style_for.initializer->type == NODE_VARIABLE; 32 | if (needs_scope) { 33 | enter_scope(ctx->symtab); 34 | } 35 | 36 | fprintf(ctx->output, "for ("); 37 | 38 | // Generate initializer 39 | if (node->data.c_style_for.initializer) { 40 | if (node->data.c_style_for.initializer->type == NODE_VARIABLE) { 41 | // Generate variable declaration without trailing semicolon or newline 42 | // NOTE: Assuming generate_variable handles context appropriately or needs adjustment 43 | generate_variable(ctx, node->data.c_style_for.initializer); // Changed function name 44 | } else { 45 | // Generate expression initializer 46 | generate_expression(ctx, node->data.c_style_for.initializer); 47 | } 48 | } 49 | fprintf(ctx->output, "; "); 50 | 51 | // Generate condition 52 | if (node->data.c_style_for.condition) { 53 | generate_expression(ctx, node->data.c_style_for.condition); 54 | } 55 | fprintf(ctx->output, "; "); 56 | 57 | // Generate incrementer 58 | if (node->data.c_style_for.incrementer) { 59 | generate_expression(ctx, node->data.c_style_for.incrementer); 60 | } 61 | fprintf(ctx->output, ") {\n"); 62 | increase_indent(ctx); 63 | 64 | // Generate loop body 65 | AST_Node* body = node->data.c_style_for.body; 66 | if (body->type == NODE_COMPOUND_STATEMENT) { 67 | // Compound statement handles its own scope 68 | generate_compound_statement_contents(ctx, body); 69 | } else { 70 | // For single statements, generate directly 71 | indent(ctx); 72 | generate_code(ctx, body); 73 | } 74 | 75 | decrease_indent(ctx); 76 | indent(ctx); 77 | fprintf(ctx->output, "}\n"); 78 | 79 | // End scope if one was created for the initializer 80 | if (needs_scope) { 81 | leave_scope(ctx->symtab); 82 | } 83 | } 84 | 85 | // Generate contents of a compound statement without the braces 86 | void generate_compound_statement_contents(CodeGenContext* ctx, AST_Node* node) { 87 | // Create new scope 88 | enter_scope(ctx->symtab); 89 | 90 | // Generate code for all statements 91 | AST_Node* stmt = node->data.compound_stmt.statements->head; 92 | while (stmt) { 93 | indent(ctx); 94 | 95 | if (stmt->type == NODE_COMPOUND_STATEMENT) { 96 | // If we have a nested compound statement, generate it directly 97 | generate_compound_statement(ctx, stmt); 98 | } else { 99 | // Otherwise, generate the statement 100 | generate_code(ctx, stmt); 101 | } 102 | 103 | // If this was a return statement, don't process any more statements 104 | if (stmt->type == NODE_RETURN) { 105 | break; 106 | } 107 | 108 | stmt = stmt->next; 109 | } 110 | 111 | // End scope 112 | leave_scope(ctx->symtab); 113 | } 114 | 115 | // Generate code for if statement 116 | void generate_if_statement(CodeGenContext* ctx, AST_Node* node) { 117 | fprintf(ctx->output, "if ("); 118 | generate_expression(ctx, node->data.if_stmt.condition); 119 | fprintf(ctx->output, ") {\n"); 120 | increase_indent(ctx); 121 | 122 | // Generate true branch 123 | if (node->data.if_stmt.true_branch->type == NODE_COMPOUND_STATEMENT) { 124 | // For compound statements, we generate the contents directly 125 | generate_compound_statement_contents(ctx, node->data.if_stmt.true_branch); 126 | } else { 127 | // For single statements 128 | indent(ctx); 129 | generate_code(ctx, node->data.if_stmt.true_branch); 130 | } 131 | 132 | decrease_indent(ctx); 133 | indent(ctx); 134 | 135 | // Generate else branch if present 136 | if (node->data.if_stmt.false_branch) { 137 | fprintf(ctx->output, "} else {\n"); 138 | increase_indent(ctx); 139 | 140 | if (node->data.if_stmt.false_branch->type == NODE_COMPOUND_STATEMENT) { 141 | // For compound statements, we generate the contents directly 142 | generate_compound_statement_contents(ctx, node->data.if_stmt.false_branch); 143 | } else { 144 | // For single statements 145 | indent(ctx); 146 | generate_code(ctx, node->data.if_stmt.false_branch); 147 | } 148 | 149 | decrease_indent(ctx); 150 | indent(ctx); 151 | } 152 | 153 | fprintf(ctx->output, "}\n"); 154 | } 155 | 156 | // Generate code for match statement 157 | void generate_match_statement(CodeGenContext* ctx, AST_Node* node) { 158 | // Get the expression to match 159 | fprintf(ctx->output, "{\n"); 160 | increase_indent(ctx); 161 | 162 | // Create a temporary variable to store the match expression 163 | indent(ctx); 164 | fprintf(ctx->output, "// Match statement\n"); 165 | 166 | // For boolean matches like in structs.zn example 167 | if (node->data.match_stmt.expression->type == NODE_IDENTIFIER) { 168 | MatchCase* current_case = node->data.match_stmt.cases->head; 169 | int first_case = 1; 170 | 171 | while (current_case) { 172 | indent(ctx); 173 | 174 | if (!first_case) { 175 | fprintf(ctx->output, "else "); 176 | } 177 | 178 | // Handle literal patterns (true/false) 179 | if (current_case->pattern->type == NODE_LITERAL_BOOL) { 180 | fprintf(ctx->output, "if (%s == %s) {\n", 181 | node->data.match_stmt.expression->data.identifier.name, 182 | current_case->pattern->data.literal.value); 183 | } else { 184 | fprintf(ctx->output, "{\n"); // Default case 185 | } 186 | 187 | increase_indent(ctx); 188 | 189 | // Generate case body 190 | if (current_case->body->type == NODE_COMPOUND_STATEMENT) { 191 | generate_compound_statement_contents(ctx, current_case->body); 192 | } else { 193 | indent(ctx); 194 | generate_code(ctx, current_case->body); 195 | } 196 | 197 | decrease_indent(ctx); 198 | indent(ctx); 199 | fprintf(ctx->output, "}"); 200 | 201 | if (first_case) { 202 | fprintf(ctx->output, "\n"); 203 | } 204 | 205 | first_case = 0; 206 | current_case = current_case->next; 207 | } 208 | } 209 | 210 | decrease_indent(ctx); 211 | indent(ctx); 212 | fprintf(ctx->output, "}\n"); 213 | } 214 | 215 | // Generate code for for..in statement (range/array) 216 | void generate_for_in_statement(CodeGenContext* ctx, AST_Node* node) { // Renamed function 217 | AST_Node* iterable = node->data.for_in.iterable; // Use for_in struct 218 | AST_Node* body = node->data.for_in.body; 219 | AST_Node* var_node = node->data.for_in.variable; // This is a NODE_VARIABLE type node used as parameter 220 | char* var_name = var_node->data.variable.name; 221 | TypeInfo* var_type = var_node->data.variable.type; // Get type info (might be NULL) 222 | 223 | // Create new scope for loop variable 224 | enter_scope(ctx->symtab); 225 | 226 | // Add loop variable to symbol table for the scope 227 | // TODO: Determine actual type from iterable if var_type is NULL 228 | add_symbol(ctx->symtab, var_name, SYMBOL_VARIABLE, var_type ? var_type : create_type_info("int", NULL)); // Assume int if type unknown 229 | 230 | if (iterable->type == NODE_RANGE) { 231 | // Generate code for range start and end 232 | char start_var[64], end_var[64]; 233 | snprintf(start_var, sizeof(start_var), "__range_start_%d", ctx->temp_var_count++); 234 | snprintf(end_var, sizeof(end_var), "__range_end_%d", ctx->temp_var_count++); 235 | 236 | indent(ctx); 237 | // Assuming range variables are integers for now 238 | fprintf(ctx->output, "int %s = ", start_var); 239 | generate_expression(ctx, iterable->data.range.start); // Use iterable 240 | fprintf(ctx->output, ";\n"); 241 | 242 | indent(ctx); 243 | fprintf(ctx->output, "int %s = ", end_var); 244 | generate_expression(ctx, iterable->data.range.end); // Use iterable 245 | fprintf(ctx->output, ";\n"); 246 | 247 | // Generate the C for loop (exclusive end '..') 248 | indent(ctx); 249 | // Declare loop variable with its type (assuming int for range) 250 | fprintf(ctx->output, "for (int %s = %s; %s < %s; %s++) {\n", var_name, start_var, var_name, end_var, var_name); // Use '<' for exclusive end 251 | increase_indent(ctx); 252 | 253 | // Generate loop body 254 | if (body->type == NODE_COMPOUND_STATEMENT) { 255 | // Compound statement handles its own scope internally, but the loop var is outer 256 | generate_compound_statement_contents(ctx, body); 257 | } else { 258 | // For single statements, generate directly within the loop scope 259 | indent(ctx); 260 | generate_code(ctx, body); 261 | } 262 | 263 | decrease_indent(ctx); 264 | indent(ctx); 265 | fprintf(ctx->output, "}\n"); 266 | 267 | } else if (iterable->type == NODE_IDENTIFIER || iterable->type == NODE_LITERAL_ARRAY) { 268 | // Handle array iteration 269 | char index_var[64], length_var[64], array_var[64]; 270 | snprintf(index_var, sizeof(index_var), "__i_%d", ctx->temp_var_count++); 271 | snprintf(length_var, sizeof(length_var), "__len_%d", ctx->temp_var_count++); 272 | snprintf(array_var, sizeof(array_var), "__arr_%d", ctx->temp_var_count++); 273 | 274 | indent(ctx); 275 | // Assuming array is accessible and has a 'length' property (requires runtime support) 276 | // This is a simplified C representation 277 | fprintf(ctx->output, "/* Array iteration requires runtime support for length and access */\n"); 278 | indent(ctx); 279 | fprintf(ctx->output, "int %s = 0; // Placeholder length\n", length_var); // Placeholder 280 | indent(ctx); 281 | fprintf(ctx->output, "void* %s = ", array_var); // Placeholder array pointer 282 | generate_expression(ctx, iterable); 283 | fprintf(ctx->output, "; // Placeholder array\n"); 284 | 285 | indent(ctx); 286 | // Determine variable type (e.g., char* for string array) 287 | const char* c_type = "void*"; // Default placeholder 288 | if (var_type && strcmp(var_type->name, "string") == 0) { 289 | c_type = "char*"; 290 | } else if (var_type && strcmp(var_type->name, "int") == 0) { 291 | c_type = "int"; 292 | } // Add more types as needed 293 | 294 | fprintf(ctx->output, "for (int %s = 0; %s < %s; %s++) {\n", index_var, index_var, length_var, index_var); 295 | increase_indent(ctx); 296 | indent(ctx); 297 | // Assign array element to loop variable (requires runtime function like array_get) 298 | fprintf(ctx->output, "%s %s = (%s)array_get(%s, %s); // Placeholder access\n", c_type, var_name, c_type, array_var, index_var); 299 | 300 | // Generate loop body 301 | if (body->type == NODE_COMPOUND_STATEMENT) { 302 | generate_compound_statement_contents(ctx, body); 303 | } else { 304 | indent(ctx); 305 | generate_code(ctx, body); 306 | } 307 | 308 | decrease_indent(ctx); 309 | indent(ctx); 310 | fprintf(ctx->output, "}\n"); 311 | 312 | } else { 313 | // Error or unsupported type 314 | indent(ctx); 315 | fprintf(ctx->output, "// ERROR: Unsupported iterable type for 'for..in' loop\n"); 316 | } 317 | 318 | // End scope for loop variable 319 | leave_scope(ctx->symtab); 320 | } 321 | 322 | // Generate code for for..in statement (map) 323 | void generate_for_map_statement(CodeGenContext* ctx, AST_Node* node) { // Renamed function 324 | AST_Node* map_expr = node->data.for_map.map_expr; // Use for_map struct 325 | (void)map_expr; // Mark as unused for now (placeholder implementation) 326 | AST_Node* body = node->data.for_map.body; 327 | AST_Node* key_var_node = node->data.for_map.key_var; 328 | AST_Node* value_var_node = node->data.for_map.value_var; 329 | char* key_name = key_var_node->data.variable.name; 330 | char* value_name = value_var_node->data.variable.name; 331 | TypeInfo* key_type = key_var_node->data.variable.type; // Assume 'any' or determine from map_expr type 332 | TypeInfo* value_type = value_var_node->data.variable.type; // Assume 'any' or determine from map_expr type 333 | 334 | // Create new scope for loop variables 335 | enter_scope(ctx->symtab); 336 | 337 | // Add loop variables to symbol table 338 | // TODO: Determine actual types from map map_expr type if key/value types are NULL 339 | add_symbol(ctx->symtab, key_name, SYMBOL_VARIABLE, key_type ? key_type : create_type_info("string", NULL)); // Assume string key if unknown 340 | add_symbol(ctx->symtab, value_name, SYMBOL_VARIABLE, value_type ? value_type : create_type_info("any", NULL)); // Assume any value if unknown 341 | 342 | indent(ctx); 343 | fprintf(ctx->output, "// Map iteration requires runtime support (iterator, key/value access)\n"); 344 | indent(ctx); 345 | // Placeholder loop - does not actually iterate map 346 | fprintf(ctx->output, "/* Placeholder map iteration */\n"); 347 | indent(ctx); 348 | fprintf(ctx->output, "{\n"); // Create a block to contain placeholder vars 349 | increase_indent(ctx); 350 | 351 | // Declare placeholder key/value variables inside the loop scope 352 | // Determine C types based on TypeInfo 353 | const char* c_key_type = "char*"; // Default placeholder 354 | if (key_type && strcmp(key_type->name, "int") == 0) c_key_type = "int"; 355 | // Add more key types 356 | 357 | const char* c_value_type = "void*"; // Default placeholder 358 | if (value_type && strcmp(value_type->name, "int") == 0) c_value_type = "int"; 359 | else if (value_type && strcmp(value_type->name, "string") == 0) c_value_type = "char*"; 360 | // Add more value types 361 | 362 | indent(ctx); 363 | fprintf(ctx->output, "%s %s = (%s)map_get_key_placeholder(); // Placeholder key\n", c_key_type, key_name, c_key_type); 364 | indent(ctx); 365 | fprintf(ctx->output, "%s %s = (%s)map_get_value_placeholder(); // Placeholder value\n", c_value_type, value_name, c_value_type); 366 | 367 | // Generate loop body 368 | if (body->type == NODE_COMPOUND_STATEMENT) { 369 | generate_compound_statement_contents(ctx, body); 370 | } else { 371 | indent(ctx); 372 | generate_code(ctx, body); 373 | } 374 | 375 | decrease_indent(ctx); 376 | indent(ctx); 377 | fprintf(ctx->output, "}\n"); 378 | 379 | // End scope 380 | leave_scope(ctx->symtab); 381 | } 382 | 383 | // Generate code for return statement 384 | void generate_return_statement(CodeGenContext* ctx, AST_Node* node) { 385 | fprintf(ctx->output, "return"); 386 | 387 | if (node->data.return_stmt.expression) { 388 | fprintf(ctx->output, " "); 389 | generate_expression(ctx, node->data.return_stmt.expression); 390 | } 391 | 392 | fprintf(ctx->output, ";\n"); 393 | } 394 | 395 | // Generate code for while statement 396 | void generate_while_statement(CodeGenContext* ctx, AST_Node* node) { 397 | fprintf(ctx->output, "while ("); 398 | generate_expression(ctx, node->data.while_statement.condition); // Use while_statement struct 399 | fprintf(ctx->output, ") {\n"); 400 | increase_indent(ctx); 401 | 402 | // Generate loop body 403 | AST_Node* body = node->data.while_statement.body; // Use while_statement struct 404 | if (body->type == NODE_COMPOUND_STATEMENT) { 405 | // Compound statement handles its own scope 406 | generate_compound_statement_contents(ctx, body); 407 | } else { 408 | // For single statements, generate directly 409 | indent(ctx); 410 | generate_code(ctx, body); 411 | } 412 | 413 | decrease_indent(ctx); 414 | indent(ctx); 415 | fprintf(ctx->output, "}\n"); 416 | } 417 | -------------------------------------------------------------------------------- /src/codegen/statement.h: -------------------------------------------------------------------------------- 1 | #ifndef CODEGEN_STATEMENT_H 2 | #define CODEGEN_STATEMENT_H 3 | 4 | #include "context.h" 5 | #include "../ast.h" 6 | 7 | // Statement generation functions 8 | void generate_if_statement(CodeGenContext* ctx, AST_Node* node); 9 | void generate_match_statement(CodeGenContext* ctx, AST_Node* node); 10 | void generate_return_statement(CodeGenContext* ctx, AST_Node* node); 11 | void generate_compound_statement(CodeGenContext* ctx, AST_Node* node); 12 | void generate_compound_statement_contents(CodeGenContext* ctx, AST_Node* node); 13 | void generate_c_style_for_statement(CodeGenContext* ctx, AST_Node* node); // Added 14 | void generate_for_in_statement(CodeGenContext* ctx, AST_Node* node); // Renamed from generate_for_statement 15 | void generate_for_map_statement(CodeGenContext* ctx, AST_Node* node); // Renamed from generate_foreach_statement 16 | void generate_while_statement(CodeGenContext* ctx, AST_Node* node); 17 | 18 | #endif // CODEGEN_STATEMENT_H 19 | -------------------------------------------------------------------------------- /src/codegen/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "utils.h" 5 | 6 | // Generate indentation 7 | void indent(CodeGenContext* ctx) { 8 | for (int i = 0; i < ctx->indentation; i++) { 9 | fprintf(ctx->output, " "); 10 | } 11 | } 12 | 13 | // Increase indentation level 14 | void increase_indent(CodeGenContext* ctx) { 15 | ctx->indentation++; 16 | } 17 | 18 | // Decrease indentation level 19 | void decrease_indent(CodeGenContext* ctx) { 20 | if (ctx->indentation > 0) { 21 | ctx->indentation--; 22 | } 23 | } 24 | 25 | // Get a new temporary variable name 26 | char* get_temp_var_name(CodeGenContext* ctx) { 27 | char buffer[32]; 28 | snprintf(buffer, sizeof(buffer), "__temp_%d", ctx->temp_var_count++); 29 | return strdup(buffer); 30 | } 31 | 32 | // Get a new label name 33 | char* get_label_name(CodeGenContext* ctx) { 34 | char buffer[32]; 35 | snprintf(buffer, sizeof(buffer), "__label_%d", ctx->label_count++); 36 | return strdup(buffer); 37 | } 38 | 39 | // Convert our type to C type 40 | char* get_c_type(TypeInfo* type) { 41 | if (!type) return strdup("void"); 42 | 43 | if (strcmp(type->name, "int") == 0) { 44 | return strdup("int"); 45 | } else if (strcmp(type->name, "float") == 0) { 46 | return strdup("float"); 47 | } else if (strcmp(type->name, "bool") == 0) { 48 | return strdup("int"); // C doesn't have bool, use int 49 | } else if (strcmp(type->name, "string") == 0) { 50 | return strdup("char*"); 51 | } else if (strcmp(type->name, "void") == 0) { 52 | return strdup("void"); 53 | } else if (strcmp(type->name, "array") == 0 && type->generic_type) { 54 | // For arrays, we need to create a pointer to the generic type 55 | char* inner_type = get_c_type(type->generic_type); 56 | char* array_type = (char*)malloc(strlen(inner_type) + 3); 57 | sprintf(array_type, "%s*", inner_type); 58 | free(inner_type); 59 | return array_type; 60 | } else if (strcmp(type->name, "map") == 0) { 61 | // Placeholder for map type - use void* for now 62 | // TODO: Replace with actual map type (e.g., from a hashmap library) 63 | return strdup("void*"); 64 | } else { 65 | // For custom types (structs), use struct prefix 66 | char* struct_type = (char*)malloc(strlen(type->name) + 8); 67 | sprintf(struct_type, "struct %s", type->name); 68 | return struct_type; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/codegen/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef CODEGEN_UTILS_H 2 | #define CODEGEN_UTILS_H 3 | 4 | #include "context.h" 5 | #include "../ast.h" 6 | 7 | // Utility functions 8 | void indent(CodeGenContext* ctx); 9 | void increase_indent(CodeGenContext* ctx); 10 | void decrease_indent(CodeGenContext* ctx); 11 | char* get_temp_var_name(CodeGenContext* ctx); 12 | char* get_label_name(CodeGenContext* ctx); 13 | char* get_c_type(TypeInfo* type); 14 | 15 | #endif // CODEGEN_UTILS_H 16 | -------------------------------------------------------------------------------- /src/error_reporter.c: -------------------------------------------------------------------------------- 1 | #include "error_reporter.h" 2 | #include 3 | #include 4 | 5 | /** 6 | * Logs an error with timestamp, module, and function context. 7 | * 8 | * Uses the current local time to generate a timestamp and prints 9 | * a formatted error message to stderr. 10 | */ 11 | void zeno_log_error(const char *module, const char *func, const char *message) { 12 | time_t now = time(NULL); 13 | struct tm *local = localtime(&now); 14 | if (!local) { 15 | fprintf(stderr, "Failed to obtain local time\n"); 16 | return; 17 | } 18 | 19 | char time_str[20]; // Format: YYYY-MM-DD HH:MM:SS 20 | if (strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", local) == 0) { 21 | fprintf(stderr, "Failed to format time string\n"); 22 | return; 23 | } 24 | 25 | fprintf(stderr, "[%s] Error in %s:%s -> %s\n", time_str, module, func, message); 26 | } 27 | -------------------------------------------------------------------------------- /src/error_reporter.h: -------------------------------------------------------------------------------- 1 | #ifndef ZENO_ERROR_REPORTER_H 2 | #define ZENO_ERROR_REPORTER_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /** 11 | * Logs an error with timestamp, module, and function context. 12 | * 13 | * @param module The source file name. 14 | * @param func The function name where the error occurred. 15 | * @param message The error message. 16 | */ 17 | void zeno_log_error(const char *module, const char *func, const char *message); 18 | 19 | #define ZENO_LOG_ERROR(message) zeno_log_error(__FILE__, __func__, message) 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | #endif /* ZENO_ERROR_REPORTER_H */ 26 | -------------------------------------------------------------------------------- /src/lexer.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include 4 | #include "ast.h" 5 | #include "parser.tab.h" 6 | 7 | void yyerror(const char* s); 8 | %} 9 | 10 | %option noyywrap 11 | %option yylineno 12 | 13 | %% 14 | 15 | [ \t\n]+ { /* Skip whitespace */ } 16 | "//".* { /* Skip single-line comments */ } 17 | 18 | "/*" { /* Skip multi-line comments */ 19 | int c; 20 | while((c = input()) != 0) { 21 | if(c == '*') { 22 | if((c = input()) == '/') 23 | break; 24 | else 25 | unput(c); 26 | } 27 | } 28 | } 29 | 30 | "fn" { return FN; } 31 | "let" { return LET; } 32 | "const" { return CONST; } 33 | "struct" { return STRUCT; } 34 | "with" { return WITH; } 35 | "where" { return WHERE; } 36 | "if" { return IF; } 37 | "else" { return ELSE; } 38 | "match" { return MATCH; } 39 | "return" { return RETURN; } 40 | "import" { return IMPORT; } 41 | "type" { return TYPE; } 42 | "while" { return WHILE; } 43 | "async" { return ASYNC; } 44 | "await" { return AWAIT; } 45 | "then" { return THEN; } 46 | "catch" { return CATCH; } 47 | "finally" { return FINALLY; } 48 | "Promise" { yylval.str = strdup(yytext); return PROMISE_TYPE; } 49 | "for" { return FOR; } 50 | "in" { return IN; } 51 | "print" { return PRINT; } 52 | 53 | "int" { yylval.str = strdup(yytext); return TYPE_NAME; } 54 | "float" { yylval.str = strdup(yytext); return TYPE_NAME; } 55 | "bool" { yylval.str = strdup(yytext); return TYPE_NAME; } 56 | "string" { yylval.str = strdup(yytext); return TYPE_NAME; } 57 | "array" { yylval.str = strdup(yytext); return TYPE_NAME; } 58 | "map" { yylval.str = strdup(yytext); return TYPE_NAME; } 59 | "any" { yylval.str = strdup(yytext); return TYPE_NAME; } 60 | 61 | "true"|"false" { yylval.str = strdup(yytext); return BOOL_LITERAL; } 62 | 63 | [0-9]+ { yylval.str = strdup(yytext); return INT_LITERAL; } 64 | [0-9]+"."[0-9]+ { yylval.str = strdup(yytext); return FLOAT_LITERAL; } 65 | \"(\\.|[^"\\])*\" { yylval.str = strdup(yytext); return STRING_LITERAL; } 66 | 67 | "==" { return EQ; } 68 | "!=" { return NEQ; } 69 | ">=" { return GE; } 70 | "<=" { return LE; } 71 | "&&" { return AND; } 72 | "||" { return OR; } 73 | "++" { return CONCAT; } 74 | "=>" { return ARROW; } 75 | "|>" { return PIPE; } 76 | "..." { return SPREAD; } 77 | ".." { return RANGE; } 78 | 79 | [a-zA-Z_][a-zA-Z0-9_]* { yylval.str = strdup(yytext); return IDENTIFIER; } 80 | 81 | "(" { return '('; } 82 | ")" { return ')'; } 83 | "{" { return '{'; } 84 | "}" { return '}'; } 85 | "[" { return '['; } 86 | "]" { return ']'; } 87 | "<" { return '<'; } 88 | ">" { return '>'; } 89 | ";" { return ';'; } 90 | ":" { return ':'; } 91 | "," { return ','; } 92 | "." { return '.'; } 93 | "=" { return '='; } 94 | "+" { return '+'; } 95 | "-" { return '-'; } 96 | "*" { return '*'; } 97 | "/" { return '/'; } 98 | "%" { return '%'; } 99 | "!" { return '!'; } 100 | "_" { return '_'; } 101 | 102 | . { yyerror("Unexpected character"); } 103 | 104 | %% 105 | -------------------------------------------------------------------------------- /src/llvm_codegen/llvm_codegen.c: -------------------------------------------------------------------------------- 1 | #include "llvm_codegen.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Implementation of an extremely simplified version for demonstration 12 | // A real implementation would need to handle all AST node types 13 | 14 | // Main code generation function 15 | void llvm_generate_code(LLVMGenContext* ctx, AST_Node* node) { 16 | if (!node) return; 17 | 18 | // Basic implementation that just handles function declarations for "main" 19 | if (node->type == NODE_PROGRAM) { 20 | // Create printf declaration 21 | LLVMTypeRef paramTypes[] = { LLVMPointerType(LLVMInt8Type(), 0) }; 22 | LLVMTypeRef funcType = LLVMFunctionType(LLVMInt32Type(), paramTypes, 1, 1); 23 | LLVMValueRef printfFunc = LLVMAddFunction(ctx->module, "printf", funcType); 24 | 25 | // Create main function 26 | LLVMTypeRef mainType = LLVMFunctionType(LLVMInt32Type(), NULL, 0, 0); 27 | LLVMValueRef mainFunc = LLVMAddFunction(ctx->module, "main", mainType); 28 | 29 | // Create entry block 30 | LLVMBasicBlockRef entry = LLVMAppendBasicBlock(mainFunc, "entry"); 31 | LLVMPositionBuilderAtEnd(ctx->builder, entry); 32 | 33 | // Create a hello world string 34 | LLVMValueRef str = LLVMBuildGlobalStringPtr(ctx->builder, "Hello, LLVM World!\n", "hello_str"); 35 | 36 | // Call printf - using LLVMBuildCall2 instead of LLVMBuildCall 37 | LLVMValueRef args[] = { str }; 38 | LLVMBuildCall2(ctx->builder, funcType, printfFunc, args, 1, ""); 39 | 40 | // Return 0 41 | LLVMBuildRet(ctx->builder, LLVMConstInt(LLVMInt32Type(), 0, 0)); 42 | } 43 | } 44 | 45 | // Compile LLVM IR to object file 46 | int llvm_compile_to_object(LLVMGenContext* ctx, const char* output_path) { 47 | if (!ctx || !output_path) return 1; 48 | 49 | // Initialize target 50 | LLVMInitializeAllTargetInfos(); 51 | LLVMInitializeAllTargets(); 52 | LLVMInitializeAllTargetMCs(); 53 | LLVMInitializeAllAsmParsers(); 54 | LLVMInitializeAllAsmPrinters(); 55 | 56 | // Get the target 57 | char* triple = LLVMGetDefaultTargetTriple(); 58 | LLVMTargetRef target; 59 | char* error = NULL; 60 | 61 | if (LLVMGetTargetFromTriple(triple, &target, &error) != 0) { 62 | fprintf(stderr, "Error getting target: %s\n", error); 63 | LLVMDisposeMessage(error); 64 | LLVMDisposeMessage(triple); 65 | return 1; 66 | } 67 | 68 | // Create target machine 69 | LLVMTargetMachineRef target_machine = LLVMCreateTargetMachine( 70 | target, triple, "", "", LLVMCodeGenLevelDefault, 71 | LLVMRelocDefault, LLVMCodeModelDefault); 72 | 73 | LLVMDisposeMessage(triple); 74 | 75 | // Set target triple in module 76 | LLVMSetTarget(ctx->module, LLVMGetDefaultTargetTriple()); 77 | 78 | // Write object file 79 | if (LLVMTargetMachineEmitToFile( 80 | target_machine, ctx->module, (char*)output_path, 81 | LLVMObjectFile, &error) != 0) { 82 | fprintf(stderr, "Error writing object file: %s\n", error); 83 | LLVMDisposeMessage(error); 84 | LLVMDisposeTargetMachine(target_machine); 85 | return 1; 86 | } 87 | 88 | LLVMDisposeTargetMachine(target_machine); 89 | return 0; 90 | } 91 | 92 | // Compile LLVM IR to executable 93 | int llvm_compile_to_executable(LLVMGenContext* ctx, const char* output_path) { 94 | if (!ctx || !output_path) return 1; 95 | 96 | // First compile to object file 97 | char obj_path[1024]; 98 | snprintf(obj_path, sizeof(obj_path), "%s.o", output_path); 99 | 100 | if (llvm_compile_to_object(ctx, obj_path) != 0) { 101 | return 1; 102 | } 103 | 104 | // Then link with compiler 105 | char link_cmd[2048]; 106 | snprintf(link_cmd, sizeof(link_cmd), 107 | "cc %s -o %s", obj_path, output_path); 108 | 109 | int ret = system(link_cmd); 110 | 111 | // Clean up object file 112 | remove(obj_path); 113 | 114 | return ret; 115 | } 116 | -------------------------------------------------------------------------------- /src/llvm_codegen/llvm_codegen.h: -------------------------------------------------------------------------------- 1 | #ifndef LLVM_CODEGEN_H 2 | #define LLVM_CODEGEN_H 3 | 4 | #include "llvm_context.h" 5 | #include "../ast.h" 6 | 7 | // Forward declarations 8 | LLVMValueRef llvm_generate_expression(LLVMGenContext* ctx, AST_Node* node); 9 | void llvm_generate_statement(LLVMGenContext* ctx, AST_Node* node); 10 | void llvm_generate_declaration(LLVMGenContext* ctx, AST_Node* node); 11 | 12 | // Generate LLVM IR from AST 13 | void llvm_generate_code(LLVMGenContext* ctx, AST_Node* node); 14 | 15 | // Compile LLVM IR to object file 16 | int llvm_compile_to_object(LLVMGenContext* ctx, const char* output_path); 17 | 18 | // Compile LLVM IR to executable 19 | int llvm_compile_to_executable(LLVMGenContext* ctx, const char* output_path); 20 | 21 | #endif // LLVM_CODEGEN_H 22 | -------------------------------------------------------------------------------- /src/llvm_codegen/llvm_context.c: -------------------------------------------------------------------------------- 1 | #include "llvm_context.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // Initialize LLVM code generation context 7 | LLVMGenContext* init_llvm_codegen(const char* module_name, int debug_info) { 8 | LLVMGenContext* ctx = (LLVMGenContext*)malloc(sizeof(LLVMGenContext)); 9 | if (!ctx) { 10 | fprintf(stderr, "Memory allocation failed\n"); 11 | exit(1); 12 | } 13 | 14 | // Initialize LLVM components 15 | ctx->context = LLVMContextCreate(); 16 | ctx->module = LLVMModuleCreateWithNameInContext(module_name, ctx->context); 17 | ctx->builder = LLVMCreateBuilderInContext(ctx->context); 18 | 19 | // Initialize symbol table 20 | ctx->symtab = init_symbol_table(); 21 | 22 | // Initialize state 23 | ctx->current_function = NULL; 24 | ctx->entry_block = NULL; 25 | ctx->current_block = NULL; 26 | ctx->value_map = NULL; 27 | ctx->label_count = 0; 28 | ctx->debug_info = debug_info; 29 | 30 | return ctx; 31 | } 32 | 33 | // Clean up LLVM code generation context 34 | void cleanup_llvm_codegen(LLVMGenContext* ctx) { 35 | if (!ctx) return; 36 | 37 | // Clean up LLVM components 38 | LLVMDisposeBuilder(ctx->builder); 39 | LLVMDisposeModule(ctx->module); 40 | LLVMContextDispose(ctx->context); 41 | 42 | // Clean up symbol table 43 | cleanup_symbol_table(ctx->symtab); 44 | 45 | // Free the context itself 46 | free(ctx); 47 | } 48 | 49 | // Get LLVM type from Zeno type 50 | LLVMTypeRef llvm_get_type(LLVMGenContext* ctx, TypeInfo* type) { 51 | if (!type) return LLVMVoidType(); 52 | 53 | if (strcmp(type->name, "int") == 0) { 54 | return LLVMInt32Type(); 55 | } else if (strcmp(type->name, "float") == 0) { 56 | return LLVMFloatType(); 57 | } else if (strcmp(type->name, "bool") == 0) { 58 | return LLVMInt1Type(); 59 | } else if (strcmp(type->name, "string") == 0) { 60 | return LLVMPointerType(LLVMInt8Type(), 0); 61 | } else if (strcmp(type->name, "void") == 0) { 62 | return LLVMVoidType(); 63 | } else if (strcmp(type->name, "array") == 0 && type->generic_type) { 64 | // For arrays, we need to create a pointer to the inner type 65 | LLVMTypeRef inner_type = llvm_get_type(ctx, type->generic_type); 66 | return LLVMPointerType(inner_type, 0); 67 | } else if (strcmp(type->name, "map") == 0) { 68 | // Represent map as an opaque pointer (i8*) for now 69 | // TODO: Define a proper runtime struct type for maps 70 | return LLVMPointerType(LLVMInt8Type(), 0); 71 | } else { 72 | // For custom types (structs), use opaque pointer type for now 73 | // TODO: Look up or create named struct types 74 | return LLVMPointerType(LLVMInt8Type(), 0); 75 | } 76 | } 77 | 78 | // Store value in context value map 79 | void llvm_store_value(LLVMGenContext* ctx, const char* name, LLVMValueRef value) { 80 | // Real implementation would use a hash map for efficiency 81 | // This is just a placeholder 82 | } 83 | 84 | // Get value from context value map 85 | LLVMValueRef llvm_get_value(LLVMGenContext* ctx, const char* name) { 86 | // Real implementation would look up in the hash map 87 | // This is just a placeholder 88 | return NULL; 89 | } 90 | 91 | // Write LLVM module to file 92 | int write_llvm_module(LLVMGenContext* ctx, const char* output_path) { 93 | if (!ctx || !output_path) return 1; 94 | 95 | // Verify the module 96 | char* error = NULL; 97 | LLVMVerifyModule(ctx->module, LLVMAbortProcessAction, &error); 98 | LLVMDisposeMessage(error); 99 | 100 | // Write bitcode to file 101 | if (LLVMWriteBitcodeToFile(ctx->module, output_path) != 0) { 102 | fprintf(stderr, "Error writing bitcode to file\n"); 103 | return 1; 104 | } 105 | 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /src/llvm_codegen/llvm_context.h: -------------------------------------------------------------------------------- 1 | #ifndef LLVM_CONTEXT_H 2 | #define LLVM_CONTEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "../symtab.h" 9 | #include "../ast.h" // Added this include for TypeInfo 10 | 11 | // LLVM code generation context 12 | typedef struct LLVMGenContext { 13 | LLVMContextRef context; // LLVM context 14 | LLVMModuleRef module; // LLVM module 15 | LLVMBuilderRef builder; // LLVM IR builder 16 | 17 | SymbolTable* symtab; // Symbol table (reused from existing code) 18 | 19 | // Function-related state 20 | LLVMValueRef current_function; // Current function being generated 21 | LLVMBasicBlockRef entry_block; // Entry block of current function 22 | LLVMBasicBlockRef current_block; // Current block being generated 23 | 24 | // Value caching 25 | LLVMValueRef* value_map; // Map from variable names to LLVM values 26 | 27 | // Control flow state 28 | int label_count; // Counter for labels 29 | 30 | // Debug info 31 | int debug_info; // Whether to generate debug info 32 | } LLVMGenContext; 33 | 34 | // Initialize LLVM code generation context 35 | LLVMGenContext* init_llvm_codegen(const char* module_name, int debug_info); 36 | 37 | // Clean up LLVM code generation context 38 | void cleanup_llvm_codegen(LLVMGenContext* ctx); 39 | 40 | // Get LLVM type from Zeno type 41 | LLVMTypeRef llvm_get_type(LLVMGenContext* ctx, TypeInfo* type); 42 | 43 | // Store value in context value map 44 | void llvm_store_value(LLVMGenContext* ctx, const char* name, LLVMValueRef value); 45 | 46 | // Get value from context value map 47 | LLVMValueRef llvm_get_value(LLVMGenContext* ctx, const char* name); 48 | 49 | // Write LLVM module to file 50 | int write_llvm_module(LLVMGenContext* ctx, const char* output_path); 51 | 52 | #endif // LLVM_CONTEXT_H 53 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "ast.h" 6 | #include "symtab.h" 7 | #include "codegen/codegen.h" 8 | #include "llvm_codegen/llvm_codegen.h" 9 | #include "zeno_cli.h" 10 | 11 | // External variables from Flex/Bison 12 | extern FILE* yyin; 13 | extern int yyparse(); 14 | extern AST_Node* root; 15 | 16 | void print_usage(const char* program_name) { 17 | printf("Zeno Language v0.1.0 - Usage:\n\n"); 18 | printf(" %s COMMAND [OPTIONS] [FILE]\n\n", program_name); 19 | printf("Commands:\n"); 20 | printf(" transpile [output] Convert Zeno code to C or LLVM IR\n"); 21 | printf(" run [OPTIONS] [file] Transpile, compile, and run Zeno code\n"); 22 | printf(" compile [OPTIONS] [file] Transpile and compile Zeno code to a binary\n"); 23 | printf(" init [directory] Create a default manifest.yaml file\n\n"); 24 | printf("Options:\n"); 25 | printf(" -v, --verbose Enable verbose output\n"); 26 | printf(" -m, --manifest PATH Specify manifest file (default: manifest.yaml)\n"); 27 | printf(" -o, --output FILE Specify output file\n"); 28 | printf(" --llvm Use LLVM backend instead of C\n"); 29 | } 30 | 31 | int main(int argc, char** argv) { 32 | // Check for minimum arguments 33 | if (argc < 2) { 34 | print_usage(argv[0]); 35 | return 1; 36 | } 37 | 38 | // Handle different commands 39 | if (strcmp(argv[1], "transpile") == 0) { 40 | // Transpile mode 41 | if (argc < 3) { 42 | fprintf(stderr, "Error: Missing input file for transpile command\n"); 43 | print_usage(argv[0]); 44 | return 1; 45 | } 46 | 47 | const char* input_file = argv[2]; 48 | const char* output_file = (argc > 3) ? argv[3] : NULL; 49 | bool use_llvm = false; 50 | bool verbose = false; 51 | 52 | // Check for LLVM flag 53 | for (int i = 3; i < argc; i++) { 54 | if (strcmp(argv[i], "--llvm") == 0) { 55 | use_llvm = true; 56 | } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) { 57 | verbose = true; 58 | } 59 | } 60 | 61 | return transpile_file(input_file, output_file, verbose, use_llvm); 62 | } 63 | else if (strcmp(argv[1], "init") == 0) { 64 | // Init mode - create a default manifest.yaml 65 | const char* dir_path = (argc > 2) ? argv[2] : NULL; 66 | return init_zeno_project(dir_path, true); 67 | } 68 | else if (strcmp(argv[1], "run") == 0 || strcmp(argv[1], "compile") == 0) { 69 | // CLI mode (run or compile) 70 | ZenoOptions options; 71 | options.verbose = false; 72 | options.manifest_path = "manifest.yaml"; 73 | options.input_file = NULL; 74 | options.output_file = NULL; 75 | options.run_mode = (strcmp(argv[1], "run") == 0); 76 | options.compile_mode = (strcmp(argv[1], "compile") == 0); 77 | options.use_llvm = false; // Default to C backend 78 | 79 | // Parse remaining arguments 80 | for (int i = 2; i < argc; i++) { 81 | if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) { 82 | options.verbose = true; 83 | } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--manifest") == 0) { 84 | if (i + 1 < argc) { 85 | options.manifest_path = argv[++i]; 86 | } else { 87 | fprintf(stderr, "Missing manifest path\n"); 88 | return 1; 89 | } 90 | } else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--output") == 0) { 91 | if (i + 1 < argc) { 92 | options.output_file = argv[++i]; 93 | } else { 94 | fprintf(stderr, "Missing output file\n"); 95 | return 1; 96 | } 97 | } else if (strcmp(argv[i], "--llvm") == 0) { 98 | options.use_llvm = true; 99 | } else if (argv[i][0] != '-') { 100 | // Assume it's the input file 101 | options.input_file = argv[i]; 102 | } else { 103 | fprintf(stderr, "Unknown option: %s\n", argv[i]); 104 | print_usage(argv[0]); 105 | return 1; 106 | } 107 | } 108 | 109 | // Load manifest - don't allow missing manifest for run/compile commands 110 | ZenoManifest* manifest = load_manifest(options.manifest_path, false); 111 | if (!manifest) { 112 | return 1; // Error message already printed by load_manifest 113 | } 114 | 115 | if (options.verbose) { 116 | printf("Zeno Compiler v0.1.0\n"); 117 | printf("Project: %s (%s)\n", manifest->name, manifest->version); 118 | printf("Output directory: %s\n", manifest->output.dir); 119 | printf("Binary name: %s\n", manifest->output.binary); 120 | printf("Using: %s backend\n", options.use_llvm ? "LLVM" : "C"); 121 | 122 | if (!options.use_llvm) { 123 | printf("Compiler: %s\n", manifest->compiler.cc); 124 | printf("Compiler flags: %s\n", manifest->compiler.flags); 125 | } 126 | 127 | if (manifest->source.main) { 128 | printf("Main source file: %s\n", manifest->source.main); 129 | } 130 | 131 | printf("Include directories:\n"); 132 | for (int i = 0; i < manifest->source.include_count; i++) { 133 | printf(" - %s\n", manifest->source.include[i]); 134 | } 135 | 136 | printf("\n"); 137 | } 138 | 139 | int result = 0; 140 | 141 | // Execute command 142 | if (options.run_mode) { 143 | result = run_zeno_file(&options, manifest); 144 | } else if (options.compile_mode) { 145 | result = compile_zeno_file(&options, manifest); 146 | } 147 | 148 | // Clean up 149 | free_manifest(manifest); 150 | 151 | return result; 152 | } 153 | else if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) { 154 | print_usage(argv[0]); 155 | return 0; 156 | } 157 | else { 158 | // Unknown command 159 | fprintf(stderr, "Error: Unknown command '%s'\n", argv[1]); 160 | print_usage(argv[0]); 161 | return 1; 162 | } 163 | } -------------------------------------------------------------------------------- /src/parser.tab.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 2.3. */ 2 | 3 | /* Skeleton interface for Bison's Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 6 | Free Software Foundation, Inc. 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2, or (at your option) 11 | any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 | Boston, MA 02110-1301, USA. */ 22 | 23 | /* As a special exception, you may create a larger work that contains 24 | part or all of the Bison parser skeleton and distribute that work 25 | under terms of your choice, so long as that work isn't itself a 26 | parser generator using the skeleton or a modified version thereof 27 | as a parser skeleton. Alternatively, if you modify or redistribute 28 | the parser skeleton itself, you may (at your option) remove this 29 | special exception, which will cause the skeleton and the resulting 30 | Bison output files to be licensed under the GNU General Public 31 | License without this special exception. 32 | 33 | This special exception was added by the Free Software Foundation in 34 | version 2.2 of Bison. */ 35 | 36 | /* Tokens. */ 37 | #ifndef YYTOKENTYPE 38 | # define YYTOKENTYPE 39 | /* Put the tokens into the symbol table, so that GDB and other debuggers 40 | know about them. */ 41 | enum yytokentype { 42 | FN = 258, 43 | LET = 259, 44 | CONST = 260, 45 | STRUCT = 261, 46 | WITH = 262, 47 | WHERE = 263, 48 | IF = 264, 49 | ELSE = 265, 50 | MATCH = 266, 51 | RETURN = 267, 52 | IMPORT = 268, 53 | TYPE = 269, 54 | EQ = 270, 55 | NEQ = 271, 56 | GE = 272, 57 | LE = 273, 58 | AND = 274, 59 | OR = 275, 60 | CONCAT = 276, 61 | ARROW = 277, 62 | PIPE = 278, 63 | SPREAD = 279, 64 | IDENTIFIER = 280, 65 | TYPE_NAME = 281, 66 | INT_LITERAL = 282, 67 | FLOAT_LITERAL = 283, 68 | STRING_LITERAL = 284, 69 | BOOL_LITERAL = 285 70 | }; 71 | #endif 72 | /* Tokens. */ 73 | #define FN 258 74 | #define LET 259 75 | #define CONST 260 76 | #define STRUCT 261 77 | #define WITH 262 78 | #define WHERE 263 79 | #define IF 264 80 | #define ELSE 265 81 | #define MATCH 266 82 | #define RETURN 267 83 | #define IMPORT 268 84 | #define TYPE 269 85 | #define EQ 270 86 | #define NEQ 271 87 | #define GE 272 88 | #define LE 273 89 | #define AND 274 90 | #define OR 275 91 | #define CONCAT 276 92 | #define ARROW 277 93 | #define PIPE 278 94 | #define SPREAD 279 95 | #define IDENTIFIER 280 96 | #define TYPE_NAME 281 97 | #define INT_LITERAL 282 98 | #define FLOAT_LITERAL 283 99 | #define STRING_LITERAL 284 100 | #define BOOL_LITERAL 285 101 | 102 | 103 | 104 | 105 | #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED 106 | typedef union YYSTYPE 107 | #line 18 "src/parser.y" 108 | { 109 | char* str; 110 | AST_Node* node; 111 | AST_Node_List* node_list; 112 | TypeInfo* type_info; 113 | StructField* field; 114 | StructField_List* field_list; 115 | ExpressionList* expr_list; 116 | GuardClause* guard; 117 | MatchCase* match_case; 118 | MatchCase_List* match_case_list; 119 | } 120 | /* Line 1529 of yacc.c. */ 121 | #line 122 "src/parser.tab.h" 122 | YYSTYPE; 123 | # define yystype YYSTYPE /* obsolescent; will be withdrawn */ 124 | # define YYSTYPE_IS_DECLARED 1 125 | # define YYSTYPE_IS_TRIVIAL 1 126 | #endif 127 | 128 | extern YYSTYPE yylval; 129 | 130 | -------------------------------------------------------------------------------- /src/parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include 4 | #include 5 | #include "ast.h" 6 | 7 | // External declarations 8 | extern int yylex(); 9 | extern int yylineno; 10 | extern char* yytext; 11 | extern FILE* yyin; 12 | 13 | // Function declarations 14 | void yyerror(const char* s); 15 | AST_Node* root = NULL; // Root of the AST 16 | %} 17 | 18 | %union { 19 | char* str; 20 | AST_Node* node; 21 | AST_Node_List* node_list; 22 | TypeInfo* type_info; 23 | StructField* field; 24 | StructField_List* field_list; 25 | ExpressionList* expr_list; 26 | GuardClause* guard; 27 | MatchCase* match_case; 28 | MatchCase_List* match_case_list; 29 | } 30 | 31 | %token FN LET CONST STRUCT WITH WHERE IF ELSE MATCH RETURN IMPORT TYPE WHILE 32 | %token EQ NEQ GE LE AND OR CONCAT ARROW PIPE SPREAD RANGE 33 | %token ASYNC AWAIT THEN CATCH FINALLY 34 | %token FOR IN PRINT /* Removed FOREACH */ 35 | %token IDENTIFIER TYPE_NAME PROMISE_TYPE 36 | %token INT_LITERAL FLOAT_LITERAL STRING_LITERAL BOOL_LITERAL 37 | 38 | %type program declaration function_declaration variable_declaration_body struct_declaration /* variable_declaration type removed */ 39 | %type type_declaration statement expression function_call simple_statement control_statement return_statement_body /* Added categories */ 40 | %type if_statement match_statement /* return_statement removed */ literal struct_instantiation 41 | %type compound_statement import_declaration struct_init_field 42 | %type c_style_for_statement for_initializer expression_opt for_statement foreach_statement range_expression map_literal map_entry while_statement 43 | %type map_entries /* Changed from expr_list to node_list */ 44 | %type declarations statements parameter_list struct_composition_list struct_init_list 45 | %type type type_with_generics 46 | %type function_name 47 | %type struct_fields 48 | %type struct_field 49 | %type argument_list array_elements 50 | %type guard_clause 51 | %type match_cases 52 | %type match_case 53 | 54 | // Precedence and Associativity Rules (Lowest to Highest) 55 | %right '=' // Assignment 56 | %left OR // Logical OR 57 | %left AND // Logical AND 58 | %left EQ NEQ // Equality 59 | %left '<' '>' LE GE // Comparison 60 | %left '+' '-' CONCAT // Addition, Subtraction, Concatenation 61 | %left '*' '/' '%' // Multiplication, Division, Modulo 62 | %right '!' // Logical NOT (Unary) 63 | %right UMINUS // Unary minus (defined below) 64 | %left '.' // Member access 65 | %left '(' ')' '[' ']' // Function call, array access (high precedence) 66 | %left PIPE // Pipe operator 67 | %left ARROW // Arrow function / Match case arrow 68 | %nonassoc IF // Non-associative IF to prevent dangling else 69 | %nonassoc ELSE // Non-associative ELSE 70 | %nonassoc WHILE // Added precedence for WHILE back 71 | %nonassoc FOR // Added precedence for FOR 72 | 73 | %start program 74 | 75 | %% 76 | 77 | program 78 | : declarations { root = create_program_node($1); } 79 | ; 80 | 81 | declarations 82 | : declaration { $$ = create_node_list($1); } 83 | | declarations declaration { append_node($1, $2); $$ = $1; } 84 | ; 85 | 86 | declaration 87 | : function_declaration { $$ = $1; } 88 | | variable_declaration_body ';' { $$ = $1; } /* Use body + semicolon */ 89 | | struct_declaration { $$ = $1; } 90 | | type_declaration { $$ = $1; } 91 | | import_declaration { $$ = $1; } 92 | ; 93 | 94 | import_declaration 95 | : IMPORT STRING_LITERAL ';' { $$ = create_import_node($2); } 96 | ; 97 | 98 | type_declaration 99 | : TYPE IDENTIFIER '=' type ';' { $$ = create_type_declaration_node($2, $4); } 100 | ; 101 | 102 | /* variable_declaration rule definition removed */ 103 | 104 | struct_declaration 105 | : STRUCT IDENTIFIER '{' struct_fields '}' { $$ = create_struct_node($2, NULL, $4); } 106 | | STRUCT IDENTIFIER WITH struct_composition_list '{' struct_fields '}' { $$ = create_struct_node($2, $4, $6); } 107 | ; 108 | 109 | struct_composition_list 110 | : IDENTIFIER { $$ = create_node_list(create_identifier_node($1)); } 111 | | struct_composition_list ',' IDENTIFIER { append_node($1, create_identifier_node($3)); $$ = $1; } 112 | ; 113 | 114 | struct_fields 115 | : struct_field { $$ = create_field_list($1); } 116 | | struct_fields ',' struct_field { append_field($1, $3); $$ = $1; } 117 | ; 118 | 119 | struct_field 120 | : IDENTIFIER ':' type { $$ = create_struct_field($1, $3); } 121 | ; 122 | 123 | function_declaration 124 | : FN function_name '(' parameter_list ')' ':' type guard_clause compound_statement { 125 | $$ = create_function_node($2, $4, $7, $8, $9); 126 | } 127 | | FN function_name '(' parameter_list ')' ':' type ARROW expression ';' { 128 | AST_Node* return_stmt = create_return_node($9); 129 | AST_Node_List* body = create_node_list(return_stmt); 130 | $$ = create_function_node($2, $4, $7, NULL, create_compound_statement_node(body)); 131 | } 132 | | ASYNC FN function_name '(' parameter_list ')' ':' type guard_clause compound_statement { 133 | // For async functions, we set a flag in the AST node 134 | AST_Node* func_node = create_function_node($3, $5, $8, $9, $10); 135 | func_node->data.function.is_async = 1; // Mark as async 136 | $$ = func_node; 137 | } 138 | | ASYNC FN function_name '(' parameter_list ')' ':' type ARROW expression ';' { 139 | AST_Node* return_stmt = create_return_node($10); 140 | AST_Node_List* body = create_node_list(return_stmt); 141 | AST_Node* func_node = create_function_node($3, $5, $8, NULL, create_compound_statement_node(body)); 142 | func_node->data.function.is_async = 1; // Mark as async 143 | $$ = func_node; 144 | } 145 | ; 146 | 147 | guard_clause 148 | : /* empty */ { $$ = NULL; } 149 | | WHERE expression { $$ = create_guard_clause($2); } 150 | ; 151 | 152 | function_name 153 | : IDENTIFIER { $$ = $1; } 154 | ; 155 | 156 | parameter_list 157 | : /* empty */ { $$ = create_node_list(NULL); } 158 | | IDENTIFIER ':' type { $$ = create_node_list(create_parameter_node($1, $3)); } 159 | | parameter_list ',' IDENTIFIER ':' type { 160 | append_node($1, create_parameter_node($3, $5)); 161 | $$ = $1; 162 | } 163 | ; 164 | 165 | type 166 | : TYPE_NAME { $$ = create_type_info($1, NULL); } 167 | | IDENTIFIER { $$ = create_type_info($1, NULL); } 168 | | type_with_generics { $$ = $1; } 169 | ; 170 | 171 | type_with_generics 172 | : TYPE_NAME '<' type '>' { $$ = create_type_info($1, $3); } 173 | | IDENTIFIER '<' type '>' { $$ = create_type_info($1, $3); } 174 | | TYPE_NAME '<' type ',' type '>' { 175 | // Check if the type name is "map" 176 | if (strcmp($1, "map") == 0) { 177 | $$ = create_map_type_info($3, $5); // Specific function for map types 178 | } else { 179 | char err_msg[100]; 180 | snprintf(err_msg, sizeof(err_msg), "Type '%s' does not support two generic arguments", $1); 181 | yyerror(err_msg); 182 | YYERROR; // Trigger parser error recovery 183 | } 184 | } 185 | ; 186 | 187 | compound_statement 188 | : '{' statements '}' { $$ = create_compound_statement_node($2); } 189 | ; 190 | 191 | statements 192 | : /* empty */ { $$ = create_node_list(NULL); } 193 | | statements statement { append_node($1, $2); $$ = $1; } 194 | ; 195 | 196 | statement 197 | : simple_statement { $$ = $1; } 198 | | control_statement { $$ = $1; } 199 | ; 200 | 201 | /* Statements that must end with a semicolon */ 202 | simple_statement 203 | : variable_declaration_body ';' { $$ = $1; } /* Use body + semicolon */ 204 | | return_statement_body ';' { $$ = $1; } 205 | | expression ';' { $$ = $1; } 206 | ; 207 | 208 | /* Control flow and block statements */ 209 | control_statement 210 | : if_statement { $$ = $1; } 211 | | while_statement { $$ = $1; } 212 | | c_style_for_statement { $$ = $1; } 213 | | for_statement { $$ = $1; } /* For range/array */ 214 | | foreach_statement { $$ = $1; } /* For map */ 215 | | match_statement { $$ = $1; } 216 | | compound_statement { $$ = $1; } 217 | ; 218 | 219 | /* Reverted C-style for loop rule (again) */ 220 | c_style_for_statement 221 | : FOR '(' for_initializer expression_opt ';' expression_opt ')' statement { 222 | /* Note: $3 (for_initializer) now includes the semicolon if it's a var_decl or expr */ 223 | $$ = create_c_style_for_node($3, $4, $6, $8); 224 | } 225 | ; 226 | 227 | /* Initializer for C-style for loop */ 228 | for_initializer 229 | : variable_declaration_body ';' { $$ = $1; } /* Use body + semicolon */ 230 | | expression_opt ';' { $$ = $1; } 231 | ; 232 | 233 | /* Optional expression (used for condition and incrementer) */ 234 | expression_opt 235 | : expression { $$ = $1; } 236 | | /* empty */ { $$ = NULL; } 237 | ; 238 | 239 | if_statement 240 | : IF '(' expression ')' statement %prec IF { $$ = create_if_node($3, $5, NULL); } // Apply IF precedence 241 | | IF '(' expression ')' statement ELSE statement { $$ = create_if_node($3, $5, $7); } // ELSE has higher precedence implicitly 242 | ; 243 | 244 | while_statement 245 | : WHILE '(' expression ')' statement { $$ = create_while_node($3, $5); } 246 | ; 247 | 248 | match_statement 249 | : MATCH expression '{' match_cases '}' { $$ = create_match_node($2, $4); } 250 | ; 251 | 252 | match_cases 253 | : match_case { $$ = create_match_case_list($1); } 254 | | match_cases match_case { append_match_case($1, $2); $$ = $1; } 255 | ; 256 | 257 | match_case 258 | : expression ARROW statement { $$ = create_match_case($1, NULL, $3); } 259 | | IDENTIFIER ':' type ARROW statement { 260 | AST_Node* binding = create_binding_pattern($1, $3); 261 | $$ = create_match_case(binding, NULL, $5); 262 | } 263 | | expression IF expression ARROW statement { $$ = create_match_case($1, $3, $5); } 264 | | '_' ARROW statement { $$ = create_match_case(create_wildcard_node(), NULL, $3); } 265 | ; 266 | 267 | /* Renamed from return_statement, removed trailing ';' */ 268 | return_statement_body 269 | : RETURN expression { $$ = create_return_node($2); } 270 | | RETURN { $$ = create_return_node(NULL); } 271 | ; 272 | 273 | expression 274 | : literal { $$ = $1; } 275 | | IDENTIFIER { $$ = create_identifier_node($1); } 276 | | function_call { $$ = $1; } 277 | | struct_instantiation { $$ = $1; } 278 | | expression PIPE expression { $$ = create_pipe_node($1, $3); } 279 | | '(' expression ')' { $$ = $2; } 280 | | '(' parameter_list ')' ':' type ARROW expression { 281 | AST_Node* return_stmt = create_return_node($7); 282 | AST_Node_List* body = create_node_list(return_stmt); 283 | $$ = create_anonymous_function_node($2, $5, create_compound_statement_node(body)); 284 | } 285 | | '(' IDENTIFIER ')' ARROW expression { 286 | // Arrow function with single parameter, no type 287 | AST_Node_List* params = create_node_list(create_parameter_node($2, create_type_info("any", NULL))); 288 | AST_Node* return_stmt = create_return_node($5); 289 | AST_Node_List* body = create_node_list(return_stmt); 290 | $$ = create_anonymous_function_node(params, create_type_info("any", NULL), create_compound_statement_node(body)); 291 | } 292 | | '(' ')' ARROW expression { 293 | // Arrow function with no parameters - create an empty parameter list, not NULL 294 | AST_Node_List* params = create_node_list(NULL); 295 | AST_Node* return_stmt = create_return_node($4); 296 | AST_Node_List* body = create_node_list(return_stmt); 297 | $$ = create_anonymous_function_node(params, create_type_info("any", NULL), create_compound_statement_node(body)); 298 | } 299 | | IDENTIFIER ARROW expression { 300 | // Arrow function with single parameter, no parentheses, no type 301 | AST_Node_List* params = create_node_list(create_parameter_node($1, create_type_info("any", NULL))); 302 | AST_Node* return_stmt = create_return_node($3); 303 | AST_Node_List* body = create_node_list(return_stmt); 304 | $$ = create_anonymous_function_node(params, create_type_info("any", NULL), create_compound_statement_node(body)); 305 | } 306 | | AWAIT expression { $$ = create_await_expression_node($2); } 307 | | PROMISE_TYPE '.' IDENTIFIER '(' argument_list ')' { 308 | if (strcmp($3, "all") == 0) { 309 | $$ = create_promise_all_node($5); 310 | } else { 311 | yyerror("Invalid Promise static method"); 312 | YYERROR; 313 | } 314 | } 315 | | expression '.' THEN '(' expression ')' { 316 | $$ = create_promise_then_node($1, $5); 317 | } 318 | | expression '.' CATCH '(' expression ')' { 319 | $$ = create_promise_catch_node($1, $5); 320 | } 321 | | expression '.' FINALLY '(' expression ')' { 322 | $$ = create_promise_finally_node($1, $5); 323 | } 324 | | expression '+' expression { $$ = create_binary_op_node(OP_ADD, $1, $3); } 325 | | expression '-' expression { $$ = create_binary_op_node(OP_SUB, $1, $3); } 326 | | expression '*' expression { $$ = create_binary_op_node(OP_MUL, $1, $3); } 327 | | expression '/' expression { $$ = create_binary_op_node(OP_DIV, $1, $3); } 328 | | expression '%' expression { $$ = create_binary_op_node(OP_MOD, $1, $3); } 329 | | expression CONCAT expression { $$ = create_binary_op_node(OP_CONCAT, $1, $3); } 330 | | expression EQ expression { $$ = create_binary_op_node(OP_EQ, $1, $3); } 331 | | expression NEQ expression { $$ = create_binary_op_node(OP_NEQ, $1, $3); } 332 | | expression '<' expression { $$ = create_binary_op_node(OP_LT, $1, $3); } 333 | | expression '>' expression { $$ = create_binary_op_node(OP_GT, $1, $3); } 334 | | expression LE expression { $$ = create_binary_op_node(OP_LE, $1, $3); } 335 | | expression GE expression { $$ = create_binary_op_node(OP_GE, $1, $3); } 336 | | expression AND expression { $$ = create_binary_op_node(OP_AND, $1, $3); } 337 | | expression OR expression { $$ = create_binary_op_node(OP_OR, $1, $3); } 338 | | '!' expression { $$ = create_unary_op_node(OP_NOT, $2); } 339 | | '-' expression %prec UMINUS { $$ = create_unary_op_node(OP_NEG, $2); } // Apply UMINUS precedence 340 | | IDENTIFIER '=' expression { $$ = create_assignment_node($1, $3); } 341 | | expression '.' IDENTIFIER { $$ = create_member_access_node($1, $3); } 342 | | SPREAD expression { $$ = create_spread_node($2); } 343 | ; 344 | 345 | struct_instantiation 346 | : '{' struct_init_list '}' { $$ = create_struct_init_node($2); } 347 | ; 348 | 349 | struct_init_list 350 | : struct_init_field { $$ = create_node_list($1); } 351 | | struct_init_list ',' struct_init_field { append_node($1, $3); $$ = $1; } 352 | ; 353 | 354 | struct_init_field 355 | : IDENTIFIER ':' expression { $$ = create_struct_init_field_node($1, $3); } 356 | | SPREAD expression { $$ = create_spread_node($2); } 357 | ; 358 | 359 | function_call 360 | : IDENTIFIER '(' argument_list ')' { $$ = create_function_call_node($1, $3); } 361 | | PRINT '(' argument_list ')' { $$ = create_function_call_node("print", $3); } 362 | ; 363 | 364 | argument_list 365 | : /* empty */ { $$ = create_expression_list(NULL); } 366 | | expression { $$ = create_expression_list($1); } 367 | | argument_list ',' expression { append_expression($1, $3); $$ = $1; } 368 | ; 369 | 370 | /* Renamed to for_in_statement internally, handles range/array iteration */ 371 | for_statement 372 | : FOR '(' IDENTIFIER IN expression ')' statement { /* Optional type */ 373 | AST_Node* var_decl = create_parameter_node($3, NULL); /* Type is NULL initially */ 374 | $$ = create_for_in_node(var_decl, $5, $7); 375 | } 376 | | FOR '(' IDENTIFIER ':' type IN expression ')' statement { /* Explicit type */ 377 | AST_Node* var_decl = create_parameter_node($3, $5); 378 | $$ = create_for_in_node(var_decl, $7, $9); 379 | } 380 | | FOR '(' IDENTIFIER IN range_expression ')' statement { /* Optional type for range */ 381 | AST_Node* var_decl = create_parameter_node($3, NULL); 382 | $$ = create_for_in_node(var_decl, $5, $7); 383 | } 384 | | FOR '(' IDENTIFIER ':' type IN range_expression ')' statement { /* Explicit type for range */ 385 | AST_Node* var_decl = create_parameter_node($3, $5); 386 | $$ = create_for_in_node(var_decl, $7, $9); 387 | } 388 | ; 389 | 390 | /* Renamed to for_map_statement internally, handles map iteration */ 391 | foreach_statement 392 | : FOR '(' IDENTIFIER ',' IDENTIFIER IN expression ')' statement { /* Changed FOREACH to FOR */ 393 | AST_Node* key_var = create_parameter_node($3, NULL); /* Type is NULL initially */ 394 | AST_Node* value_var = create_parameter_node($5, NULL); /* Type is NULL initially */ 395 | $$ = create_for_map_node(key_var, value_var, $7, $9); 396 | } 397 | ; 398 | 399 | range_expression 400 | : expression RANGE expression { $$ = create_range_node($1, $3); } 401 | ; 402 | 403 | literal 404 | : INT_LITERAL { $$ = create_int_literal_node($1); } 405 | | FLOAT_LITERAL { $$ = create_float_literal_node($1); } 406 | | STRING_LITERAL { $$ = create_string_literal_node($1); } 407 | | BOOL_LITERAL { $$ = create_bool_literal_node($1); } 408 | | '[' array_elements ']' { $$ = create_array_literal_node($2); } 409 | | map_literal { $$ = $1; } 410 | ; 411 | 412 | map_literal 413 | : '{' map_entries '}' { $$ = create_map_literal_node($2); } /* $2 is now node_list */ 414 | ; 415 | 416 | map_entries 417 | : map_entry { $$ = create_node_list($1); } /* Changed create_expression_list to create_node_list */ 418 | | map_entries ',' map_entry { append_node($1, $3); $$ = $1; } /* Changed append_expression to append_node */ 419 | /* | / * empty * / { $$ = create_node_list(NULL); } */ /* Disallow completely empty map {} to resolve r/r conflict with empty block */ 420 | ; 421 | 422 | map_entry 423 | : expression ':' expression { /* Changed key from STRING_LITERAL to expression */ 424 | $$ = create_map_entry_node($1, $3); 425 | } 426 | ; 427 | 428 | array_elements 429 | : /* empty */ { $$ = create_expression_list(NULL); } 430 | | expression { $$ = create_expression_list($1); } 431 | | array_elements ',' expression { append_expression($1, $3); $$ = $1; } 432 | ; 433 | 434 | %% 435 | 436 | void yyerror(const char* s) { 437 | fprintf(stderr, "Parser error at line %d: %s near '%s'\n", yylineno, s, yytext); 438 | exit(1); 439 | } 440 | -------------------------------------------------------------------------------- /src/promise.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file promise.c 3 | * @brief Implementation of the Promise API 4 | */ 5 | 6 | #include "promise.h" 7 | #include 8 | #include 9 | #include 10 | 11 | /* Maximum number of handlers in a chain */ 12 | #define MAX_HANDLERS 32 13 | 14 | /* Promise handler types */ 15 | typedef enum { 16 | HANDLER_THEN, 17 | HANDLER_CATCH, 18 | HANDLER_FINALLY 19 | } handler_type_t; 20 | 21 | /* Promise handler structure */ 22 | typedef struct { 23 | handler_type_t type; 24 | union { 25 | zn_then_handler_t then_fn; 26 | zn_catch_handler_t catch_fn; 27 | zn_finally_handler_t finally_fn; 28 | } handler; 29 | zn_promise_t *next_promise; 30 | } promise_handler_t; 31 | 32 | /* Promise structure */ 33 | struct zn_promise { 34 | zn_promise_state_t state; 35 | void *value; 36 | void *error; 37 | 38 | /* Synchronization */ 39 | zn_mutex_t mutex; 40 | zn_cond_t cond; 41 | 42 | /* Handlers */ 43 | promise_handler_t handlers[MAX_HANDLERS]; 44 | int handler_count; 45 | 46 | /* Async task */ 47 | zn_thread_t thread; 48 | bool has_thread; 49 | }; 50 | 51 | /* Argument pack structure for async functions */ 52 | typedef struct { 53 | int num_args; 54 | void *args[10]; /* Support up to 10 arguments */ 55 | } arg_pack_t; 56 | 57 | /* Resolver context structure */ 58 | typedef struct { 59 | zn_promise_resolver_t resolver; 60 | void *context; 61 | zn_promise_t *promise; 62 | } resolver_context_t; 63 | 64 | /* Forward declarations for internal functions */ 65 | static void promise_resolve_internal(zn_promise_t *promise, void *value); 66 | static void promise_reject_internal(zn_promise_t *promise, void *error); 67 | static void promise_execute_handlers(zn_promise_t *promise); 68 | static void *promise_thread_func(void *arg); 69 | static void *resolver_thread(void *arg); 70 | 71 | /* Helper function to get thread-specific data */ 72 | void *zn_thread_self_data(void) { 73 | /* This would require thread-specific storage implementation in threads.h/c */ 74 | /* For now just return NULL as a placeholder */ 75 | return NULL; 76 | } 77 | 78 | /* Helper function to set thread-specific data */ 79 | void zn_thread_set_data(zn_thread_t *thread, void *data) { 80 | /* This would require thread-specific storage implementation in threads.h/c */ 81 | /* For now just a placeholder */ 82 | (void)thread; 83 | (void)data; 84 | } 85 | 86 | /* Resolver callback wrappers */ 87 | static void resolve_callback(void *value) { 88 | zn_promise_t *promise = zn_thread_self_data(); 89 | promise_resolve_internal(promise, value); 90 | } 91 | 92 | static void reject_callback(void *error) { 93 | zn_promise_t *promise = zn_thread_self_data(); 94 | promise_reject_internal(promise, error); 95 | } 96 | 97 | /* Thread function for resolver */ 98 | static void *resolver_thread(void *arg) { 99 | resolver_context_t *ctx = (resolver_context_t *)arg; 100 | ctx->resolver(resolve_callback, reject_callback, ctx->context); 101 | free(ctx); 102 | return NULL; 103 | } 104 | 105 | zn_promise_t *zn_promise_new(zn_promise_resolver_t resolver, void *context) { 106 | if (!resolver) { 107 | return NULL; 108 | } 109 | 110 | zn_promise_t *promise = (zn_promise_t *)malloc(sizeof(zn_promise_t)); 111 | if (!promise) { 112 | return NULL; 113 | } 114 | 115 | memset(promise, 0, sizeof(zn_promise_t)); 116 | promise->state = ZN_PROMISE_PENDING; 117 | 118 | if (zn_mutex_init(&promise->mutex) != 0 || 119 | zn_cond_init(&promise->cond) != 0) { 120 | free(promise); 121 | return NULL; 122 | } 123 | 124 | /* Prepare and start the resolver thread */ 125 | if (zn_thread_init(&promise->thread) != 0) { 126 | zn_mutex_destroy(&promise->mutex); 127 | zn_cond_destroy(&promise->cond); 128 | free(promise); 129 | return NULL; 130 | } 131 | 132 | /* Store promise in thread for resolver callbacks */ 133 | zn_thread_set_data(&promise->thread, promise); 134 | 135 | resolver_context_t *ctx = (resolver_context_t *)malloc(sizeof(resolver_context_t)); 136 | if (!ctx) { 137 | zn_mutex_destroy(&promise->mutex); 138 | zn_cond_destroy(&promise->cond); 139 | free(promise); 140 | return NULL; 141 | } 142 | 143 | ctx->resolver = resolver; 144 | ctx->context = context; 145 | ctx->promise = promise; 146 | 147 | if (zn_thread_create(&promise->thread, resolver_thread, ctx) != 0) { 148 | zn_mutex_destroy(&promise->mutex); 149 | zn_cond_destroy(&promise->cond); 150 | free(ctx); 151 | free(promise); 152 | return NULL; 153 | } 154 | 155 | promise->has_thread = true; 156 | return promise; 157 | } 158 | 159 | zn_promise_t *zn_promise_resolve(void *value) { 160 | zn_promise_t *promise = (zn_promise_t *)malloc(sizeof(zn_promise_t)); 161 | if (!promise) { 162 | return NULL; 163 | } 164 | 165 | memset(promise, 0, sizeof(zn_promise_t)); 166 | promise->state = ZN_PROMISE_FULFILLED; 167 | promise->value = value; 168 | 169 | if (zn_mutex_init(&promise->mutex) != 0 || 170 | zn_cond_init(&promise->cond) != 0) { 171 | free(promise); 172 | return NULL; 173 | } 174 | 175 | /* Signal that promise is already fulfilled */ 176 | zn_cond_broadcast(&promise->cond); 177 | 178 | return promise; 179 | } 180 | 181 | zn_promise_t *zn_promise_reject(void *error) { 182 | zn_promise_t *promise = (zn_promise_t *)malloc(sizeof(zn_promise_t)); 183 | if (!promise) { 184 | return NULL; 185 | } 186 | 187 | memset(promise, 0, sizeof(zn_promise_t)); 188 | promise->state = ZN_PROMISE_REJECTED; 189 | promise->error = error; 190 | 191 | if (zn_mutex_init(&promise->mutex) != 0 || 192 | zn_cond_init(&promise->cond) != 0) { 193 | free(promise); 194 | return NULL; 195 | } 196 | 197 | /* Signal that promise is already rejected */ 198 | zn_cond_broadcast(&promise->cond); 199 | 200 | return promise; 201 | } 202 | 203 | static void promise_resolve_internal(zn_promise_t *promise, void *value) { 204 | zn_mutex_lock(&promise->mutex); 205 | 206 | /* Only pending promises can be resolved */ 207 | if (promise->state == ZN_PROMISE_PENDING) { 208 | promise->state = ZN_PROMISE_FULFILLED; 209 | promise->value = value; 210 | 211 | /* Signal to any awaiting threads */ 212 | zn_cond_broadcast(&promise->cond); 213 | 214 | /* Execute handlers */ 215 | promise_execute_handlers(promise); 216 | } 217 | 218 | zn_mutex_unlock(&promise->mutex); 219 | } 220 | 221 | static void promise_reject_internal(zn_promise_t *promise, void *error) { 222 | zn_mutex_lock(&promise->mutex); 223 | 224 | /* Only pending promises can be rejected */ 225 | if (promise->state == ZN_PROMISE_PENDING) { 226 | promise->state = ZN_PROMISE_REJECTED; 227 | promise->error = error; 228 | 229 | /* Signal to any awaiting threads */ 230 | zn_cond_broadcast(&promise->cond); 231 | 232 | /* Execute handlers */ 233 | promise_execute_handlers(promise); 234 | } 235 | 236 | zn_mutex_unlock(&promise->mutex); 237 | } 238 | 239 | static void promise_execute_handlers(zn_promise_t *promise) { 240 | for (int i = 0; i < promise->handler_count; i++) { 241 | promise_handler_t *handler = &promise->handlers[i]; 242 | zn_promise_t *next_promise = handler->next_promise; 243 | 244 | if (!next_promise) { 245 | continue; 246 | } 247 | 248 | /* Handle based on promise state and handler type */ 249 | if (promise->state == ZN_PROMISE_FULFILLED) { 250 | if (handler->type == HANDLER_THEN) { 251 | /* Execute then handler */ 252 | void *result = NULL; 253 | if (handler->handler.then_fn) { 254 | result = handler->handler.then_fn(promise->value); 255 | } else { 256 | /* If no handler, pass through the value */ 257 | result = promise->value; 258 | } 259 | promise_resolve_internal(next_promise, result); 260 | } else if (handler->type == HANDLER_FINALLY) { 261 | /* Execute finally handler */ 262 | if (handler->handler.finally_fn) { 263 | handler->handler.finally_fn(); 264 | } 265 | /* Pass through value */ 266 | promise_resolve_internal(next_promise, promise->value); 267 | } else { 268 | /* Skip catch handler, pass through value */ 269 | promise_resolve_internal(next_promise, promise->value); 270 | } 271 | } else if (promise->state == ZN_PROMISE_REJECTED) { 272 | if (handler->type == HANDLER_CATCH) { 273 | /* Execute catch handler */ 274 | void *result = NULL; 275 | if (handler->handler.catch_fn) { 276 | result = handler->handler.catch_fn(promise->error); 277 | /* Recovered from error */ 278 | promise_resolve_internal(next_promise, result); 279 | } else { 280 | /* No handler, propagate the error */ 281 | promise_reject_internal(next_promise, promise->error); 282 | } 283 | } else if (handler->type == HANDLER_FINALLY) { 284 | /* Execute finally handler */ 285 | if (handler->handler.finally_fn) { 286 | handler->handler.finally_fn(); 287 | } 288 | /* Pass through error */ 289 | promise_reject_internal(next_promise, promise->error); 290 | } else { 291 | /* Skip then handler, propagate error */ 292 | promise_reject_internal(next_promise, promise->error); 293 | } 294 | } 295 | } 296 | } 297 | 298 | zn_promise_t *zn_promise_then(zn_promise_t *promise, zn_then_handler_t on_fulfilled) { 299 | if (!promise) { 300 | return NULL; 301 | } 302 | 303 | zn_promise_t *next_promise = (zn_promise_t *)malloc(sizeof(zn_promise_t)); 304 | if (!next_promise) { 305 | return NULL; 306 | } 307 | 308 | memset(next_promise, 0, sizeof(zn_promise_t)); 309 | next_promise->state = ZN_PROMISE_PENDING; 310 | 311 | if (zn_mutex_init(&next_promise->mutex) != 0 || 312 | zn_cond_init(&next_promise->cond) != 0) { 313 | free(next_promise); 314 | return NULL; 315 | } 316 | 317 | zn_mutex_lock(&promise->mutex); 318 | 319 | if (promise->handler_count < MAX_HANDLERS) { 320 | /* Add then handler */ 321 | promise_handler_t *handler = &promise->handlers[promise->handler_count++]; 322 | handler->type = HANDLER_THEN; 323 | handler->handler.then_fn = on_fulfilled; 324 | handler->next_promise = next_promise; 325 | 326 | /* If promise already fulfilled, execute handler immediately */ 327 | if (promise->state != ZN_PROMISE_PENDING) { 328 | promise_execute_handlers(promise); 329 | } 330 | } 331 | 332 | zn_mutex_unlock(&promise->mutex); 333 | 334 | return next_promise; 335 | } 336 | 337 | zn_promise_t *zn_promise_catch(zn_promise_t *promise, zn_catch_handler_t on_rejected) { 338 | if (!promise) { 339 | return NULL; 340 | } 341 | 342 | zn_promise_t *next_promise = (zn_promise_t *)malloc(sizeof(zn_promise_t)); 343 | if (!next_promise) { 344 | return NULL; 345 | } 346 | 347 | memset(next_promise, 0, sizeof(zn_promise_t)); 348 | next_promise->state = ZN_PROMISE_PENDING; 349 | 350 | if (zn_mutex_init(&next_promise->mutex) != 0 || 351 | zn_cond_init(&next_promise->cond) != 0) { 352 | free(next_promise); 353 | return NULL; 354 | } 355 | 356 | zn_mutex_lock(&promise->mutex); 357 | 358 | if (promise->handler_count < MAX_HANDLERS) { 359 | /* Add catch handler */ 360 | promise_handler_t *handler = &promise->handlers[promise->handler_count++]; 361 | handler->type = HANDLER_CATCH; 362 | handler->handler.catch_fn = on_rejected; 363 | handler->next_promise = next_promise; 364 | 365 | /* If promise already rejected, execute handler immediately */ 366 | if (promise->state != ZN_PROMISE_PENDING) { 367 | promise_execute_handlers(promise); 368 | } 369 | } 370 | 371 | zn_mutex_unlock(&promise->mutex); 372 | 373 | return next_promise; 374 | } 375 | 376 | zn_promise_t *zn_promise_finally(zn_promise_t *promise, zn_finally_handler_t on_finally) { 377 | if (!promise) { 378 | return NULL; 379 | } 380 | 381 | zn_promise_t *next_promise = (zn_promise_t *)malloc(sizeof(zn_promise_t)); 382 | if (!next_promise) { 383 | return NULL; 384 | } 385 | 386 | memset(next_promise, 0, sizeof(zn_promise_t)); 387 | next_promise->state = ZN_PROMISE_PENDING; 388 | 389 | if (zn_mutex_init(&next_promise->mutex) != 0 || 390 | zn_cond_init(&next_promise->cond) != 0) { 391 | free(next_promise); 392 | return NULL; 393 | } 394 | 395 | zn_mutex_lock(&promise->mutex); 396 | 397 | if (promise->handler_count < MAX_HANDLERS) { 398 | /* Add finally handler */ 399 | promise_handler_t *handler = &promise->handlers[promise->handler_count++]; 400 | handler->type = HANDLER_FINALLY; 401 | handler->handler.finally_fn = on_finally; 402 | handler->next_promise = next_promise; 403 | 404 | /* If promise already settled, execute handler immediately */ 405 | if (promise->state != ZN_PROMISE_PENDING) { 406 | promise_execute_handlers(promise); 407 | } 408 | } 409 | 410 | zn_mutex_unlock(&promise->mutex); 411 | 412 | return next_promise; 413 | } 414 | 415 | void *zn_promise_await(zn_promise_t *promise) { 416 | if (!promise) { 417 | return NULL; 418 | } 419 | 420 | zn_mutex_lock(&promise->mutex); 421 | 422 | /* Wait while promise is pending */ 423 | while (promise->state == ZN_PROMISE_PENDING) { 424 | zn_cond_wait(&promise->cond, &promise->mutex); 425 | } 426 | 427 | void *result = (promise->state == ZN_PROMISE_FULFILLED) ? 428 | promise->value : NULL; 429 | 430 | zn_mutex_unlock(&promise->mutex); 431 | 432 | return result; 433 | } 434 | 435 | zn_promise_state_t zn_promise_state(zn_promise_t *promise) { 436 | if (!promise) { 437 | return ZN_PROMISE_REJECTED; 438 | } 439 | 440 | zn_mutex_lock(&promise->mutex); 441 | zn_promise_state_t state = promise->state; 442 | zn_mutex_unlock(&promise->mutex); 443 | 444 | return state; 445 | } 446 | 447 | void zn_promise_free(zn_promise_t *promise) { 448 | if (!promise) { 449 | return; 450 | } 451 | 452 | /* Wait for promise thread to complete */ 453 | if (promise->has_thread) { 454 | zn_thread_join(&promise->thread, NULL); 455 | } 456 | 457 | zn_mutex_destroy(&promise->mutex); 458 | zn_cond_destroy(&promise->cond); 459 | 460 | free(promise); 461 | } 462 | 463 | /* Helper function to pack arguments */ 464 | void *_zn_async_pack(void *arg, ...) { 465 | arg_pack_t *pack = (arg_pack_t *)malloc(sizeof(arg_pack_t)); 466 | if (!pack) { 467 | return NULL; 468 | } 469 | 470 | memset(pack, 0, sizeof(arg_pack_t)); 471 | 472 | /* First argument */ 473 | if (arg) { 474 | pack->args[0] = arg; 475 | pack->num_args = 1; 476 | } 477 | 478 | /* Parse variable arguments */ 479 | va_list args; 480 | va_start(args, arg); 481 | 482 | for (int i = 1; i < 10; i++) { 483 | void *next_arg = va_arg(args, void *); 484 | if (!next_arg) { 485 | break; 486 | } 487 | 488 | pack->args[i] = next_arg; 489 | pack->num_args++; 490 | } 491 | 492 | va_end(args); 493 | 494 | return pack; 495 | } 496 | 497 | /* Helper structure for async call */ 498 | typedef struct { 499 | void *(*func)(void *); 500 | void *args; 501 | zn_promise_t *promise; 502 | } async_call_ctx_t; 503 | 504 | /* Thread function for async call */ 505 | static void *async_thread_func(void *arg) { 506 | async_call_ctx_t *ctx = (async_call_ctx_t *)arg; 507 | void *result = ctx->func(ctx->args); 508 | 509 | /* Resolve the promise with the result */ 510 | promise_resolve_internal(ctx->promise, result); 511 | 512 | free(ctx); 513 | return NULL; 514 | } 515 | 516 | /* Helper function for async call */ 517 | zn_promise_t *_zn_async_call(void *(*func)(void *), void *args) { 518 | if (!func) { 519 | return zn_promise_reject(NULL); 520 | } 521 | 522 | zn_promise_t *promise = (zn_promise_t *)malloc(sizeof(zn_promise_t)); 523 | if (!promise) { 524 | return NULL; 525 | } 526 | 527 | memset(promise, 0, sizeof(zn_promise_t)); 528 | promise->state = ZN_PROMISE_PENDING; 529 | 530 | if (zn_mutex_init(&promise->mutex) != 0 || 531 | zn_cond_init(&promise->cond) != 0) { 532 | free(promise); 533 | return NULL; 534 | } 535 | 536 | /* Create context for thread */ 537 | async_call_ctx_t *ctx = (async_call_ctx_t *)malloc(sizeof(async_call_ctx_t)); 538 | if (!ctx) { 539 | zn_mutex_destroy(&promise->mutex); 540 | zn_cond_destroy(&promise->cond); 541 | free(promise); 542 | return NULL; 543 | } 544 | 545 | ctx->func = func; 546 | ctx->args = args; 547 | ctx->promise = promise; 548 | 549 | /* Create and start thread */ 550 | if (zn_thread_init(&promise->thread) != 0 || 551 | zn_thread_create(&promise->thread, async_thread_func, ctx) != 0) { 552 | zn_mutex_destroy(&promise->mutex); 553 | zn_cond_destroy(&promise->cond); 554 | free(ctx); 555 | free(promise); 556 | return NULL; 557 | } 558 | 559 | promise->has_thread = true; 560 | 561 | return promise; 562 | } 563 | -------------------------------------------------------------------------------- /src/promise.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file promise.h 3 | * @brief Promise implementation similar to JavaScript Promises 4 | */ 5 | 6 | #ifndef ZENO_PROMISE_H 7 | #define ZENO_PROMISE_H 8 | 9 | #include "threads.h" 10 | #include 11 | #include 12 | 13 | /** 14 | * @brief Promise states 15 | */ 16 | typedef enum { 17 | ZN_PROMISE_PENDING, 18 | ZN_PROMISE_FULFILLED, 19 | ZN_PROMISE_REJECTED 20 | } zn_promise_state_t; 21 | 22 | /** 23 | * @brief Promise structure (opaque type) 24 | */ 25 | typedef struct zn_promise zn_promise_t; 26 | 27 | /** 28 | * @brief Promise resolver function type 29 | */ 30 | typedef void (*zn_promise_resolver_t)(void (*resolve)(void *value), 31 | void (*reject)(void *error), 32 | void *context); 33 | 34 | /** 35 | * @brief Fulfill handler function type 36 | */ 37 | typedef void *(*zn_then_handler_t)(void *value); 38 | 39 | /** 40 | * @brief Reject handler function type 41 | */ 42 | typedef void *(*zn_catch_handler_t)(void *error); 43 | 44 | /** 45 | * @brief Finally handler function type 46 | */ 47 | typedef void (*zn_finally_handler_t)(void); 48 | 49 | /** 50 | * @brief Create a new promise 51 | * @param resolver Function that will try to resolve the promise 52 | * @param context User context for the resolver 53 | * @return New promise 54 | */ 55 | zn_promise_t *zn_promise_new(zn_promise_resolver_t resolver, void *context); 56 | 57 | /** 58 | * @brief Create a resolved promise 59 | * @param value Value to resolve with 60 | * @return New resolved promise 61 | */ 62 | zn_promise_t *zn_promise_resolve(void *value); 63 | 64 | /** 65 | * @brief Create a rejected promise 66 | * @param error Error to reject with 67 | * @return New rejected promise 68 | */ 69 | zn_promise_t *zn_promise_reject(void *error); 70 | 71 | /** 72 | * @brief Chain a fulfillment handler to a promise 73 | * @param promise The promise 74 | * @param on_fulfilled Handler for fulfillment 75 | * @return A new promise 76 | */ 77 | zn_promise_t *zn_promise_then(zn_promise_t *promise, zn_then_handler_t on_fulfilled); 78 | 79 | /** 80 | * @brief Chain a rejection handler to a promise 81 | * @param promise The promise 82 | * @param on_rejected Handler for rejection 83 | * @return A new promise 84 | */ 85 | zn_promise_t *zn_promise_catch(zn_promise_t *promise, zn_catch_handler_t on_rejected); 86 | 87 | /** 88 | * @brief Add a finally handler to a promise 89 | * @param promise The promise 90 | * @param on_finally Handler to run when promise settles 91 | * @return A new promise 92 | */ 93 | zn_promise_t *zn_promise_finally(zn_promise_t *promise, zn_finally_handler_t on_finally); 94 | 95 | /** 96 | * @brief Wait for a promise to settle 97 | * @param promise The promise 98 | * @return Value if fulfilled, NULL if rejected 99 | */ 100 | void *zn_promise_await(zn_promise_t *promise); 101 | 102 | /** 103 | * @brief Returns the current state of the promise 104 | * @param promise The promise 105 | * @return Current state 106 | */ 107 | zn_promise_state_t zn_promise_state(zn_promise_t *promise); 108 | 109 | /** 110 | * @brief Free resources used by a promise 111 | * @param promise The promise 112 | */ 113 | void zn_promise_free(zn_promise_t *promise); 114 | 115 | /** 116 | * @brief Macro to declare an async function 117 | * @param return_type Return type of the function 118 | * @param name Function name 119 | * @param ... Function parameters 120 | */ 121 | #define ZN_ASYNC(return_type, name, ...) \ 122 | zn_promise_t *name(__VA_ARGS__) 123 | 124 | /** 125 | * @brief Macro to create a new async task from a function 126 | * @param func Function to execute asynchronously 127 | * @param ... Function arguments 128 | * @return Promise that will be fulfilled with the function result 129 | */ 130 | #define zn_async(func, ...) \ 131 | _zn_async_call((void *(*)(void *))func, _zn_async_pack(__VA_ARGS__)) 132 | 133 | /* Helper function for zn_async macro */ 134 | zn_promise_t *_zn_async_call(void *(*func)(void *), void *args); 135 | 136 | /* Helper function for packing arguments */ 137 | void *_zn_async_pack(void *arg, ...); 138 | 139 | /** 140 | * @brief Executes a promise and waits for its result 141 | * @param promise The promise to await 142 | * @return Result of the promise 143 | */ 144 | #define zn_await(promise) \ 145 | zn_promise_await(promise) 146 | 147 | #endif /* ZENO_PROMISE_H */ 148 | -------------------------------------------------------------------------------- /src/socket.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file socket.c 3 | * @brief Implementation of simple wrapper for C socket API 4 | */ 5 | 6 | #include "socket.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "error_reporter.h" 14 | 15 | /** 16 | * Create a success result 17 | */ 18 | static socket_result_t socket_success(void) { 19 | return (socket_result_t){ 20 | .success = true, 21 | .error_msg = NULL, 22 | .error_code = 0 23 | }; 24 | } 25 | 26 | /** 27 | * Create an error result 28 | */ 29 | static socket_result_t socket_error(const char *error_msg) { 30 | ZENO_LOG_ERROR(error_msg); 31 | return (socket_result_t){ 32 | .success = false, 33 | .error_msg = error_msg, 34 | .error_code = errno 35 | }; 36 | } 37 | 38 | socket_result_t socket_create(socket_t *sock, socket_type_t type) { 39 | if (!sock) { 40 | return socket_error("Invalid socket pointer"); 41 | } 42 | 43 | int sock_type = (type == SOCKET_TCP) ? SOCK_STREAM : SOCK_DGRAM; 44 | int protocol = (type == SOCKET_TCP) ? IPPROTO_TCP : IPPROTO_UDP; 45 | 46 | int fd = socket(AF_INET, sock_type, protocol); 47 | if (fd < 0) { 48 | return socket_error("Failed to create socket"); 49 | } 50 | 51 | // Initialize socket structure 52 | memset(sock, 0, sizeof(socket_t)); 53 | sock->fd = fd; 54 | sock->type = type; 55 | sock->is_server = false; 56 | sock->is_connected = false; 57 | sock->is_nonblocking = false; 58 | 59 | // Set socket options 60 | int option = 1; 61 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) < 0) { 62 | close(fd); 63 | return socket_error("Failed to set socket options"); 64 | } 65 | 66 | return socket_success(); 67 | } 68 | 69 | socket_result_t socket_set_nonblocking(socket_t *sock, bool nonblocking) { 70 | if (!sock || sock->fd < 0) { 71 | return socket_error("Invalid socket"); 72 | } 73 | 74 | int flags = fcntl(sock->fd, F_GETFL, 0); 75 | if (flags < 0) { 76 | return socket_error("Failed to get socket flags"); 77 | } 78 | 79 | if (nonblocking) { 80 | flags |= O_NONBLOCK; 81 | } else { 82 | flags &= ~O_NONBLOCK; 83 | } 84 | 85 | if (fcntl(sock->fd, F_SETFL, flags) < 0) { 86 | return socket_error("Failed to set socket flags"); 87 | } 88 | 89 | sock->is_nonblocking = nonblocking; 90 | return socket_success(); 91 | } 92 | 93 | socket_result_t socket_bind(socket_t *sock, int port) { 94 | if (!sock || sock->fd < 0) { 95 | return socket_error("Invalid socket"); 96 | } 97 | 98 | struct sockaddr_in addr; 99 | memset(&addr, 0, sizeof(addr)); 100 | addr.sin_family = AF_INET; 101 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 102 | addr.sin_port = htons(port); 103 | 104 | if (bind(sock->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 105 | return socket_error("Failed to bind socket"); 106 | } 107 | 108 | sock->addr = addr; 109 | sock->is_server = true; 110 | return socket_success(); 111 | } 112 | 113 | socket_result_t socket_listen(socket_t *sock, int backlog) { 114 | if (!sock || sock->fd < 0) { 115 | return socket_error("Invalid socket"); 116 | } 117 | 118 | if (!sock->is_server) { 119 | return socket_error("Socket is not bound to an address"); 120 | } 121 | 122 | if (listen(sock->fd, backlog) < 0) { 123 | return socket_error("Failed to listen on socket"); 124 | } 125 | 126 | return socket_success(); 127 | } 128 | 129 | socket_result_t socket_accept(socket_t *server_sock, socket_t *client_sock) { 130 | if (!server_sock || server_sock->fd < 0 || !client_sock) { 131 | return socket_error("Invalid socket"); 132 | } 133 | 134 | if (!server_sock->is_server) { 135 | return socket_error("Not a server socket"); 136 | } 137 | 138 | struct sockaddr_in client_addr; 139 | socklen_t addr_len = sizeof(client_addr); 140 | 141 | int client_fd = accept(server_sock->fd, (struct sockaddr *)&client_addr, &addr_len); 142 | if (client_fd < 0) { 143 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 144 | return socket_error("No pending connections"); 145 | } 146 | return socket_error("Failed to accept connection"); 147 | } 148 | 149 | // Initialize client socket 150 | memset(client_sock, 0, sizeof(socket_t)); 151 | client_sock->fd = client_fd; 152 | client_sock->type = server_sock->type; 153 | client_sock->addr = client_addr; 154 | client_sock->is_server = false; 155 | client_sock->is_connected = true; 156 | client_sock->is_nonblocking = false; 157 | 158 | return socket_success(); 159 | } 160 | 161 | socket_result_t socket_connect(socket_t *sock, const char *hostname, int port) { 162 | if (!sock || sock->fd < 0 || !hostname) { 163 | return socket_error("Invalid socket or hostname"); 164 | } 165 | 166 | if (sock->is_server) { 167 | return socket_error("Cannot connect with a server socket"); 168 | } 169 | 170 | struct hostent *host = gethostbyname(hostname); 171 | if (!host) { 172 | return socket_error("Failed to resolve hostname"); 173 | } 174 | 175 | struct sockaddr_in addr; 176 | memset(&addr, 0, sizeof(addr)); 177 | addr.sin_family = AF_INET; 178 | addr.sin_port = htons(port); 179 | addr.sin_addr = *((struct in_addr *)host->h_addr); 180 | 181 | if (connect(sock->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 182 | if (sock->is_nonblocking && (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK)) { 183 | // This is expected for non-blocking sockets and not an error 184 | sock->addr = addr; 185 | return socket_success(); 186 | } 187 | return socket_error("Failed to connect to server"); 188 | } 189 | 190 | sock->addr = addr; 191 | sock->is_connected = true; 192 | return socket_success(); 193 | } 194 | 195 | socket_result_t socket_send(socket_t *sock, const void *data, size_t size, size_t *bytes_sent) { 196 | if (!sock || sock->fd < 0 || !data) { 197 | return socket_error("Invalid socket or data"); 198 | } 199 | 200 | if (!sock->is_connected && sock->type == SOCKET_TCP) { 201 | return socket_error("Socket is not connected"); 202 | } 203 | 204 | ssize_t sent = send(sock->fd, data, size, 0); 205 | if (sent < 0) { 206 | if (sock->is_nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { 207 | if (bytes_sent) { 208 | *bytes_sent = 0; 209 | } 210 | return socket_success(); 211 | } 212 | return socket_error("Failed to send data"); 213 | } 214 | 215 | if (bytes_sent) { 216 | *bytes_sent = (size_t)sent; 217 | } 218 | return socket_success(); 219 | } 220 | 221 | socket_result_t socket_receive(socket_t *sock, void *buffer, size_t size, size_t *bytes_received) { 222 | if (!sock || sock->fd < 0 || !buffer) { 223 | return socket_error("Invalid socket or buffer"); 224 | } 225 | 226 | if (!sock->is_connected && sock->type == SOCKET_TCP) { 227 | return socket_error("Socket is not connected"); 228 | } 229 | 230 | ssize_t received = recv(sock->fd, buffer, size, 0); 231 | if (received < 0) { 232 | if (sock->is_nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { 233 | if (bytes_received) { 234 | *bytes_received = 0; 235 | } 236 | return socket_success(); 237 | } 238 | return socket_error("Failed to receive data"); 239 | } 240 | 241 | if (bytes_received) { 242 | *bytes_received = (size_t)received; 243 | } 244 | return socket_success(); 245 | } 246 | 247 | socket_result_t socket_close(socket_t *sock) { 248 | if (!sock || sock->fd < 0) { 249 | return socket_error("Invalid socket"); 250 | } 251 | 252 | if (close(sock->fd) < 0) { 253 | return socket_error("Failed to close socket"); 254 | } 255 | 256 | sock->fd = -1; 257 | sock->is_connected = false; 258 | sock->is_server = false; 259 | return socket_success(); 260 | } 261 | 262 | socket_result_t socket_async_connect(socket_t *sock, const char* hostname, int port, int timeout_ms) { 263 | socket_result_t res = socket_set_nonblocking(sock, true); 264 | if (!res.success) return res; 265 | 266 | res = socket_connect(sock, hostname, port); 267 | if (res.success) { 268 | return res; 269 | } 270 | if (errno != EINPROGRESS) { 271 | return res; 272 | } 273 | 274 | fd_set writefds; 275 | FD_ZERO(&writefds); 276 | FD_SET(sock->fd, &writefds); 277 | 278 | struct timeval tv; 279 | tv.tv_sec = timeout_ms / 1000; 280 | tv.tv_usec = (timeout_ms % 1000) * 1000; 281 | 282 | int sel = select(sock->fd + 1, NULL, &writefds, NULL, &tv); 283 | if (sel > 0 && FD_ISSET(sock->fd, &writefds)) { 284 | int err; 285 | socklen_t len = sizeof(err); 286 | getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &err, &len); 287 | if (err == 0) { 288 | sock->is_connected = true; 289 | return socket_success(); 290 | } else { 291 | errno = err; 292 | return socket_error("Asynchronous connect failed"); 293 | } 294 | } else if (sel == 0) { 295 | return socket_error("Asynchronous connect timed out"); 296 | } 297 | return socket_error("Select error during asynchronous connect"); 298 | } 299 | 300 | const char *socket_get_error_string(socket_result_t result) { 301 | if (result.success) { 302 | return "Success"; 303 | } 304 | 305 | if (result.error_msg) { 306 | return result.error_msg; 307 | } 308 | 309 | return strerror(result.error_code); 310 | } 311 | -------------------------------------------------------------------------------- /src/socket.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file socket.h 3 | * @brief Simple wrapper for C socket API 4 | */ 5 | 6 | #ifndef ZENO_SOCKET_H 7 | #define ZENO_SOCKET_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /** 15 | * Socket type enumeration 16 | */ 17 | typedef enum { 18 | SOCKET_TCP, /**< TCP socket */ 19 | SOCKET_UDP /**< UDP socket */ 20 | } socket_type_t; 21 | 22 | /** 23 | * Socket handle structure 24 | */ 25 | typedef struct { 26 | int fd; /**< Socket file descriptor */ 27 | socket_type_t type; /**< Socket type (TCP/UDP) */ 28 | struct sockaddr_in addr; /**< Socket address */ 29 | bool is_server; /**< Is this a server socket? */ 30 | bool is_connected; /**< Is the socket connected? */ 31 | bool is_nonblocking; /**< Is the socket non-blocking? */ 32 | } socket_t; 33 | 34 | /** 35 | * Socket result structure 36 | */ 37 | typedef struct { 38 | bool success; /**< Operation success flag */ 39 | const char *error_msg; /**< Error message if success is false */ 40 | int error_code; /**< Error code if success is false */ 41 | } socket_result_t; 42 | 43 | /** 44 | * Create a new socket 45 | * 46 | * @param socket Pointer to socket_t structure to initialize 47 | * @param type Socket type (SOCKET_TCP or SOCKET_UDP) 48 | * @return Result of the operation 49 | */ 50 | socket_result_t socket_create(socket_t *socket, socket_type_t type); 51 | 52 | /** 53 | * Set a socket to non-blocking mode 54 | * 55 | * @param socket Socket to set as non-blocking 56 | * @param nonblocking true to set as non-blocking, false for blocking 57 | * @return Result of the operation 58 | */ 59 | socket_result_t socket_set_nonblocking(socket_t *socket, bool nonblocking); 60 | 61 | /** 62 | * Bind a socket to a specific port on all interfaces 63 | * 64 | * @param socket Socket to bind 65 | * @param port Port number to bind to 66 | * @return Result of the operation 67 | */ 68 | socket_result_t socket_bind(socket_t *socket, int port); 69 | 70 | /** 71 | * Start listening for connections on a socket 72 | * 73 | * @param socket Socket to listen on (must be bound first) 74 | * @param backlog Maximum queue length for pending connections 75 | * @return Result of the operation 76 | */ 77 | socket_result_t socket_listen(socket_t *socket, int backlog); 78 | 79 | /** 80 | * Accept a new connection on a listening socket 81 | * 82 | * @param server_socket Socket that is listening 83 | * @param client_socket Pointer to socket_t structure to initialize with the new connection 84 | * @return Result of the operation 85 | */ 86 | socket_result_t socket_accept(socket_t *server_socket, socket_t *client_socket); 87 | 88 | /** 89 | * Connect to a server 90 | * 91 | * @param socket Socket to use for the connection 92 | * @param hostname Hostname or IP address to connect to 93 | * @param port Port number to connect to 94 | * @return Result of the operation 95 | */ 96 | socket_result_t socket_connect(socket_t *socket, const char *hostname, int port); 97 | 98 | /** 99 | * Send data over a socket 100 | * 101 | * @param socket Socket to send data on 102 | * @param data Pointer to data to send 103 | * @param size Size of data to send in bytes 104 | * @param bytes_sent Pointer to store number of bytes actually sent (can be NULL) 105 | * @return Result of the operation 106 | */ 107 | socket_result_t socket_send(socket_t *socket, const void *data, size_t size, size_t *bytes_sent); 108 | 109 | /** 110 | * Receive data from a socket 111 | * 112 | * @param socket Socket to receive data from 113 | * @param buffer Pointer to buffer to store received data 114 | * @param size Size of buffer in bytes 115 | * @param bytes_received Pointer to store number of bytes actually received (can be NULL) 116 | * @return Result of the operation 117 | */ 118 | socket_result_t socket_receive(socket_t *socket, void *buffer, size_t size, size_t *bytes_received); 119 | 120 | /** 121 | * Close a socket 122 | * 123 | * @param socket Socket to close 124 | * @return Result of the operation 125 | */ 126 | socket_result_t socket_close(socket_t *socket); 127 | 128 | /** 129 | * Get string representation of a socket error 130 | * 131 | * @param result Socket result containing the error 132 | * @return String representation of the error 133 | */ 134 | const char *socket_get_error_string(socket_result_t result); 135 | 136 | socket_result_t socket_async_connect(socket_t *socket, const char* hostname, int port, int timeout_ms); 137 | 138 | #endif /* ZENO_SOCKET_H */ 139 | -------------------------------------------------------------------------------- /src/symtab.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "symtab.h" 5 | 6 | // Initialize symbol table 7 | SymbolTable* init_symbol_table() { 8 | SymbolTable* table = (SymbolTable*)malloc(sizeof(SymbolTable)); 9 | if (!table) { 10 | fprintf(stderr, "Memory allocation failed\n"); 11 | exit(1); 12 | } 13 | 14 | // Create global scope 15 | table->current_scope = (SymbolScope*)malloc(sizeof(SymbolScope)); 16 | if (!table->current_scope) { 17 | fprintf(stderr, "Memory allocation failed\n"); 18 | free(table); 19 | exit(1); 20 | } 21 | 22 | table->current_scope->entries = NULL; 23 | table->current_scope->parent = NULL; 24 | 25 | return table; 26 | } 27 | 28 | // Enter a new scope 29 | void enter_scope(SymbolTable* table) { 30 | if (!table) return; 31 | 32 | SymbolScope* new_scope = (SymbolScope*)malloc(sizeof(SymbolScope)); 33 | if (!new_scope) { 34 | fprintf(stderr, "Memory allocation failed\n"); 35 | exit(1); 36 | } 37 | 38 | new_scope->entries = NULL; 39 | new_scope->parent = table->current_scope; 40 | table->current_scope = new_scope; 41 | } 42 | 43 | // Leave current scope 44 | void leave_scope(SymbolTable* table) { 45 | if (!table || !table->current_scope || !table->current_scope->parent) { 46 | return; // Cannot leave global scope 47 | } 48 | 49 | SymbolScope* old_scope = table->current_scope; 50 | table->current_scope = old_scope->parent; 51 | 52 | // Clean up entries in the old scope 53 | SymbolEntry* entry = old_scope->entries; 54 | while (entry) { 55 | SymbolEntry* next = entry->next; 56 | free(entry->name); 57 | free(entry); 58 | entry = next; 59 | } 60 | 61 | free(old_scope); 62 | } 63 | 64 | // Add symbol to current scope 65 | void add_symbol(SymbolTable* table, char* name, SymbolType type, TypeInfo* type_info) { // Added type_info parameter 66 | if (!table || !table->current_scope || !name) return; 67 | 68 | // Check if symbol already exists in current scope 69 | SymbolEntry* entry = table->current_scope->entries; 70 | while (entry) { 71 | if (strcmp(entry->name, name) == 0) { 72 | fprintf(stderr, "Symbol %s already defined in current scope\n", name); 73 | return; 74 | } 75 | entry = entry->next; 76 | } 77 | 78 | // Create new entry 79 | SymbolEntry* new_entry = (SymbolEntry*)malloc(sizeof(SymbolEntry)); 80 | if (!new_entry) { 81 | fprintf(stderr, "Memory allocation failed\n"); 82 | exit(1); 83 | } 84 | 85 | new_entry->name = strdup(name); 86 | new_entry->type = type; 87 | new_entry->type_info = type_info; // Store the type info 88 | new_entry->next = table->current_scope->entries; 89 | table->current_scope->entries = new_entry; 90 | } 91 | 92 | // Look up symbol in all accessible scopes 93 | SymbolEntry* lookup_symbol(SymbolTable* table, char* name) { 94 | if (!table || !name) return NULL; 95 | 96 | // Search in current scope and all parent scopes 97 | SymbolScope* scope = table->current_scope; 98 | while (scope) { 99 | SymbolEntry* entry = scope->entries; 100 | while (entry) { 101 | if (strcmp(entry->name, name) == 0) { 102 | return entry; 103 | } 104 | entry = entry->next; 105 | } 106 | scope = scope->parent; 107 | } 108 | 109 | return NULL; // Symbol not found 110 | } 111 | 112 | // Clean up symbol table 113 | void cleanup_symbol_table(SymbolTable* table) { 114 | if (!table) return; 115 | 116 | // Leave all scopes until we reach the global scope 117 | while (table->current_scope && table->current_scope->parent) { 118 | leave_scope(table); 119 | } 120 | 121 | // Clean up global scope 122 | if (table->current_scope) { 123 | SymbolEntry* entry = table->current_scope->entries; 124 | while (entry) { 125 | SymbolEntry* next = entry->next; 126 | free(entry->name); 127 | free(entry); 128 | entry = next; 129 | } 130 | free(table->current_scope); 131 | } 132 | 133 | free(table); 134 | } 135 | -------------------------------------------------------------------------------- /src/symtab.h: -------------------------------------------------------------------------------- 1 | #ifndef SYMTAB_H 2 | #define SYMTAB_H 3 | 4 | #include "ast.h" // Include ast.h for TypeInfo 5 | 6 | // Symbol types 7 | typedef enum { 8 | SYMBOL_VARIABLE, 9 | SYMBOL_FUNCTION, 10 | SYMBOL_STRUCT, 11 | SYMBOL_TYPE 12 | } SymbolType; 13 | 14 | // Symbol entry 15 | typedef struct SymbolEntry { 16 | char* name; 17 | SymbolType type; 18 | TypeInfo* type_info; // Added to store detailed type information 19 | struct SymbolEntry* next; 20 | } SymbolEntry; 21 | 22 | // Symbol table scope 23 | typedef struct SymbolScope { 24 | SymbolEntry* entries; 25 | struct SymbolScope* parent; 26 | } SymbolScope; 27 | 28 | // Symbol table 29 | typedef struct { 30 | SymbolScope* current_scope; 31 | } SymbolTable; 32 | 33 | // Initialize symbol table 34 | SymbolTable* init_symbol_table(); 35 | 36 | // Enter a new scope 37 | void enter_scope(SymbolTable* table); 38 | 39 | // Leave current scope 40 | void leave_scope(SymbolTable* table); 41 | 42 | // Add symbol to current scope 43 | void add_symbol(SymbolTable* table, char* name, SymbolType type, TypeInfo* type_info); // Added type_info parameter 44 | 45 | // Look up symbol in all accessible scopes 46 | SymbolEntry* lookup_symbol(SymbolTable* table, char* name); 47 | 48 | // Clean up symbol table 49 | void cleanup_symbol_table(SymbolTable* table); 50 | 51 | #endif // SYMTAB_H 52 | -------------------------------------------------------------------------------- /src/threads.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file threads.c 3 | * @brief Implementation of the threads wrapper 4 | */ 5 | 6 | #include "threads.h" 7 | #include 8 | #include 9 | #include 10 | 11 | /* Thread pool implementation */ 12 | struct task_node { 13 | zn_task_func_t func; 14 | void *arg; 15 | struct task_node *next; 16 | }; 17 | 18 | struct zn_thread_pool { 19 | zn_thread_t *threads; 20 | size_t num_threads; 21 | struct task_node *task_queue; 22 | struct task_node *task_queue_tail; 23 | zn_mutex_t queue_mutex; 24 | zn_cond_t queue_cond; 25 | bool shutdown; 26 | }; 27 | 28 | /* Thread pool worker function */ 29 | static void *thread_pool_worker(void *arg) { 30 | zn_thread_pool_t *pool = (zn_thread_pool_t *)arg; 31 | 32 | while (1) { 33 | zn_task_func_t func = NULL; 34 | void *task_arg = NULL; 35 | 36 | zn_mutex_lock(&pool->queue_mutex); 37 | 38 | while (pool->task_queue == NULL && !pool->shutdown) { 39 | zn_cond_wait(&pool->queue_cond, &pool->queue_mutex); 40 | } 41 | 42 | if (pool->shutdown && pool->task_queue == NULL) { 43 | zn_mutex_unlock(&pool->queue_mutex); 44 | break; 45 | } 46 | 47 | struct task_node *task = pool->task_queue; 48 | if (task) { 49 | pool->task_queue = task->next; 50 | if (pool->task_queue == NULL) { 51 | pool->task_queue_tail = NULL; 52 | } 53 | 54 | func = task->func; 55 | task_arg = task->arg; 56 | free(task); 57 | } 58 | 59 | zn_mutex_unlock(&pool->queue_mutex); 60 | 61 | if (func) { 62 | func(task_arg); 63 | } 64 | } 65 | 66 | return NULL; 67 | } 68 | 69 | int zn_thread_init(zn_thread_t *thread) { 70 | if (!thread) { 71 | return -1; 72 | } 73 | 74 | memset(thread, 0, sizeof(zn_thread_t)); 75 | thread->is_running = false; 76 | 77 | return 0; 78 | } 79 | 80 | int zn_thread_create(zn_thread_t *thread, void *(*function)(void *), void *arg) { 81 | if (!thread || !function) { 82 | return -1; 83 | } 84 | 85 | thread->function = function; 86 | thread->arg = arg; 87 | 88 | int result = pthread_create(&thread->thread, NULL, function, arg); 89 | if (result == 0) { 90 | thread->is_running = true; 91 | } 92 | 93 | return result; 94 | } 95 | 96 | int zn_thread_join(zn_thread_t *thread, void **result) { 97 | if (!thread || !thread->is_running) { 98 | return -1; 99 | } 100 | 101 | int ret = pthread_join(thread->thread, result ? result : &thread->result); 102 | if (ret == 0) { 103 | thread->is_running = false; 104 | } 105 | 106 | return ret; 107 | } 108 | 109 | int zn_thread_detach(zn_thread_t *thread) { 110 | if (!thread || !thread->is_running) { 111 | return -1; 112 | } 113 | 114 | int ret = pthread_detach(thread->thread); 115 | if (ret == 0) { 116 | thread->is_running = false; 117 | } 118 | 119 | return ret; 120 | } 121 | 122 | void zn_thread_destroy(zn_thread_t *thread) { 123 | if (!thread) { 124 | return; 125 | } 126 | 127 | if (thread->is_running) { 128 | zn_thread_join(thread, NULL); 129 | } 130 | 131 | memset(thread, 0, sizeof(zn_thread_t)); 132 | } 133 | 134 | int zn_mutex_init(zn_mutex_t *mutex) { 135 | if (!mutex) { 136 | return -1; 137 | } 138 | 139 | int result = pthread_mutex_init(&mutex->mutex, NULL); 140 | if (result == 0) { 141 | mutex->initialized = true; 142 | } 143 | 144 | return result; 145 | } 146 | 147 | int zn_mutex_lock(zn_mutex_t *mutex) { 148 | if (!mutex || !mutex->initialized) { 149 | return -1; 150 | } 151 | 152 | return pthread_mutex_lock(&mutex->mutex); 153 | } 154 | 155 | int zn_mutex_trylock(zn_mutex_t *mutex) { 156 | if (!mutex || !mutex->initialized) { 157 | return -1; 158 | } 159 | 160 | return pthread_mutex_trylock(&mutex->mutex); 161 | } 162 | 163 | int zn_mutex_unlock(zn_mutex_t *mutex) { 164 | if (!mutex || !mutex->initialized) { 165 | return -1; 166 | } 167 | 168 | return pthread_mutex_unlock(&mutex->mutex); 169 | } 170 | 171 | int zn_mutex_destroy(zn_mutex_t *mutex) { 172 | if (!mutex || !mutex->initialized) { 173 | return -1; 174 | } 175 | 176 | int result = pthread_mutex_destroy(&mutex->mutex); 177 | if (result == 0) { 178 | mutex->initialized = false; 179 | } 180 | 181 | return result; 182 | } 183 | 184 | int zn_cond_init(zn_cond_t *cond) { 185 | if (!cond) { 186 | return -1; 187 | } 188 | 189 | int result = pthread_cond_init(&cond->cond, NULL); 190 | if (result == 0) { 191 | cond->initialized = true; 192 | } 193 | 194 | return result; 195 | } 196 | 197 | int zn_cond_wait(zn_cond_t *cond, zn_mutex_t *mutex) { 198 | if (!cond || !mutex || !cond->initialized || !mutex->initialized) { 199 | return -1; 200 | } 201 | 202 | return pthread_cond_wait(&cond->cond, &mutex->mutex); 203 | } 204 | 205 | int zn_cond_signal(zn_cond_t *cond) { 206 | if (!cond || !cond->initialized) { 207 | return -1; 208 | } 209 | 210 | return pthread_cond_signal(&cond->cond); 211 | } 212 | 213 | int zn_cond_broadcast(zn_cond_t *cond) { 214 | if (!cond || !cond->initialized) { 215 | return -1; 216 | } 217 | 218 | return pthread_cond_broadcast(&cond->cond); 219 | } 220 | 221 | int zn_cond_destroy(zn_cond_t *cond) { 222 | if (!cond || !cond->initialized) { 223 | return -1; 224 | } 225 | 226 | int result = pthread_cond_destroy(&cond->cond); 227 | if (result == 0) { 228 | cond->initialized = false; 229 | } 230 | 231 | return result; 232 | } 233 | 234 | zn_thread_pool_t *zn_thread_pool_create(size_t num_threads) { 235 | if (num_threads == 0) { 236 | num_threads = zn_get_num_cores(); 237 | } 238 | 239 | zn_thread_pool_t *pool = (zn_thread_pool_t *)malloc(sizeof(zn_thread_pool_t)); 240 | if (!pool) { 241 | return NULL; 242 | } 243 | 244 | memset(pool, 0, sizeof(zn_thread_pool_t)); 245 | 246 | pool->threads = (zn_thread_t *)malloc(num_threads * sizeof(zn_thread_t)); 247 | if (!pool->threads) { 248 | free(pool); 249 | return NULL; 250 | } 251 | 252 | pool->num_threads = num_threads; 253 | pool->task_queue = NULL; 254 | pool->task_queue_tail = NULL; 255 | pool->shutdown = false; 256 | 257 | if (zn_mutex_init(&pool->queue_mutex) != 0) { 258 | free(pool->threads); 259 | free(pool); 260 | return NULL; 261 | } 262 | 263 | if (zn_cond_init(&pool->queue_cond) != 0) { 264 | zn_mutex_destroy(&pool->queue_mutex); 265 | free(pool->threads); 266 | free(pool); 267 | return NULL; 268 | } 269 | 270 | /* Create worker threads */ 271 | for (size_t i = 0; i < num_threads; i++) { 272 | zn_thread_init(&pool->threads[i]); 273 | if (zn_thread_create(&pool->threads[i], thread_pool_worker, pool) != 0) { 274 | pool->shutdown = true; 275 | zn_cond_broadcast(&pool->queue_cond); 276 | 277 | /* Join any threads that were created successfully */ 278 | for (size_t j = 0; j < i; j++) { 279 | zn_thread_join(&pool->threads[j], NULL); 280 | } 281 | 282 | /* Clean up resources */ 283 | zn_cond_destroy(&pool->queue_cond); 284 | zn_mutex_destroy(&pool->queue_mutex); 285 | free(pool->threads); 286 | free(pool); 287 | return NULL; 288 | } 289 | } 290 | 291 | return pool; 292 | } 293 | 294 | int zn_thread_pool_add_task(zn_thread_pool_t *pool, zn_task_func_t func, void *arg) { 295 | if (!pool || !func) { 296 | return -1; 297 | } 298 | 299 | struct task_node *task = (struct task_node *)malloc(sizeof(struct task_node)); 300 | if (!task) { 301 | return -1; 302 | } 303 | 304 | task->func = func; 305 | task->arg = arg; 306 | task->next = NULL; 307 | 308 | zn_mutex_lock(&pool->queue_mutex); 309 | 310 | if (pool->shutdown) { 311 | zn_mutex_unlock(&pool->queue_mutex); 312 | free(task); 313 | return -1; 314 | } 315 | 316 | /* Add task to queue */ 317 | if (pool->task_queue == NULL) { 318 | pool->task_queue = task; 319 | pool->task_queue_tail = task; 320 | } else { 321 | pool->task_queue_tail->next = task; 322 | pool->task_queue_tail = task; 323 | } 324 | 325 | /* Signal a worker thread */ 326 | zn_cond_signal(&pool->queue_cond); 327 | 328 | zn_mutex_unlock(&pool->queue_mutex); 329 | 330 | return 0; 331 | } 332 | 333 | void zn_thread_pool_destroy(zn_thread_pool_t *pool) { 334 | if (!pool) { 335 | return; 336 | } 337 | 338 | zn_mutex_lock(&pool->queue_mutex); 339 | 340 | /* Clean up task queue */ 341 | struct task_node *task, *next_task; 342 | for (task = pool->task_queue; task != NULL; task = next_task) { 343 | next_task = task->next; 344 | free(task); 345 | } 346 | 347 | pool->task_queue = NULL; 348 | pool->task_queue_tail = NULL; 349 | pool->shutdown = true; 350 | 351 | zn_cond_broadcast(&pool->queue_cond); 352 | zn_mutex_unlock(&pool->queue_mutex); 353 | 354 | /* Wait for worker threads to exit */ 355 | for (size_t i = 0; i < pool->num_threads; i++) { 356 | zn_thread_join(&pool->threads[i], NULL); 357 | } 358 | 359 | /* Clean up resources */ 360 | zn_cond_destroy(&pool->queue_cond); 361 | zn_mutex_destroy(&pool->queue_mutex); 362 | free(pool->threads); 363 | free(pool); 364 | } 365 | 366 | size_t zn_get_num_cores(void) { 367 | long num_cores = sysconf(_SC_NPROCESSORS_ONLN); 368 | return (num_cores > 0) ? (size_t)num_cores : 1; 369 | } 370 | 371 | pthread_t zn_thread_self(void) { 372 | return pthread_self(); 373 | } 374 | 375 | /* Thread-specific data key */ 376 | static pthread_key_t thread_data_key; 377 | static bool thread_key_initialized = false; 378 | static pthread_once_t key_once = PTHREAD_ONCE_INIT; 379 | 380 | /* Initialize thread-specific data key */ 381 | static void init_thread_key(void) { 382 | pthread_key_create(&thread_data_key, NULL); 383 | thread_key_initialized = true; 384 | } 385 | 386 | int zn_thread_set_data(zn_thread_t *thread, void *data) { 387 | if (!thread) { 388 | return -1; 389 | } 390 | 391 | /* Initialize key if needed */ 392 | pthread_once(&key_once, init_thread_key); 393 | 394 | /* Store data in thread-local storage */ 395 | pthread_setspecific(thread_data_key, data); 396 | 397 | return 0; 398 | } 399 | 400 | void *zn_thread_self_data(void) { 401 | /* Initialize key if needed */ 402 | pthread_once(&key_once, init_thread_key); 403 | 404 | /* Retrieve data from thread-local storage */ 405 | return pthread_getspecific(thread_data_key); 406 | } 407 | -------------------------------------------------------------------------------- /src/threads.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file threads.h 3 | * @brief A simple wrapper around pthread to simplify thread usage 4 | */ 5 | 6 | #ifndef ZENO_THREADS_H 7 | #define ZENO_THREADS_H 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | /** 14 | * @brief Thread handle structure 15 | */ 16 | typedef struct zn_thread { 17 | pthread_t thread; 18 | bool is_running; 19 | void *(*function)(void *); 20 | void *arg; 21 | void *result; 22 | } zn_thread_t; 23 | 24 | /** 25 | * @brief Mutex structure 26 | */ 27 | typedef struct zn_mutex { 28 | pthread_mutex_t mutex; 29 | bool initialized; 30 | } zn_mutex_t; 31 | 32 | /** 33 | * @brief Condition variable structure 34 | */ 35 | typedef struct zn_cond { 36 | pthread_cond_t cond; 37 | bool initialized; 38 | } zn_cond_t; 39 | 40 | /** 41 | * @brief Thread pool structure 42 | */ 43 | typedef struct zn_thread_pool zn_thread_pool_t; 44 | 45 | /** 46 | * @brief Task function type 47 | */ 48 | typedef void (*zn_task_func_t)(void *); 49 | 50 | /** 51 | * @brief Initialize a thread 52 | * @param thread Pointer to a thread handle 53 | * @return 0 on success, error code otherwise 54 | */ 55 | int zn_thread_init(zn_thread_t *thread); 56 | 57 | /** 58 | * @brief Create and start a new thread 59 | * @param thread Pointer to a thread handle 60 | * @param function Thread function to execute 61 | * @param arg Argument to pass to the function 62 | * @return 0 on success, error code otherwise 63 | */ 64 | int zn_thread_create(zn_thread_t *thread, void *(*function)(void *), void *arg); 65 | 66 | /** 67 | * @brief Wait for a thread to complete 68 | * @param thread Pointer to a thread handle 69 | * @param result Pointer to store the thread result 70 | * @return 0 on success, error code otherwise 71 | */ 72 | int zn_thread_join(zn_thread_t *thread, void **result); 73 | 74 | /** 75 | * @brief Detach a thread 76 | * @param thread Pointer to a thread handle 77 | * @return 0 on success, error code otherwise 78 | */ 79 | int zn_thread_detach(zn_thread_t *thread); 80 | 81 | /** 82 | * @brief Clean up thread resources 83 | * @param thread Pointer to a thread handle 84 | */ 85 | void zn_thread_destroy(zn_thread_t *thread); 86 | 87 | /** 88 | * @brief Initialize a mutex 89 | * @param mutex Pointer to a mutex 90 | * @return 0 on success, error code otherwise 91 | */ 92 | int zn_mutex_init(zn_mutex_t *mutex); 93 | 94 | /** 95 | * @brief Lock a mutex 96 | * @param mutex Pointer to a mutex 97 | * @return 0 on success, error code otherwise 98 | */ 99 | int zn_mutex_lock(zn_mutex_t *mutex); 100 | 101 | /** 102 | * @brief Try to lock a mutex (non-blocking) 103 | * @param mutex Pointer to a mutex 104 | * @return 0 on success, error code otherwise 105 | */ 106 | int zn_mutex_trylock(zn_mutex_t *mutex); 107 | 108 | /** 109 | * @brief Unlock a mutex 110 | * @param mutex Pointer to a mutex 111 | * @return 0 on success, error code otherwise 112 | */ 113 | int zn_mutex_unlock(zn_mutex_t *mutex); 114 | 115 | /** 116 | * @brief Clean up mutex resources 117 | * @param mutex Pointer to a mutex 118 | * @return 0 on success, error code otherwise 119 | */ 120 | int zn_mutex_destroy(zn_mutex_t *mutex); 121 | 122 | /** 123 | * @brief Initialize a condition variable 124 | * @param cond Pointer to a condition variable 125 | * @return 0 on success, error code otherwise 126 | */ 127 | int zn_cond_init(zn_cond_t *cond); 128 | 129 | /** 130 | * @brief Wait on a condition variable 131 | * @param cond Pointer to a condition variable 132 | * @param mutex Pointer to a mutex 133 | * @return 0 on success, error code otherwise 134 | */ 135 | int zn_cond_wait(zn_cond_t *cond, zn_mutex_t *mutex); 136 | 137 | /** 138 | * @brief Signal a condition variable (wake one thread) 139 | * @param cond Pointer to a condition variable 140 | * @return 0 on success, error code otherwise 141 | */ 142 | int zn_cond_signal(zn_cond_t *cond); 143 | 144 | /** 145 | * @brief Broadcast a condition variable (wake all threads) 146 | * @param cond Pointer to a condition variable 147 | * @return 0 on success, error code otherwise 148 | */ 149 | int zn_cond_broadcast(zn_cond_t *cond); 150 | 151 | /** 152 | * @brief Clean up condition variable resources 153 | * @param cond Pointer to a condition variable 154 | * @return 0 on success, error code otherwise 155 | */ 156 | int zn_cond_destroy(zn_cond_t *cond); 157 | 158 | /** 159 | * @brief Create a thread pool 160 | * @param num_threads Number of worker threads to create 161 | * @return Pointer to the thread pool or NULL on error 162 | */ 163 | zn_thread_pool_t *zn_thread_pool_create(size_t num_threads); 164 | 165 | /** 166 | * @brief Add a task to the thread pool 167 | * @param pool Thread pool 168 | * @param func Task function 169 | * @param arg Argument to pass to the function 170 | * @return 0 on success, error code otherwise 171 | */ 172 | int zn_thread_pool_add_task(zn_thread_pool_t *pool, zn_task_func_t func, void *arg); 173 | 174 | /** 175 | * @brief Wait for all tasks to complete and destroy the thread pool 176 | * @param pool Thread pool 177 | */ 178 | void zn_thread_pool_destroy(zn_thread_pool_t *pool); 179 | 180 | /** 181 | * @brief Get the number of CPU cores available 182 | * @return Number of cores 183 | */ 184 | size_t zn_get_num_cores(void); 185 | 186 | /** 187 | * @brief Get the current thread ID 188 | * @return Thread ID 189 | */ 190 | pthread_t zn_thread_self(void); 191 | 192 | /** 193 | * @brief Set thread-specific data 194 | * @param thread Thread handle 195 | * @param data Data to store 196 | * @return 0 on success, error code otherwise 197 | */ 198 | int zn_thread_set_data(zn_thread_t *thread, void *data); 199 | 200 | /** 201 | * @brief Get thread-specific data 202 | * @return Thread-specific data or NULL if not set 203 | */ 204 | void *zn_thread_self_data(void); 205 | 206 | #endif /* ZENO_THREADS_H */ 207 | -------------------------------------------------------------------------------- /src/zeno_arc.c: -------------------------------------------------------------------------------- 1 | #include "zeno_arc.h" 2 | 3 | /** 4 | * Zeno Automatic Reference Counting - Implementation 5 | * 6 | * This file implements the runtime support functions for Zeno's automatic 7 | * reference counting system. It provides the infrastructure for tracking 8 | * object lifetimes and automatically releasing memory when objects are 9 | * no longer needed. 10 | */ 11 | 12 | // Global statistics tracking (if enabled) 13 | #ifdef ZENO_ARC_STATS 14 | typedef struct { 15 | size_t total_allocations; // Total number of allocations 16 | size_t active_allocations; // Current active allocations 17 | size_t total_memory; // Total memory allocated (in bytes) 18 | size_t active_memory; // Current active memory (in bytes) 19 | size_t total_retains; // Total number of retain operations 20 | size_t total_releases; // Total number of release operations 21 | } ZenoRC_Stats; 22 | 23 | static ZenoRC_Stats zeno_rc_stats = {0}; 24 | 25 | // Get current statistics 26 | ZenoRC_Stats ZenoRC_getStats() { 27 | return zeno_rc_stats; 28 | } 29 | 30 | // Print current statistics 31 | void ZenoRC_printStats() { 32 | printf("Zeno ARC Statistics:\n"); 33 | printf(" Total allocations: %zu\n", zeno_rc_stats.total_allocations); 34 | printf(" Active allocations: %zu\n", zeno_rc_stats.active_allocations); 35 | printf(" Total memory: %zu bytes\n", zeno_rc_stats.total_memory); 36 | printf(" Active memory: %zu bytes\n", zeno_rc_stats.active_memory); 37 | printf(" Total retains: %zu\n", zeno_rc_stats.total_retains); 38 | printf(" Total releases: %zu\n", zeno_rc_stats.total_releases); 39 | } 40 | 41 | // Update allocation statistics 42 | static void update_alloc_stats(size_t size) { 43 | zeno_rc_stats.total_allocations++; 44 | zeno_rc_stats.active_allocations++; 45 | zeno_rc_stats.total_memory += size; 46 | zeno_rc_stats.active_memory += size; 47 | } 48 | 49 | // Update deallocation statistics 50 | static void update_dealloc_stats(size_t size) { 51 | zeno_rc_stats.active_allocations--; 52 | zeno_rc_stats.active_memory -= size; 53 | } 54 | 55 | // Update retain statistics 56 | static void update_retain_stats() { 57 | zeno_rc_stats.total_retains++; 58 | } 59 | 60 | // Update release statistics 61 | static void update_release_stats() { 62 | zeno_rc_stats.total_releases++; 63 | } 64 | #endif 65 | 66 | // Registry of type deinitializers 67 | typedef struct ZenoRC_TypeDeinit { 68 | const char* type_name; 69 | void (*deinit)(void*); 70 | struct ZenoRC_TypeDeinit* next; 71 | } ZenoRC_TypeDeinit; 72 | 73 | static ZenoRC_TypeDeinit* zeno_rc_type_registry = NULL; 74 | 75 | // Register a deinitializer for a type 76 | void ZenoRC_registerDeinit(const char* type_name, void (*deinit)(void*)) { 77 | // Check if type already registered 78 | ZenoRC_TypeDeinit* entry = zeno_rc_type_registry; 79 | while (entry) { 80 | if (strcmp(entry->type_name, type_name) == 0) { 81 | // Update existing entry 82 | entry->deinit = deinit; 83 | return; 84 | } 85 | entry = entry->next; 86 | } 87 | 88 | // Create new entry 89 | ZenoRC_TypeDeinit* new_entry = (ZenoRC_TypeDeinit*)malloc(sizeof(ZenoRC_TypeDeinit)); 90 | if (!new_entry) { 91 | fprintf(stderr, "ZenoRC: Memory allocation failed for type registry\n"); 92 | return; 93 | } 94 | 95 | new_entry->type_name = strdup(type_name); 96 | new_entry->deinit = deinit; 97 | new_entry->next = zeno_rc_type_registry; 98 | zeno_rc_type_registry = new_entry; 99 | 100 | if (ZENO_ARC_DEBUG) { 101 | printf("ZenoRC: Registered deinitializer for type %s\n", type_name); 102 | } 103 | } 104 | 105 | // Get the deinitializer for a type 106 | void (*ZenoRC_getDeinit(const char* type_name))(void*) { 107 | ZenoRC_TypeDeinit* entry = zeno_rc_type_registry; 108 | while (entry) { 109 | if (strcmp(entry->type_name, type_name) == 0) { 110 | return entry->deinit; 111 | } 112 | entry = entry->next; 113 | } 114 | return NULL; 115 | } 116 | 117 | // Cleanup the type registry 118 | void ZenoRC_cleanupRegistry() { 119 | ZenoRC_TypeDeinit* entry = zeno_rc_type_registry; 120 | while (entry) { 121 | ZenoRC_TypeDeinit* next = entry->next; 122 | free((void*)entry->type_name); 123 | free(entry); 124 | entry = next; 125 | } 126 | zeno_rc_type_registry = NULL; 127 | 128 | if (ZENO_ARC_DEBUG) { 129 | printf("ZenoRC: Type registry cleaned up\n"); 130 | } 131 | } 132 | 133 | // Allocate an object with automatic reference counting 134 | void* ZenoRC_allocObject(size_t size, const char* type_name) { 135 | // Allocate memory for the header and the object 136 | size_t total_size = sizeof(ZenoRC_Header) + size; 137 | ZenoRC_Header* header = (ZenoRC_Header*)malloc(total_size); 138 | if (!header) { 139 | fprintf(stderr, "ZenoRC: Memory allocation failed for type %s\n", type_name); 140 | exit(1); 141 | } 142 | 143 | // Initialize header 144 | header->ref_count = 1; 145 | header->deinit = ZenoRC_getDeinit(type_name); 146 | header->size = size; 147 | header->type_name = strdup(type_name); 148 | 149 | // Get pointer to object memory (after header) 150 | void* object_ptr = (void*)(header + 1); 151 | 152 | // Zero-initialize the object memory 153 | memset(object_ptr, 0, size); 154 | 155 | #ifdef ZENO_ARC_STATS 156 | update_alloc_stats(total_size); 157 | #endif 158 | 159 | if (ZENO_ARC_DEBUG) { 160 | printf("ZenoRC: Allocated %s at %p (count: %d)\n", 161 | type_name, object_ptr, header->ref_count); 162 | } 163 | 164 | return object_ptr; 165 | } 166 | 167 | // Retain an object (increment its reference count) 168 | void ZenoRC_retainObject(void* ptr) { 169 | if (!ptr) return; 170 | 171 | ZenoRC_Header* header = ((ZenoRC_Header*)ptr) - 1; 172 | header->ref_count++; 173 | 174 | #ifdef ZENO_ARC_STATS 175 | update_retain_stats(); 176 | #endif 177 | 178 | if (ZENO_ARC_DEBUG) { 179 | printf("ZenoRC: Retained %s at %p (count: %d)\n", 180 | header->type_name, ptr, header->ref_count); 181 | } 182 | } 183 | 184 | // Release an object (decrement reference count and free if zero) 185 | void ZenoRC_releaseObject(void* ptr) { 186 | if (!ptr) return; 187 | 188 | ZenoRC_Header* header = ((ZenoRC_Header*)ptr) - 1; 189 | header->ref_count--; 190 | 191 | #ifdef ZENO_ARC_STATS 192 | update_release_stats(); 193 | #endif 194 | 195 | if (ZENO_ARC_DEBUG) { 196 | printf("ZenoRC: Released %s at %p (count: %d)\n", 197 | header->type_name, ptr, header->ref_count); 198 | } 199 | 200 | if (header->ref_count == 0) { 201 | if (ZENO_ARC_DEBUG) { 202 | printf("ZenoRC: Deallocating %s at %p\n", header->type_name, ptr); 203 | } 204 | 205 | // Call custom destructor if provided 206 | if (header->deinit) { 207 | header->deinit(ptr); 208 | } 209 | 210 | size_t total_size = sizeof(ZenoRC_Header) + header->size; 211 | 212 | #ifdef ZENO_ARC_STATS 213 | update_dealloc_stats(total_size); 214 | #endif 215 | 216 | // Free the type name 217 | free((void*)header->type_name); 218 | 219 | // Free the memory 220 | free(header); 221 | } else if (header->ref_count < 0) { 222 | fprintf(stderr, "ZenoRC: Error - negative reference count for %s at %p (%d)\n", 223 | header->type_name, ptr, header->ref_count); 224 | } 225 | } 226 | 227 | // Set a custom deinitializer for a specific object 228 | void ZenoRC_setObjectDeinit(void* ptr, void (*deinit)(void*)) { 229 | if (!ptr) return; 230 | 231 | ZenoRC_Header* header = ((ZenoRC_Header*)ptr) - 1; 232 | header->deinit = deinit; 233 | 234 | if (ZENO_ARC_DEBUG) { 235 | printf("ZenoRC: Set deinitializer for %s at %p\n", header->type_name, ptr); 236 | } 237 | } 238 | 239 | // Create a new reference counted string 240 | char* ZenoRC_createString(const char* str) { 241 | if (!str) return NULL; 242 | 243 | size_t len = strlen(str) + 1; 244 | char* new_str = (char*)ZenoRC_allocObject(len, "String"); 245 | 246 | // Copy the string data 247 | strcpy(new_str, str); 248 | 249 | return new_str; 250 | } 251 | 252 | // Create a reference counted array 253 | void* ZenoRC_createArray(size_t elem_size, size_t count, const char* elem_type) { 254 | size_t total_size = elem_size * count; 255 | 256 | // Create a type name for the array 257 | char type_name[256]; 258 | snprintf(type_name, sizeof(type_name), "Array<%s>", elem_type); 259 | 260 | return ZenoRC_allocObject(total_size, type_name); 261 | } 262 | 263 | // Initialize the ARC system 264 | void ZenoRC_initialize() { 265 | if (ZENO_ARC_DEBUG) { 266 | printf("ZenoRC: Initializing automatic reference counting system\n"); 267 | } 268 | 269 | // Register built-in deinitializers 270 | 271 | // String doesn't need a custom deinitializer as it's just a char array 272 | ZenoRC_registerDeinit("String", NULL); 273 | 274 | #ifdef ZENO_ARC_STATS 275 | // Reset statistics 276 | memset(&zeno_rc_stats, 0, sizeof(ZenoRC_Stats)); 277 | #endif 278 | } 279 | 280 | // Shutdown the ARC system 281 | void ZenoRC_shutdown() { 282 | if (ZENO_ARC_DEBUG) { 283 | printf("ZenoRC: Shutting down automatic reference counting system\n"); 284 | } 285 | 286 | #ifdef ZENO_ARC_STATS 287 | // Print final statistics 288 | ZenoRC_printStats(); 289 | #endif 290 | 291 | // Clean up type registry 292 | ZenoRC_cleanupRegistry(); 293 | } 294 | 295 | // Automatic initialization and cleanup 296 | __attribute__((constructor)) static void __zeno_arc_initialize() { 297 | ZenoRC_initialize(); 298 | } 299 | 300 | __attribute__((destructor)) static void __zeno_arc_shutdown() { 301 | ZenoRC_shutdown(); 302 | } -------------------------------------------------------------------------------- /src/zeno_arc.h: -------------------------------------------------------------------------------- 1 | #ifndef ZENO_ARC_H 2 | #define ZENO_ARC_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /** 10 | * Zeno Automatic Reference Counting 11 | * 12 | * This header provides the runtime support for automatic reference counting in Zeno. 13 | * It works by wrapping allocated objects with a header that tracks reference count 14 | * and custom deinitializers. 15 | */ 16 | 17 | // Header structure for reference counted objects 18 | typedef struct { 19 | int ref_count; // Number of references to this object 20 | void (*deinit)(void*); // Custom deinitializer function 21 | size_t size; // Size of the managed object 22 | const char* type_name; // Type name for debugging 23 | } ZenoRC_Header; 24 | 25 | // Debug setting - set to true to enable debug messages 26 | #ifndef ZENO_ARC_DEBUG 27 | #define ZENO_ARC_DEBUG false 28 | #endif 29 | 30 | // Allocate reference counted memory 31 | static inline void* ZenoRC_alloc(size_t size, const char* type_name) { 32 | ZenoRC_Header* header = (ZenoRC_Header*)malloc(sizeof(ZenoRC_Header) + size); 33 | if (!header) { 34 | fprintf(stderr, "ZenoRC: Memory allocation failed for type %s\n", type_name); 35 | exit(1); 36 | } 37 | 38 | header->ref_count = 1; 39 | header->deinit = NULL; 40 | header->size = size; 41 | header->type_name = type_name; 42 | 43 | void* ptr = (void*)(header + 1); 44 | 45 | if (ZENO_ARC_DEBUG) { 46 | printf("ZenoRC: Allocated %s at %p (count: %d)\n", 47 | type_name, ptr, header->ref_count); 48 | } 49 | 50 | // Return pointer to the memory after the header 51 | return ptr; 52 | } 53 | 54 | // Retain (increment reference count) 55 | static inline void ZenoRC_retain(void* ptr) { 56 | if (!ptr) return; 57 | 58 | ZenoRC_Header* header = ((ZenoRC_Header*)ptr) - 1; 59 | header->ref_count++; 60 | 61 | if (ZENO_ARC_DEBUG) { 62 | printf("ZenoRC: Retained %s at %p (count: %d)\n", 63 | header->type_name, ptr, header->ref_count); 64 | } 65 | } 66 | 67 | // Release (decrement reference count and free if zero) 68 | static inline void ZenoRC_release(void* ptr) { 69 | if (!ptr) return; 70 | 71 | ZenoRC_Header* header = ((ZenoRC_Header*)ptr) - 1; 72 | header->ref_count--; 73 | 74 | if (ZENO_ARC_DEBUG) { 75 | printf("ZenoRC: Released %s at %p (count: %d)\n", 76 | header->type_name, ptr, header->ref_count); 77 | } 78 | 79 | if (header->ref_count == 0) { 80 | if (ZENO_ARC_DEBUG) { 81 | printf("ZenoRC: Deallocating %s at %p\n", header->type_name, ptr); 82 | } 83 | 84 | // Call custom destructor if provided 85 | if (header->deinit) { 86 | header->deinit(ptr); 87 | } 88 | 89 | // Free the memory 90 | free(header); 91 | } else if (header->ref_count < 0) { 92 | fprintf(stderr, "ZenoRC: Error - negative reference count for %s at %p (%d)\n", 93 | header->type_name, ptr, header->ref_count); 94 | } 95 | } 96 | 97 | // Set custom destructor for an object 98 | static inline void ZenoRC_setDeinit(void* ptr, void (*deinit)(void*)) { 99 | if (!ptr) return; 100 | 101 | ZenoRC_Header* header = ((ZenoRC_Header*)ptr) - 1; 102 | header->deinit = deinit; 103 | 104 | if (ZENO_ARC_DEBUG) { 105 | printf("ZenoRC: Set deinitializer for %s at %p\n", header->type_name, ptr); 106 | } 107 | } 108 | 109 | // Get reference count (for debugging) 110 | static inline int ZenoRC_getCount(void* ptr) { 111 | if (!ptr) return 0; 112 | 113 | ZenoRC_Header* header = ((ZenoRC_Header*)ptr) - 1; 114 | return header->ref_count; 115 | } 116 | 117 | // Create a copy of an object (with new reference count) 118 | static inline void* ZenoRC_copy(void* ptr) { 119 | if (!ptr) return NULL; 120 | 121 | ZenoRC_Header* header = ((ZenoRC_Header*)ptr) - 1; 122 | void* new_ptr = ZenoRC_alloc(header->size, header->type_name); 123 | 124 | // Copy the data 125 | memcpy(new_ptr, ptr, header->size); 126 | 127 | // Copy the destructor 128 | ZenoRC_setDeinit(new_ptr, header->deinit); 129 | 130 | if (ZENO_ARC_DEBUG) { 131 | printf("ZenoRC: Created copy of %s from %p to %p\n", 132 | header->type_name, ptr, new_ptr); 133 | } 134 | 135 | return new_ptr; 136 | } 137 | 138 | // Create a string (with reference counting) 139 | static inline char* ZenoRC_string(const char* str) { 140 | if (!str) return NULL; 141 | 142 | size_t len = strlen(str) + 1; 143 | char* new_str = (char*)ZenoRC_alloc(len, "String"); 144 | 145 | // Copy the string data 146 | strcpy(new_str, str); 147 | 148 | return new_str; 149 | } 150 | 151 | // Get the type name of an object 152 | static inline const char* ZenoRC_typeName(void* ptr) { 153 | if (!ptr) return "NULL"; 154 | 155 | ZenoRC_Header* header = ((ZenoRC_Header*)ptr) - 1; 156 | return header->type_name; 157 | } 158 | 159 | // Dump reference counting info for debugging 160 | static inline void ZenoRC_dump(void* ptr) { 161 | if (!ptr) { 162 | printf("ZenoRC: NULL pointer\n"); 163 | return; 164 | } 165 | 166 | ZenoRC_Header* header = ((ZenoRC_Header*)ptr) - 1; 167 | printf("ZenoRC Object:\n"); 168 | printf(" Address: %p\n", ptr); 169 | printf(" Type: %s\n", header->type_name); 170 | printf(" Size: %zu bytes\n", header->size); 171 | printf(" Ref Count: %d\n", header->ref_count); 172 | printf(" Deinitializer: %s\n", header->deinit ? "Yes" : "No"); 173 | } 174 | 175 | // Convenience macros for common ARC operations 176 | #define ZENO_ALLOC(type) ((type*)ZenoRC_alloc(sizeof(type), #type)) 177 | #define ZENO_RETAIN(ptr) ZenoRC_retain(ptr) 178 | #define ZENO_RELEASE(ptr) ZenoRC_release(ptr) 179 | #define ZENO_STRING(str) ZenoRC_string(str) 180 | 181 | #endif // ZENO_ARC_H -------------------------------------------------------------------------------- /src/zeno_cli.h: -------------------------------------------------------------------------------- 1 | #ifndef ZENO_CLI_H 2 | #define ZENO_CLI_H 3 | 4 | #include 5 | 6 | // Manifest structure 7 | typedef struct { 8 | char* name; 9 | char* version; 10 | 11 | struct { 12 | char* dir; 13 | char* binary; 14 | } output; 15 | 16 | struct { 17 | char* main; 18 | char** include; 19 | int include_count; 20 | } source; 21 | 22 | struct { 23 | char* cc; 24 | char* flags; 25 | } compiler; 26 | } ZenoManifest; 27 | 28 | // Command options 29 | typedef struct { 30 | bool verbose; 31 | char* manifest_path; 32 | char* input_file; 33 | char* output_file; 34 | bool run_mode; 35 | bool compile_mode; 36 | bool use_llvm; // New flag for LLVM backend 37 | } ZenoOptions; 38 | 39 | // Function declarations 40 | ZenoManifest* load_manifest(const char* path, bool allow_missing); 41 | void free_manifest(ZenoManifest* manifest); 42 | int run_zeno_file(ZenoOptions* options, ZenoManifest* manifest); 43 | int compile_zeno_file(ZenoOptions* options, ZenoManifest* manifest); 44 | int init_zeno_project(const char* dir_path, bool verbose); 45 | 46 | // Transpile function (defined in main.c) 47 | int transpile_file(const char* input_path, const char* output_path, bool verbose, bool use_llvm); 48 | 49 | #endif // ZENO_CLI_H --------------------------------------------------------------------------------