From f2a23db588a076bd3f352a33490ec3a28fc581bc Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Wed, 14 Dec 2022 02:08:07 -0800 Subject: [PATCH] Support integer literals --- src/template/lib.zig | 91 ++++++++++++++++++++++++++++++-------- src/template/test.tmp.html | 1 + 2 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/template/lib.zig b/src/template/lib.zig index 6e5944d..ac9110e 100644 --- a/src/template/lib.zig +++ b/src/template/lib.zig @@ -235,6 +235,7 @@ fn EvaluateExpression( .slice => |sl| []const std.meta.Elem(EvaluateExpression(sl.iterable, Args, Captures, Context)), }, .optional_unwrap => |expr| std.meta.Child(EvaluateExpression(expr.*, Args, Captures, Context)), + .int => isize, }; } @@ -271,8 +272,8 @@ fn evaluateExpression( }, .slice => |sl| { const iterable = try evaluateExpression(sl.iterable, args, captures, context); - const start = try evaluateExpression(sl.start, args, captures, context); - const end = try evaluateExpression(sl.end, args, captures, context); + const start = std.math.cast(usize, try evaluateExpression(sl.start, args, captures, context)) orelse return error.IndexOutOfBounds; + const end = std.math.cast(usize, try evaluateExpression(sl.end, args, captures, context)) orelse return error.IndexOutOfBounds; if (comptime std.meta.trait.is(.Array)(@TypeOf(iterable))) @compileError("Cannot slice an array, pass a slice or pointer to array instead"); if (start > iterable.len or end > iterable.len) return error.IndexOutOfBounds; @@ -283,6 +284,7 @@ fn evaluateExpression( const val = try evaluateExpression(expr.*, args, captures, context); return val orelse error.NullOptional; }, + .int => |i| return i, }; } @@ -512,7 +514,7 @@ fn parseTemplateTokens(comptime tokens: ControlTokenIter) []const TemplateToken while (iter.next()) |token| switch (token) { .whitespace => |wsp| items = items ++ [_]TemplateToken{.{ .whitespace = wsp }}, - .text => |text| items = items ++ [_]TemplateToken{.{ .text = text }}, + .number, .text => |text| items = items ++ [_]TemplateToken{.{ .text = text }}, .open_bracket => { const next = iter.next() orelse @compileError("Unexpected end of template"); if (next == .open_bracket) { @@ -550,6 +552,33 @@ fn parseTemplateTokens(comptime tokens: ControlTokenIter) []const TemplateToken } } +fn tryParseIdentifier(comptime tokens: ControlTokenIter) ?ParseResult(ControlTokenIter, []const u8) { + comptime { + var iter = skipWhitespace(tokens); + + var ident: []const u8 = ""; + var first: bool = true; + while (iter.next()) |token| switch (token) { + .number, .text => |text| { + if (first and token == .number) return null; + ident = ident ++ text; + first = false; + }, + else => { + iter.putBack(token); + break; + }, + }; + + if (first) return null; + + return ParseResult(ControlTokenIter, []const u8){ + .new_iter = iter, + .item = ident, + }; + } +} + fn parseExpression(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, Expression) { comptime { var iter = tokens; @@ -564,25 +593,24 @@ fn parseExpression(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIt if (expr == null) { expr = .{ .args = {} }; if (iter.peek()) |n| if (n == .text) iter.putBack(.{ .period = {} }); - } else switch (iter.next() orelse break) { - .text => |text| expr = .{ + } else if (tryParseIdentifier(iter)) |ident| { + iter = ident.new_iter; + + expr = .{ .deref = &.{ .container = expr.?, - .field = text, + .field = ident.item, }, - }, - .question_mark => expr = .{ + }; + } else if (iter.peek()) |next| if (next == .question_mark) { + _ = iter.next(); + expr = .{ .optional_unwrap = blk: { const e = expr.?; break :blk &e; }, - }, - else => |t2| { - iter.pushBack(t2); - iter.pushBack(token); - break; - }, - } + }; + }; last_valid_iter = iter; }, .dollar => { @@ -622,6 +650,12 @@ fn parseExpression(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIt expr = .{ .builtin = &builtin.item }; last_valid_iter = iter; }, + .number => |n| { + if (expr != null) break; + const num = std.fmt.parseInt(isize, n, 10) catch @compileError("Error parsing integer"); + expr = .{ .int = num }; + last_valid_iter = iter; + }, else => break, } } @@ -808,7 +842,7 @@ fn parseControlBlock(comptime tokens: ControlTokenIter) ParseResult(ControlToken }, else => { @compileLog(iter.row); - @compileError("TODO " ++ @tagName(token) ++ " " ++ token.text); + @compileError("TODO " ++ @tagName(token)); }, }; @@ -1011,7 +1045,7 @@ fn parseFormat(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter, expectToken(iter.next(), .double_quote); var fmt_str: []const u8 = ""; while (true) switch (iter.next() orelse @compileError("Unexpected end of template")) { - .text, .whitespace => |t| fmt_str = fmt_str ++ t, + .text, .number, .whitespace => |t| fmt_str = fmt_str ++ t, .open_bracket => fmt_str = fmt_str ++ "{", .close_bracket => fmt_str = fmt_str ++ "}", .period => fmt_str = fmt_str ++ ".", @@ -1071,6 +1105,7 @@ const Expression = union(enum) { equals: *const EqualsExpr, builtin: *const BuiltinCall, optional_unwrap: *const Expression, + int: isize, }; const For = struct { @@ -1193,6 +1228,7 @@ const BuiltinCall = union(Builtin) { const ControlToken = union(enum) { text: []const u8, + number: []const u8, open_bracket: void, close_bracket: void, period: void, @@ -1219,6 +1255,18 @@ const ControlTokenIter = struct { row: usize = 0, + fn isControlChar(ch: u8) bool { + return switch (ch) { + '{', '}', '.', '#', '|', '$', '/', '=', '@', ',', '%', '(', ')', '"', '?' => true, + + else => false, + }; + } + + fn isTextChar(ch: u8) bool { + return !std.ascii.isWhitespace(ch) and !std.ascii.isDigit(ch) and !isControlChar(ch); + } + fn next(self: *ControlTokenIter) ?ControlToken { if (self.peeked_token_count != 0) { const t = self.peeked_tokens[self.peeked_token_count - 1].?; @@ -1257,9 +1305,16 @@ const ControlTokenIter = struct { self.start += idx - 1; return .{ .whitespace = remaining[0..idx] }; }, + '0'...'9' => { + var idx: usize = 0; + while (idx < remaining.len and std.ascii.isDigit(remaining[idx])) : (idx += 1) {} + + self.start += idx - 1; + return .{ .number = remaining[0..idx] }; + }, else => { var idx: usize = 0; - while (idx < remaining.len and std.mem.indexOfScalar(u8, "{}.#|$/=@,%()\"? \t\n\r", remaining[idx]) == null) : (idx += 1) {} + while (idx < remaining.len and isTextChar(remaining[idx])) : (idx += 1) {} self.start += idx - 1; return .{ .text = remaining[0..idx] }; diff --git a/src/template/test.tmp.html b/src/template/test.tmp.html index 1b484b9..1879bd0 100644 --- a/src/template/test.tmp.html +++ b/src/template/test.tmp.html @@ -27,6 +27,7 @@ {=/if} sliced: {#for @slice(.foo, .start, .end) |$s|}{$s}, {/for} + sliced: {#for @slice(.foo, 1, 3) |$s|}{$s}, {/for} format: {#format "s" .x}