├── .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
--------------------------------------------------------------------------------