Embedded programming language for Zig
1
fork

Configure Feed

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

standard library improvements

IamPyu 1731d287 a1854b33

+320 -56
+60 -1
src/env.zig
··· 13 13 allocator: Allocator, 14 14 primary_scope: Scope, 15 15 16 + /// Initialize an environment with a global allocator 16 17 pub fn init(allocator: Allocator) !Self { 17 18 return Self{ 18 19 .allocator = allocator, ··· 20 21 }; 21 22 } 22 23 24 + /// Deinitialize the environment 23 25 pub fn deinit(self: *Self) void { 24 26 self.primary_scope.deinit(); 25 27 } 26 28 29 + /// Evaluate AST of expressions using the environment's primary scope 27 30 pub fn evalAst(self: *Self, ast: []const *Value) !*Value { 28 31 var iter = SliceIterator(*Value).init(ast); 29 32 var value: ?*Value = null; ··· 117 120 const scope = &env.primary_scope; 118 121 119 122 const list = try Value.initList(scope, &.{ 120 - try Value.initNativeFunc(scope, zexa.std.lib.dbg), 123 + try Value.initNativeFunc(scope, zexa.std.corelib.dbgFormat), 121 124 try Value.initFromAny(scope, 3), 122 125 try Value.initFromAny(scope, 8), 123 126 try Value.initFromAny(scope, 435), ··· 125 128 126 129 _ = try scope.evalExpr(list); 127 130 } 131 + 132 + test "cons2" { 133 + var env = try Environment.init(std.testing.allocator); 134 + defer env.deinit(); 135 + 136 + const scope = &env.primary_scope; 137 + 138 + const list = try Value.initList(scope, &.{ 139 + try Value.initFromAny(scope, 1), 140 + try Value.initFromAny(scope, 2), 141 + try Value.initFromAny(scope, 3), 142 + try Value.initFromAny(scope, 4), 143 + try Value.initFromAny(scope, 5), 144 + try Value.initString(scope, "hello world!"), 145 + try Value.initList(scope, &.{ 146 + try Value.initString(scope, "hello"), 147 + try Value.initString(scope, "world"), 148 + try Value.initNil(scope), 149 + try Value.initFromAny(scope, 1337.69), 150 + try Value.initString(scope, "very long string"), 151 + try Value.initFromAny(scope, false), 152 + }), 153 + }); 154 + 155 + var current = list; 156 + while (current.getCdr()) |cdr| : (current = cdr) { 157 + if (current.getCar()) |car| { 158 + _ = try zexa.std.corelib.dbgFormat(scope, &.{car}); 159 + } 160 + } 161 + } 162 + 163 + test "format" { 164 + var env = try Environment.init(std.testing.allocator); 165 + defer env.deinit(); 166 + 167 + const scope = &env.primary_scope; 168 + 169 + const list = try Value.initList(scope, &.{ 170 + try Value.initNativeFunc(scope, zexa.std.corelib.format), 171 + try Value.initString(scope, "hello %{}% %{}% %{}% %{}%"), 172 + try Value.initFromAny(scope, 3), 173 + try Value.initFromAny(scope, 87), 174 + try Value.initFromAny(scope, 435), 175 + try Value.initString(scope, "heigjerof"), 176 + }); 177 + 178 + const v = try scope.evalExpr(list); 179 + _ = try zexa.std.corelib.dbgFormat(scope, &.{v}); 180 + 181 + const s = try (try Value.initString(scope, "hey ")).add( 182 + scope, 183 + try Value.initString(scope, "cool!"), 184 + ); 185 + _ = try zexa.std.corelib.dbgFormat(scope, &.{s}); 186 + }
+1 -1
src/lexer.zig
··· 37 37 }; 38 38 39 39 test "line_info" { 40 - std.debug.print("at: {f}\n", .{LineInfo{ .file = "src/lexer.zig", .line = 3, .column = 6 }}); 40 + std.debug.print("{f}\n", .{LineInfo{ .file = "src/lexer.zig", .line = 3, .column = 6 }}); 41 41 } 42 42 43 43 pub const Token = struct {
+6 -2
src/std.zig
··· 3 3 const Scope = zexa.types.Scope; 4 4 const Value = zexa.types.Value; 5 5 6 - pub const lib = @import("./std/root.zig"); 6 + pub const corelib = @import("./std/core.zig"); 7 7 8 8 pub fn loadStd(scope: *Scope) !void { 9 - try scope.insert("dbg", try Value.initFromAny(scope, lib.dbg)); 9 + try scope.insert("dbg", try Value.initFromAny(scope, corelib.dbg)); 10 + try scope.insert("format", try Value.initFromAny(scope, corelib.format)); 11 + try scope.insert("clone", try Value.initFromAny(scope, corelib.clone)); 12 + try scope.insert("append", try Value.initFromAny(scope, corelib.append)); 13 + try scope.insert("add", try Value.initFromAny(scope, corelib.add)); 10 14 }
+106
src/std/core.zig
··· 1 + const std = @import("std"); 2 + 3 + const Io = std.Io; 4 + 5 + const SliceIterator = @import("mitochondria").SliceIterator; 6 + 7 + const zexa = @import("zexa"); 8 + const Scope = zexa.types.Scope; 9 + const Value = zexa.types.Value; 10 + 11 + pub fn dbg(scope: *Scope, args: []const *Value) !*Value { 12 + for (args) |v| { 13 + std.debug.print("{any}\n", .{v.*}); 14 + } 15 + 16 + return Value.initNil(scope); 17 + } 18 + 19 + pub fn dbgFormat(scope: *Scope, args: []const *Value) !*Value { 20 + for (args) |v| { 21 + std.debug.print("{f}\n", .{v}); 22 + } 23 + 24 + return Value.initNil(scope); 25 + } 26 + 27 + pub fn format(scope: *Scope, args: []const *Value) !*Value { 28 + if (args.len == 0) { 29 + return Value.initNil(scope); 30 + } 31 + 32 + if (args[0].getType() != .Str) { 33 + return Value.initNil(scope); 34 + } 35 + 36 + const fmt = args[0].*.atom.str; 37 + 38 + var buf: []u8 = try scope.allocator.alloc(u8, fmt.len); 39 + defer scope.allocator.free(buf); 40 + @memcpy(buf, fmt); 41 + 42 + var iter = SliceIterator(*Value).init(args[1..]); 43 + 44 + while (iter.next()) |val| { 45 + const repstr = blk: { 46 + var writer = Io.Writer.Allocating.init(scope.allocator); 47 + try writer.writer.print("{f}", .{val}); 48 + 49 + break :blk try writer.toOwnedSlice(); 50 + }; 51 + defer scope.allocator.free(repstr); 52 + 53 + var writer = Io.Writer.Allocating.init(scope.allocator); 54 + 55 + const needle = "%{}%"; 56 + 57 + if (std.mem.indexOf(u8, buf, needle)) |index| { 58 + var biter = SliceIterator(u8).init(buf); 59 + while (biter.peek()) |c| { 60 + if (biter.index == index) { 61 + try writer.writer.print("{s}", .{repstr}); 62 + biter.index += needle.len; 63 + } else { 64 + try writer.writer.print("{s}", .{&.{c}}); 65 + biter.index += 1; 66 + } 67 + } 68 + 69 + scope.allocator.free(buf); 70 + buf = try writer.toOwnedSlice(); 71 + } 72 + } 73 + 74 + return Value.initString(scope, buf); 75 + } 76 + 77 + pub fn clone(scope: *Scope, args: []const *Value) !*Value { 78 + if (args.len != 1) { 79 + return Value.initNil(scope); 80 + } 81 + 82 + return args[0].clone(scope); 83 + } 84 + 85 + pub fn append(scope: *Scope, args: []const *Value) !*Value { 86 + if (args.len == 0) { 87 + return Value.initNil(scope); 88 + } 89 + 90 + const list = args[0]; 91 + const args2 = args[1..]; 92 + 93 + for (args2) |v| { 94 + list.append(v); 95 + } 96 + } 97 + 98 + pub fn add(scope: *Scope, args: []const *Value) !*Value { 99 + if (args.len == 0) { 100 + return Value.initNil(scope); 101 + } 102 + 103 + // TODO 104 + 105 + return Value.initNil(scope); 106 + }
-13
src/std/root.zig
··· 1 - const std = @import("std"); 2 - 3 - const zexa = @import("zexa"); 4 - const Scope = zexa.types.Scope; 5 - const Value = zexa.types.Value; 6 - 7 - pub fn dbg(scope: *Scope, args: []*Value) !*Value { 8 - for (args) |v| { 9 - std.debug.print("{any}\n", .{v.*}); 10 - } 11 - 12 - return Value.initNil(scope); 13 - }
+147 -39
src/types.zig
··· 63 63 } 64 64 65 65 pub fn evalExpr(self: *Self, expr: *Value) !*Value { 66 - const val = switch (expr.*) { 66 + switch (expr.*) { 67 67 .atom => |atom| switch (atom) { 68 - .nil => Value{ .atom = .nil }, 69 - .bool => |b| Value{ .atom = .{ .bool = b } }, 70 - .num => |n| Value{ .atom = .{ .num = n } }, 68 + .nil => return Value.initNil(self), 69 + .bool => |b| return Value.initFromAny(self, b), 70 + .num => |n| return Value.initFromAny(self, n), 71 71 .str => |s| return try Value.initString(self, s), 72 72 .sym => |s| return self.get(s) orelse try Value.initNil(self), 73 73 .quote => |v| return v, 74 74 .native_func => return expr, 75 75 }, 76 76 .cons => { 77 - const slice = try expr.toSlice(self); 77 + const func = try self.evalExpr(expr.getCar().?); 78 + 79 + var args = try std.ArrayList(*Value).initCapacity(self.allocator, 3); 80 + defer args.deinit(self.allocator); 78 81 79 - if (slice.len == 0) { 80 - return Value.initNil(self); 82 + var current = expr.getCdr().?; 83 + while (current.getCdr()) |cdr| : (current = cdr) { 84 + if (current.getCar()) |car| { 85 + try args.append(self.allocator, try self.evalExpr(car)); 86 + } 81 87 } 82 88 83 - const car = try self.evalExpr(slice[0]); 84 - const cdr = slice[1..]; 85 - 86 - switch (car.*) { 89 + switch (func.*) { 87 90 .atom => |atom| switch (atom) { 88 91 .native_func => |f| { 89 - for (cdr) |*v| { 90 - v.* = try self.evalExpr(v.*); 91 - } 92 - 93 - return f(self, cdr); 92 + return f(self, args.items); 94 93 }, 95 - else => return RuntimeError.InvalidFunction, 94 + else => return expr, 96 95 }, 97 - else => return RuntimeError.InvalidFunction, 96 + else => return expr, 98 97 } 99 98 100 99 return Value.initNil(self); 101 100 }, 102 - }; 103 - 104 - return Value.initFrom(self, val); 101 + } 105 102 } 106 103 }; 107 104 108 105 pub const Symbol = []const u8; 109 106 pub const Number = f64; 110 - pub const NativeFunc = *const fn (scope: *Scope, args: []*Value) anyerror!*Value; 107 + pub const NativeFunc = *const fn (scope: *Scope, args: []const *Value) anyerror!*Value; 111 108 pub const NativeMacro = void; 112 109 113 110 pub const ValueType = enum { ··· 212 209 const ptr = cons; 213 210 var rest = values[1..]; 214 211 while (rest.len != 0) { 215 - std.debug.print("hm?\n", .{}); 216 212 const ncons = try Value.initCons(scope, rest[0], try Value.initNil(scope)); 217 213 218 214 cons.cons.cdr = ncons; ··· 224 220 return ptr; 225 221 } 226 222 223 + pub fn clone(self: *const Self, scope: *Scope) !*Value { 224 + switch (self.*) { 225 + .atom => |atom| switch (atom) { 226 + .nil => return try Value.initNil(scope), 227 + .bool => |b| return try Value.initFromAny(scope, b), 228 + .num => |n| return try Value.initFromAny(scope, n), 229 + .str => |s| return try Value.initString(scope, s), 230 + .sym => |s| return try Value.initSymbol(scope, s), 231 + .quote => |v| return try Value.initFrom(scope, Value{ 232 + .atom = .{ 233 + .quote = v, 234 + }, 235 + }), 236 + .native_func => |f| return try Value.initNativeFunc(scope, f), 237 + }, 238 + .cons => |cons| { 239 + return try Value.initCons(scope, cons.car, cons.cdr); 240 + }, 241 + } 242 + } 243 + 244 + pub fn append(self: *Self, scope: *Scope, other: *Self) !void { 245 + switch (self.*) { 246 + .cons => |*cons| { 247 + if (cons.cdr.isNil()) { 248 + cons.cdr = try Value.initCons(scope, other, try Value.initNil(scope)); 249 + } else { 250 + try cons.cdr.append(scope, other); 251 + } 252 + }, 253 + else => {}, 254 + } 255 + } 256 + 257 + /// If this value is a cons set return the CAR of the set 258 + pub fn getCar(self: *const Self) ?*Value { 259 + return switch (self.*) { 260 + .cons => |cons| cons.car, 261 + else => null, 262 + }; 263 + } 264 + 265 + /// If this value is a cons set return the CDR of the set 266 + pub fn getCdr(self: *const Self) ?*Value { 267 + return switch (self.*) { 268 + .cons => |cons| cons.cdr, 269 + else => null, 270 + }; 271 + } 272 + 227 273 pub fn isNil(self: *const Self) bool { 228 274 return switch (self.*) { 229 275 .atom => |atom| switch (atom) { ··· 236 282 237 283 pub fn toSlice(self: *Self, scope: *Scope) ![]*Value { 238 284 var list = try std.ArrayList(*Value).initCapacity(scope.allocator, 10); 239 - try self.toSliceInner(scope, &list); 285 + try self.toSliceInner(scope, self, &list); 240 286 const slice = try list.toOwnedSlice(scope.allocator); 241 287 defer scope.allocator.free(slice); 242 288 ··· 246 292 return nslice; 247 293 } 248 294 249 - fn toSliceInner(self: *Self, scope: *Scope, list: *std.ArrayList(*Value)) !void { 295 + fn toSliceInner(self: *Self, scope: *Scope, start: *Value, list: *std.ArrayList(*Value)) !void { 296 + if (self == start) { 297 + return; 298 + } 299 + 250 300 switch (self.*) { 251 301 .atom => |atom| switch (atom) { 252 302 .nil => {}, ··· 254 304 }, 255 305 .cons => |cons| { 256 306 try list.append(scope.allocator, cons.car); 257 - try cons.cdr.toSliceInner(scope, list); 258 - }, 259 - } 260 - } 261 - 262 - pub fn append(self: *Self, scope: *Scope, other: *Self) !void { 263 - switch (self.*) { 264 - .cons => |*cons| { 265 - if (cons.cdr.isNil()) { 266 - cons.cdr = try Value.initCons(scope, other, try Value.initNil(scope)); 267 - } else { 268 - try cons.cdr.append(scope, other); 269 - } 307 + try cons.cdr.toSliceInner(scope, start, list); 270 308 }, 271 - else => {}, 272 309 } 273 310 } 274 311 ··· 285 322 }, 286 323 .cons => ValueType.Cons, 287 324 }; 325 + } 326 + 327 + pub fn format(self: *const Self, writer: *std.Io.Writer) !void { 328 + switch (self.*) { 329 + .atom => |atom| switch (atom) { 330 + .nil => try writer.print("nil", .{}), 331 + .bool => |b| try writer.print("{any}", .{b}), 332 + .num => |n| try writer.print("{d}", .{n}), 333 + .str => |s| try writer.print("\"{s}\"", .{s}), 334 + .sym => |s| try writer.print("'{s}", .{s}), 335 + .quote => |v| try writer.print("{f}", .{v}), 336 + .native_func => try writer.print("{*}", .{self}), 337 + }, 338 + .cons => { 339 + _ = try writer.write("("); 340 + var current = self; 341 + while (current.getCdr()) |cdr| : ({ 342 + current = cdr; 343 + if (current.getCdr() != null) { 344 + _ = try writer.write(" "); 345 + } 346 + }) { 347 + if (current.getCar()) |car| { 348 + try car.format(writer); 349 + } 350 + } 351 + _ = try writer.write(")"); 352 + }, 353 + } 354 + } 355 + 356 + pub fn add(self: *const Self, scope: *Scope, other: *const Self) !*Value { 357 + if (self.getType() != other.getType()) { 358 + return Value.initNil(scope); 359 + } 360 + 361 + const cloned = try self.clone(scope); 362 + 363 + switch (cloned.*) { 364 + .atom => |atom| switch (atom) { 365 + .num => |n| return try Value.initFromAny( 366 + scope, 367 + n + other.atom.num, 368 + ), 369 + .str => |s| { 370 + const str = try std.mem.concat(scope.allocator, u8, &.{ 371 + s, 372 + other.atom.str, 373 + }); 374 + defer scope.allocator.free(str); 375 + 376 + return Value.initString(scope, str); 377 + }, 378 + .sym => |s| { 379 + const str = try std.mem.concat(scope.allocator, u8, &.{ 380 + s, 381 + other.atom.sym, 382 + }); 383 + defer scope.allocator.free(str); 384 + 385 + return Value.initSymbol(scope, str); 386 + }, 387 + else => {}, 388 + }, 389 + .cons => |cons| { 390 + _ = cons; 391 + // TODO 392 + }, 393 + } 394 + 395 + return cloned; 288 396 } 289 397 };