Embedded programming language for Zig
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Many improvements

IamPyu 4c9364f1 b81bf996

+160 -63
+1 -1
build.zig
··· 30 30 const test_step = b.step("test", "Run tests"); 31 31 test_step.dependOn(&run_mod_tests.step); 32 32 33 - const add_exe = b.option(bool, "exe", "Whether to build the Zexa interpreter") orelse false; 33 + const add_exe = b.option(bool, "exe", "Whether to build the Zexa interpreter") orelse true; 34 34 35 35 if (add_exe) { 36 36 const exe_mod = b.createModule(.{
+2 -2
build.zig.zon
··· 3 3 .version = "0.1.0", 4 4 .dependencies = .{ 5 5 .mitochondria = .{ 6 - .url = "git+https://codeberg.org/IamPyu/mitochondria.git#7b79ffc2df9493e27d9e5c29a53b29ba2c6b3d8a", 7 - .hash = "mitochondria-0.1.0-RHWHd1h4AAAwcZAj91bIc38DOwq8I8hDgo9sybJis8-v", 6 + .url = "git+https://codeberg.org/IamPyu/mitochondria.git#fe26983dee8a66efe936481e1293716a11f87b9a", 7 + .hash = "mitochondria-0.1.0-RHWHdzWBAACfDLyx5ACj8lvDsHLZnDriXGY1MpQvpUBO", 8 8 }, 9 9 }, 10 10 .minimum_zig_version = "0.15.2",
+19 -5
src/lang.zig
··· 12 12 External, 13 13 Other, 14 14 Exception, 15 + InvalidType, 15 16 } || std.mem.Allocator.Error; 16 17 17 18 pub const Symbol = []const u8; 18 19 pub const Number = f64; 19 20 pub const NativeFunc = *const fn (scope: *Scope, args: []const *Value) RuntimeError!*Value; 20 21 pub const NativeMacro = *const fn (scope: *Scope, args: []const *Value) RuntimeError!AST; 22 + 23 + pub const Library = *const fn (scope: *Scope) RuntimeError!void; 21 24 22 25 pub const AST = []*Value; 23 26 ··· 129 132 *Value => Self.initFrom(scope, .{ 130 133 .atom = .{ .quote = value }, 131 134 }), 132 - else => error.InvalidType, 135 + else => RuntimeError.InvalidType, 133 136 }; 134 137 } 135 138 ··· 482 485 } 483 486 }, 484 487 .native_macro => {}, 485 - else => return error.InvalidFunction, 488 + else => return RuntimeError.InvalidFunction, 486 489 } 487 490 488 491 const value = blk: { ··· 640 643 return out; 641 644 } 642 645 646 + /// Copies `msg` to use as the current exception message 643 647 pub fn setException(self: *Self, msg: []const u8) !void { 644 648 const s = self.getRoot(); 645 649 s.exception = try s.allocator.alloc(u8, msg.len); ··· 666 670 try self.variables.put(key, value); 667 671 } 668 672 673 + pub fn insertRegistry(self: *Self, registry: []const struct { []const u8, *Value }) !void { 674 + for (registry) |registee| { 675 + try self.insert(registee[0], registee[1]); 676 + } 677 + } 678 + 669 679 pub fn get(self: *Self, key: []const u8) ?*Value { 670 680 if (self.variables.get(key)) |v| { 671 681 return v; ··· 701 711 .function, .native_func, .native_macro, .userdata => return expr, 702 712 .cons => { 703 713 const uneval_func = expr.getCar().?; 704 - _ = self.getCallStack().append(uneval_func) catch return error.External; 714 + _ = self.getCallStack().append(uneval_func) catch return RuntimeError.External; 705 715 706 716 var scope = try self.branch(); 707 717 defer scope.deinit(); ··· 715 725 try args.append(scope.allocator, arg); 716 726 } 717 727 718 - const result = func.call(&scope, args.items) catch |e| return self.dump_stack_and_error(e); 728 + const result = func.call(&scope, args.items) catch |e| return self.dumpStackAndError(e); 719 729 720 730 const c = try result.clone(self); 721 731 self.getCallStack().pop(); ··· 731 741 return value orelse try Value.initNil(self); 732 742 } 733 743 734 - pub fn dump_stack_and_error(self: *Self, e: RuntimeError) RuntimeError { 744 + pub fn dumpStackAndError(self: *Self, e: RuntimeError) RuntimeError { 735 745 std.debug.print("error occured in scope {*}: {}\n", .{ self, e }); 736 746 std.debug.print("unwinding callstack {*}:\n", .{&self.getCallStack()}); 737 747 self.getCallStack().dump(self); 738 748 return e; 749 + } 750 + 751 + pub fn loadLibrary(self: *Self, lib: Library) RuntimeError!void { 752 + try lib(self); 739 753 } 740 754 };
+4 -4
src/lexer.zig
··· 175 175 176 176 while (iter.peek()) |nc| : (line.column += 1) { 177 177 if (nc == '\n') { 178 - return error.StringBreak; 178 + return LexError.StringBreak; 179 179 } 180 180 181 181 if (nc == CHARS.STRDELIM and !in_escape) { ··· 227 227 } else if (expr_ignore(nc)) { 228 228 break; 229 229 } else { 230 - return error.InvalidSymbol; 230 + return LexError.InvalidSymbol; 231 231 } 232 232 } 233 233 ··· 249 249 try sb.appendChar(iter.next().?); 250 250 } else if (nc == '.') { 251 251 if (has_decimal) { 252 - return error.ExtraDecimalPoint; 252 + return LexError.ExtraDecimalPoint; 253 253 } 254 254 has_decimal = true; 255 255 try sb.appendChar(iter.next().?); ··· 266 266 }); 267 267 } else { 268 268 if (!std.ascii.isWhitespace(c)) { 269 - return error.UnexpectedToken; 269 + return LexError.UnexpectedToken; 270 270 } 271 271 }, 272 272 }
+1 -1
src/parser.zig
··· 123 123 124 124 var env = try Environment.init(std.testing.allocator); 125 125 defer env.deinit(); 126 - try zexa.stdlib.loadStd(&env.primary_scope, .{}); 126 + try zexa.stdlib.loadStd(&env.primary_scope); 127 127 128 128 var parser = try Parser.init(std.testing.allocator, &tokenizer, &env); 129 129 try parser.parse();
+3 -2
src/root.zig
··· 1 1 pub const lang = @import("./lang.zig"); 2 2 pub const types = lang; 3 3 pub const env = @import("./env.zig"); 4 - pub const stdlib = @import("./std.zig"); 4 + pub const stdlib = @import("./stdlib.zig"); 5 5 pub const lexer = @import("./lexer.zig"); 6 6 pub const parser = @import("./parser.zig"); 7 + pub const util = @import("./util.zig"); 7 8 8 9 const std = @import("std"); 9 10 const Allocator = std.mem.Allocator; ··· 41 42 /// Initialize a Zexa `State` 42 43 pub fn init(allocator: Allocator) !Self { 43 44 var e = try env.Environment.init(allocator); 44 - try stdlib.loadStd(&e.primary_scope, .{}); 45 + try stdlib.loadStd(&e.primary_scope); 45 46 46 47 return .{ 47 48 .allocator = allocator,
-13
src/std.zig
··· 1 - const std = @import("std"); 2 - const zexa = @import("./root.zig"); 3 - const Scope = zexa.types.Scope; 4 - const Value = zexa.types.Value; 5 - 6 - const corelib = @import("./std/core.zig"); 7 - 8 - pub const StdConfiguration = struct {}; 9 - 10 - pub fn loadStd(scope: *Scope, config: StdConfiguration) !void { 11 - _ = config; 12 - try corelib.load(scope); 13 - }
+86 -33
src/std/core.zig src/stdlib/core.zig
··· 11 11 const AST = zexa.types.AST; 12 12 const RuntimeError = zexa.types.RuntimeError; 13 13 14 - pub fn load(scope: *Scope) !void { 15 - try scope.insert("#t", try Value.initFromAny(scope, true)); 16 - try scope.insert("#f", try Value.initFromAny(scope, false)); 17 - try scope.insert("nil", try Value.initNil(scope)); 14 + const util = @import("../util.zig"); 18 15 19 - try scope.insert("progn", try Value.initNativeMacro(scope, progn)); 20 - try scope.insert("if", try Value.initNativeMacro(scope, @"if")); 21 - try scope.insert("while", try Value.initNativeMacro(scope, @"while")); 22 - try scope.insert("let", try Value.initNativeMacro(scope, let)); 23 - try scope.insert("lambda", try Value.initNativeMacro(scope, lambda)); 16 + const EXCEPTION_SIZE = 30; 24 17 25 - try scope.insert("eval", try Value.initNativeFunc(scope, eval)); 26 - try scope.insert("define", try Value.initNativeFunc(scope, define)); 27 - try scope.insert("set", try Value.initNativeFunc(scope, set)); 28 - try scope.insert("print", try Value.initNativeFunc(scope, print)); 29 - try scope.insert("format", try Value.initNativeFunc(scope, format)); 30 - try scope.insert("clone", try Value.initNativeFunc(scope, clone)); 31 - try scope.insert("list", try Value.initNativeFunc(scope, list)); 32 - try scope.insert("append", try Value.initNativeFunc(scope, append)); 33 - try scope.insert("map", try Value.initNativeFunc(scope, map)); 18 + pub fn load(scope: *Scope) RuntimeError!void { 19 + try scope.insertRegistry(&.{ 20 + .{ "#t", try Value.initFromAny(scope, true) }, 21 + .{ "#f", try Value.initFromAny(scope, false) }, 22 + .{ "nil", try Value.initNil(scope) }, 34 23 35 - try scope.insert("add", try Value.initNativeFunc(scope, add)); 36 - try scope.insert("sub", try Value.initNativeFunc(scope, sub)); 37 - try scope.insert("mul", try Value.initNativeFunc(scope, mul)); 38 - try scope.insert("div", try Value.initNativeFunc(scope, div)); 39 - try scope.insert("eql", try Value.initNativeFunc(scope, eql)); 40 - try scope.insert("not", try Value.initNativeFunc(scope, not)); 41 - try scope.insert("and", try Value.initNativeFunc(scope, @"and")); 42 - try scope.insert("or", try Value.initNativeFunc(scope, @"or")); 24 + .{ "progn", try Value.initNativeMacro(scope, progn) }, 25 + .{ "if", try Value.initNativeMacro(scope, @"if") }, 26 + .{ "while", try Value.initNativeMacro(scope, @"while") }, 27 + .{ "let", try Value.initNativeMacro(scope, let) }, 28 + .{ "lambda", try Value.initNativeMacro(scope, lambda) }, 29 + 30 + .{ "eval", try Value.initNativeFunc(scope, eval) }, 31 + .{ "define", try Value.initNativeFunc(scope, define) }, 32 + .{ "set", try Value.initNativeFunc(scope, set) }, 33 + .{ "print", try Value.initNativeFunc(scope, print) }, 34 + .{ "format", try Value.initNativeFunc(scope, format) }, 35 + .{ "clone", try Value.initNativeFunc(scope, clone) }, 36 + .{ "list", try Value.initNativeFunc(scope, list) }, 37 + .{ "append", try Value.initNativeFunc(scope, append) }, 38 + .{ "map", try Value.initNativeFunc(scope, map) }, 39 + 40 + .{ "add", try Value.initNativeFunc(scope, add) }, 41 + .{ "sub", try Value.initNativeFunc(scope, sub) }, 42 + .{ "mul", try Value.initNativeFunc(scope, mul) }, 43 + .{ "div", try Value.initNativeFunc(scope, div) }, 44 + .{ "eql", try Value.initNativeFunc(scope, eql) }, 45 + .{ "not", try Value.initNativeFunc(scope, not) }, 46 + .{ "and", try Value.initNativeFunc(scope, @"and") }, 47 + .{ "or", try Value.initNativeFunc(scope, @"or") }, 48 + }); 43 49 } 44 50 45 51 // -- MACROS -- ··· 157 163 // -- FUNCTIONS -- 158 164 159 165 pub fn eval(scope: *Scope, args: []const *Value) RuntimeError!*Value { 166 + var sb = try StringBuilder.init(scope.allocator, EXCEPTION_SIZE); 167 + defer sb.deinit(); 160 168 if (args.len != 1) { 169 + try util.unexpectedArguments(&sb, .{ .expected = 1, .got = args.len }); 170 + try scope.setException(sb.written()); 161 171 return RuntimeError.InvalidArguments; 162 172 } 163 173 ··· 165 175 } 166 176 167 177 pub fn define(scope: *Scope, args: []const *Value) RuntimeError!*Value { 178 + var sb = try StringBuilder.init(scope.allocator, EXCEPTION_SIZE); 179 + defer sb.deinit(); 168 180 if (args.len != 2) { 181 + try util.unexpectedArguments(&sb, .{ .expected = 2, .got = args.len }); 182 + try scope.setException(sb.written()); 169 183 return RuntimeError.InvalidArguments; 170 184 } 171 185 ··· 180 194 181 195 pub fn set(scope: *Scope, args: []const *Value) RuntimeError!*Value { 182 196 var iter = SliceIterator(*Value).init(args); 197 + var sb = try StringBuilder.init(scope.allocator, EXCEPTION_SIZE); 198 + defer sb.deinit(); 183 199 184 200 while (iter.next()) |sym| { 185 201 const val = iter.next() orelse break; ··· 191 207 p.get(s).?.* = (try val.clone(p.getRoot())).*; 192 208 try p.insert(s, val); 193 209 } else { 194 - var sb = try StringBuilder.init(scope.allocator, 30); 195 - try sb.append("Undefined variable: "); 196 - try sb.append(s); 197 - const sbo = try sb.build(scope.allocator); 198 - try scope.setException(sbo); 210 + sb.clear(); 211 + try util.undefinedVariable(&sb, s); 212 + try scope.setException(sb.written()); 199 213 return RuntimeError.Exception; 200 214 } 201 215 }, 202 - else => return RuntimeError.InvalidArguments, 216 + else => { 217 + sb.clear(); 218 + try util.invalidType(&sb, Value.AtomTag.sym, sym.getType()); 219 + try scope.setException(sb.written()); 220 + 221 + return RuntimeError.InvalidArguments; 222 + }, 203 223 } 204 224 } 205 225 ··· 215 235 } 216 236 217 237 pub fn format(scope: *Scope, args: []const *Value) RuntimeError!*Value { 238 + var sb = try StringBuilder.init(scope.allocator, EXCEPTION_SIZE); 239 + defer sb.deinit(); 240 + 218 241 if (args.len == 0) { 219 - return Value.initNil(scope); 242 + try util.unexpectedArguments(&sb, .{ .expected = 1, .got = args.len, .mode = .at_least }); 243 + try scope.setException(sb.written()); 244 + return RuntimeError.Exception; 220 245 } 221 246 222 247 if (args[0].getType() != .str) { ··· 272 297 } 273 298 274 299 pub fn clone(scope: *Scope, args: []const *Value) RuntimeError!*Value { 300 + var sb = try StringBuilder.init(scope.allocator, EXCEPTION_SIZE); 301 + defer sb.deinit(); 275 302 if (args.len != 1) { 303 + try util.unexpectedArguments(&sb, .{ .expected = 1, .got = args.len }); 304 + try scope.setException(sb.written()); 276 305 return Value.initNil(scope); 277 306 } 278 307 ··· 301 330 } 302 331 303 332 pub fn append(scope: *Scope, args: []const *Value) RuntimeError!*Value { 333 + var sb = try StringBuilder.init(scope.allocator, EXCEPTION_SIZE); 334 + defer sb.deinit(); 304 335 if (args.len == 0) { 336 + try util.unexpectedArguments(&sb, .{ .expected = 1, .got = args.len, .mode = .at_least }); 337 + try scope.setException(sb.written()); 305 338 return Value.initNil(scope); 306 339 } 307 340 ··· 316 349 } 317 350 318 351 pub fn map(scope: *Scope, args: []const *Value) RuntimeError!*Value { 352 + var sb = try StringBuilder.init(scope.allocator, EXCEPTION_SIZE); 353 + defer sb.deinit(); 319 354 if (args.len != 2) { 355 + try util.unexpectedArguments(&sb, .{ .expected = 2, .got = args.len }); 356 + try scope.setException(sb.written()); 320 357 return RuntimeError.Exception; 321 358 } 322 359 ··· 393 430 } 394 431 395 432 pub fn eql(scope: *Scope, args: []const *Value) RuntimeError!*Value { 433 + var sb = try StringBuilder.init(scope.allocator, EXCEPTION_SIZE); 434 + defer sb.deinit(); 396 435 if (args.len != 2) { 436 + try util.unexpectedArguments(&sb, .{ .expected = 2, .got = args.len }); 437 + try scope.setException(sb.written()); 397 438 return RuntimeError.InvalidArguments; 398 439 } 399 440 ··· 402 443 } 403 444 404 445 pub fn not(scope: *Scope, args: []const *Value) RuntimeError!*Value { 446 + var sb = try StringBuilder.init(scope.allocator, EXCEPTION_SIZE); 447 + defer sb.deinit(); 405 448 if (args.len != 1) { 449 + try util.unexpectedArguments(&sb, .{ .expected = 1, .got = args.len }); 450 + try scope.setException(sb.written()); 406 451 return RuntimeError.InvalidArguments; 407 452 } 408 453 ··· 410 455 } 411 456 412 457 pub fn @"and"(scope: *Scope, args: []const *Value) RuntimeError!*Value { 458 + var sb = try StringBuilder.init(scope.allocator, EXCEPTION_SIZE); 459 + defer sb.deinit(); 413 460 if (args.len != 2) { 461 + try util.unexpectedArguments(&sb, .{ .expected = 2, .got = args.len }); 462 + try scope.setException(sb.written()); 414 463 return RuntimeError.InvalidArguments; 415 464 } 416 465 ··· 419 468 } 420 469 421 470 pub fn @"or"(scope: *Scope, args: []const *Value) RuntimeError!*Value { 471 + var sb = try StringBuilder.init(scope.allocator, EXCEPTION_SIZE); 472 + defer sb.deinit(); 422 473 if (args.len != 2) { 474 + try util.unexpectedArguments(&sb, .{ .expected = 2, .got = args.len }); 475 + try scope.setException(sb.written()); 423 476 return RuntimeError.InvalidArguments; 424 477 } 425 478
+12
src/stdlib.zig
··· 1 + const std = @import("std"); 2 + const zexa = @import("./root.zig"); 3 + const Scope = zexa.types.Scope; 4 + const Value = zexa.types.Value; 5 + 6 + pub const corelib = @import("./stdlib/core.zig"); 7 + 8 + pub const StdConfiguration = struct {}; 9 + 10 + pub fn loadStd(scope: *Scope) !void { 11 + try scope.loadLibrary(corelib.load); 12 + }
+1 -1
src/test/hello.zexa
··· 1 - (print "Hello World") 1 + (print "Hello World")
+1 -1
src/test/map.zexa
··· 2 2 (define 'mapped-list (map (lambda (x) (mul x 2)) my-list)) 3 3 4 4 (print my-list) 5 - (print mapped-list) 5 + (print mapped-list)
+30
src/util.zig
··· 1 + const zexa = @import("./root.zig"); 2 + const Value = zexa.lang.Value; 3 + const ValueType = zexa.lang.ValueType; 4 + 5 + const mitochondria = @import("mitochondria"); 6 + pub const StringBuilder = mitochondria.StringBuilder; 7 + 8 + pub fn undefinedVariable(sb: *StringBuilder, name: []const u8) !void { 9 + try sb.appendFormat("undefined variable: {s}", .{name}); 10 + } 11 + 12 + pub fn invalidType(sb: *StringBuilder, expected: ValueType, got: ValueType) !void { 13 + try sb.appendFormat("expected type `{any}` got: `{any}`", .{ expected, got }); 14 + } 15 + 16 + pub fn unexpectedArguments(sb: *StringBuilder, args: struct { 17 + expected: usize, 18 + got: usize, 19 + mode: enum { 20 + exact, 21 + at_least, 22 + at_most, 23 + } = .exact, 24 + }) !void { 25 + switch (args.mode) { 26 + .exact => try sb.appendFormat("unexpected number of arguments (expected {d}, got {d})", .{ args.expected, args.got }), 27 + .at_least => try sb.appendFormat("unexpected number of arguments (expected at least {d}, got {d})", .{ args.expected, args.got }), 28 + .at_most => try sb.appendFormat("unexpected number of arguments (expected at most {d}, got {d})", .{ args.expected, args.got }), 29 + } 30 + }