Support integer literals
This commit is contained in:
parent
25d6ee0245
commit
f2a23db588
2 changed files with 74 additions and 18 deletions
|
@ -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] };
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
|
Loading…
Reference in a new issue