Support integer literals

This commit is contained in:
jaina heartles 2022-12-14 02:08:07 -08:00
parent 25d6ee0245
commit f2a23db588
2 changed files with 74 additions and 18 deletions

View file

@ -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] };

View file

@ -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}