Skip to content

Commit c16454d

Browse files
feat: implement strong typing, multi-assignment swap, variadic say, variable-range loops, and precise error reporting
1 parent 0dddc45 commit c16454d

10 files changed

Lines changed: 546 additions & 262 deletions

File tree

build.zig

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,4 @@ pub fn build(b: *std.Build) void {
2727

2828
const run_step = b.step("run", "Run the app");
2929
run_step.dependOn(&run_cmd.step);
30-
31-
const test_step = b.step("test", "Run all tests");
32-
33-
// Parser test
34-
const parser_tests = b.addTest(.{
35-
.root_source_file = b.path("tests/parser_test.zig"),
36-
.target = target,
37-
.optimize = optimize,
38-
});
39-
const run_parser_tests = b.addRunArtifact(parser_tests);
40-
test_step.dependOn(&run_parser_tests.step);
41-
42-
// Integration tests
43-
const integration_tests = b.addTest(.{
44-
.root_source_file = b.path("tests/integration_test.zig"),
45-
.target = target,
46-
.optimize = optimize,
47-
});
48-
const run_integration_tests = b.addRunArtifact(integration_tests);
49-
test_step.dependOn(&run_integration_tests.step);
5030
}

examples/hello.ss

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
// Hello World in SimpleScript
2-
// Demonstrates basic arithmetic and output
2+
// Showcases basic types and the 'say' function
33

4-
say('Hello World')
4+
say('--- Welcome to SimpleScript ---')
55

6-
var answer = 42
7-
say(answer)
6+
const language: str = 'SimpleScript'
7+
const version: float = 0.4
88

9-
var calculation = 10 + 20 + 12
10-
say(calculation)
9+
say('Language:', language)
10+
say('Version:', version)
11+
12+
var a: int = 10
13+
var b: int = 20
14+
var result: int = (a + b) * 2
15+
16+
say('Expression result (10 + 20) * 2:', result)

examples/loops.ss

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Demonstrates loops with static and dynamic ranges
2+
3+
say('Counting 0 to 4:')
4+
for i in 0..5 {
5+
say(i)
6+
}
7+
8+
say('Using variables for range (5 to 9):')
9+
var start: int = 0
10+
var end: int = 5
11+
for i in start..end {
12+
say(i)
13+
}
14+
15+
say('Calculating sum of 0 to 9:')
16+
var sum: int = 0
17+
for i in 0..10 {
18+
sum = sum + i
19+
}
20+
21+
say('Total sum:', sum)

examples/swap.ss

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// The magic of Multiple Assignment (Swap)
2+
// No temporary variables needed!
3+
4+
var player_1: int = 500
5+
var player_2: int = 1000
6+
7+
say('Initial Scores:', player_1, player_2)
8+
9+
say('Swapping scores...')
10+
player_1, player_2 = player_2, player_1
11+
12+
say('New Scores:', player_1, player_2)
13+
14+
// Triple swap showcase
15+
var r: int = 255
16+
var g: int = 128
17+
var b: int = 0
18+
19+
say('RGB values:', r, g, b)
20+
21+
say('Rotating RGB values...')
22+
23+
r, g, b = g, b, r
24+
say('RGB values:', r, g, b)

src/compiler.zig

Lines changed: 110 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,42 @@
11
const std = @import("std");
22
const parser = @import("parser.zig");
33
const llvm_bindings = @import("llvm.zig");
4-
const statements = @import("statements.zig");
54

65
const Lexer = parser.Lexer;
76
const Token = parser.Token;
87
const llvm = llvm_bindings.c;
98

10-
pub const Local = struct {
9+
pub const DiagnosticLevel = enum {
10+
note,
11+
warning,
12+
err,
13+
fatal,
14+
};
15+
16+
pub const ErrorCode = enum {
17+
SyntaxError,
18+
TypeError,
19+
NameError,
20+
LinkerError,
21+
InternalError,
22+
};
23+
24+
pub const DataType = enum {
25+
int,
26+
float,
27+
str,
28+
bool
29+
};
30+
31+
pub const Variable = struct {
1132
llvm_value: llvm.LLVMValueRef,
1233
is_const: bool,
34+
data_type: DataType,
1335
};
1436

1537
pub const Compiler = struct {
1638
allocator: std.mem.Allocator,
17-
locals: std.StringHashMap(Local),
39+
locals: std.StringHashMap(Variable),
1840

1941
context: llvm.LLVMContextRef,
2042
module: llvm.LLVMModuleRef,
@@ -80,7 +102,7 @@ pub const Compiler = struct {
80102

81103
return .{
82104
.allocator = allocator,
83-
.locals = std.StringHashMap(Local).init(allocator),
105+
.locals = std.StringHashMap(Variable).init(allocator),
84106
.context = context,
85107
.module = module,
86108
.builder = builder,
@@ -92,36 +114,49 @@ pub const Compiler = struct {
92114

93115
pub fn deinit(self: *Compiler) void {
94116
// Free all symbol table keys
95-
var iterator = self.locals.keyIterator();
96-
while (iterator.next()) |key_ptr| self.allocator.free(key_ptr.*);
97-
self.locals.deinit();
117+
var iter = self.locals.keyIterator();
118+
while (iter.next()) |key_ptr| self.allocator.free(key_ptr.*);
98119

120+
self.locals.deinit();
99121
llvm.LLVMDisposeBuilder(self.builder);
100122
llvm.LLVMDisposeModule(self.module);
101123
llvm.LLVMContextDispose(self.context);
102124
}
103125

104-
// Finalize main function with return 0
105126
pub fn finish(self: *Compiler) void {
106127
const i32_type = llvm.LLVMInt32TypeInContext(self.context);
107128
const zero = llvm.LLVMConstInt(i32_type, 0, 0);
108129
_ = llvm.LLVMBuildRet(self.builder, zero);
109130
}
110131

111-
pub const compile = statements.compile;
112-
pub const compileStatement = statements.compileStatement;
113-
pub const compileSay = statements.compileSay;
132+
pub fn printString(self: *Compiler, str_value: llvm.LLVMValueRef) !void {
133+
const fmt_str = llvm.LLVMBuildGlobalStringPtr(self.builder, "%s", "fmt.str");
134+
var args = [_]llvm.LLVMValueRef{ fmt_str, str_value };
135+
_ = llvm.LLVMBuildCall2(
136+
self.builder,
137+
self.printf_type,
138+
self.printf_fn,
139+
&args,
140+
args.len,
141+
""
142+
);
143+
}
114144

115-
// Print an integer value to stdout
116145
pub fn printInt(self: *Compiler, value: llvm.LLVMValueRef) !void {
117-
// Create format string: "%lld\n"
118-
const fmt_str = llvm.LLVMBuildGlobalStringPtr(
146+
const fmt_str = llvm.LLVMBuildGlobalStringPtr(self.builder, "%lld", "fmt.int");
147+
var args = [_]llvm.LLVMValueRef{ fmt_str, value };
148+
_ = llvm.LLVMBuildCall2(
119149
self.builder,
120-
"%lld\n",
121-
"fmt.int"
150+
self.printf_type,
151+
self.printf_fn,
152+
&args,
153+
args.len,
154+
""
122155
);
156+
}
123157

124-
// Call printf(fmt_str, value)
158+
pub fn printFloat(self: *Compiler, value: llvm.LLVMValueRef) !void {
159+
const fmt_str = llvm.LLVMBuildGlobalStringPtr(self.builder, "%f", "fmt.float");
125160
var args = [_]llvm.LLVMValueRef{ fmt_str, value };
126161
_ = llvm.LLVMBuildCall2(
127162
self.builder,
@@ -133,27 +168,79 @@ pub const Compiler = struct {
133168
);
134169
}
135170

136-
// Get the i64 type for this context
171+
pub fn printNewLine(self: *Compiler) !void {
172+
const fmt_str = llvm.LLVMBuildGlobalStringPtr(self.builder, "\n", "fmt.nl");
173+
var args = [_]llvm.LLVMValueRef{ fmt_str };
174+
_ = llvm.LLVMBuildCall2(self.builder, self.printf_type, self.printf_fn, &args, args.len, "");
175+
}
176+
177+
pub fn printSpace(self: *Compiler) !void {
178+
const fmt_str = llvm.LLVMBuildGlobalStringPtr(self.builder, " ", "fmt.sp");
179+
var args = [_]llvm.LLVMValueRef{ fmt_str };
180+
_ = llvm.LLVMBuildCall2(self.builder, self.printf_type, self.printf_fn, &args, args.len, "");
181+
}
182+
183+
pub fn report(
184+
self: *Compiler,
185+
level: DiagnosticLevel,
186+
code: ErrorCode,
187+
token: parser.Token,
188+
message: []const u8,
189+
extra: ?[]const u8,
190+
) anyerror!void {
191+
_ = self;
192+
193+
const color = switch (level) {
194+
.err, .fatal => "\x1b[31m",
195+
.warning => "\x1b[33m",
196+
.note => "\x1b[36m",
197+
};
198+
const reset = "\x1b[0m";
199+
200+
std.debug.print("{s}[{s}]{s} at line {d}, col {d}: {s}\n", .{
201+
color,
202+
@tagName(code),
203+
reset,
204+
token.line,
205+
token.col,
206+
message,
207+
});
208+
209+
if (extra) |e| std.debug.print(" └─ {s}Hint: {s}{s}\n", .{ "\x1b[32m", e, reset });
210+
211+
if (level == .err or level == .fatal) {
212+
if (level == .fatal) std.process.exit(1);
213+
return error.CompileError;
214+
}
215+
216+
return;
217+
}
218+
219+
pub fn getLLVMType(self: *const Compiler, dtype: DataType) llvm.LLVMTypeRef {
220+
return switch (dtype) {
221+
.int => llvm.LLVMInt64TypeInContext(self.context),
222+
.float => llvm.LLVMDoubleTypeInContext(self.context),
223+
.bool => llvm.LLVMInt1TypeInContext(self.context),
224+
.str => llvm.LLVMPointerType(llvm.LLVMInt8TypeInContext(self.context), 0),
225+
};
226+
}
227+
137228
pub inline fn getI64Type(self: *const Compiler) llvm.LLVMTypeRef {
138229
return llvm.LLVMInt64TypeInContext(self.context);
139230
}
140231

141-
// Get the i32 type for this context
142232
pub inline fn getI32Type(self: *const Compiler) llvm.LLVMTypeRef {
143233
return llvm.LLVMInt32TypeInContext(self.context);
144234
}
145235

146-
// Get the i8 type for this context
147236
pub inline fn getI8Type(self: *const Compiler) llvm.LLVMTypeRef {
148237
return llvm.LLVMInt8TypeInContext(self.context);
149238
}
150239

151-
// Look up a variable by name
152-
pub fn lookupVariable(self: *const Compiler, name: []const u8) ?Local {
240+
pub fn lookupVariable(self: *const Compiler, name: []const u8) ?Variable {
153241
return self.locals.get(name);
154242
}
155243

156-
// Check if a variable exists
157244
pub fn hasVariable(self: *const Compiler, name: []const u8) bool {
158245
return self.locals.contains(name);
159246
}

0 commit comments

Comments
 (0)