From: Brendan Hansen Date: Tue, 1 Nov 2022 03:13:24 +0000 (-0500) Subject: added selector expressions X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=865dfda489d9fb94b6a02338de65618a2f2324de;p=onyxlang.io.git added selector expressions --- diff --git a/src/app.onyx b/src/app.onyx index 4c52d97..d923656 100644 --- a/src/app.onyx +++ b/src/app.onyx @@ -15,6 +15,14 @@ reg: otmp.TemplateRegistry; y = "123123", numbers = .[1, 2, 3, 4], names = .["joe", "jim", "john"], + + matrix = .[ + .[ 1, 2, 3, 4 ], + .[ 5, 6, 7, 8 ], + .[ 9, 10, 11, 12 ] + ], + + test = ^reg, }); res->status(200); } diff --git a/src/html-templates/src/otmp.onyx b/src/html-templates/src/otmp.onyx index e40d4f3..9b7f757 100644 --- a/src/html-templates/src/otmp.onyx +++ b/src/html-templates/src/otmp.onyx @@ -62,7 +62,9 @@ delete :: (t: ^TemplateRegistry) { temp.filepath = filename |> string.alloc_copy(as_allocator(^self.arena)); temp.name = permanent_name; - parse_template(temp, ^contents); + if err := parse_template(temp, ^contents); err != .None { + return .Template_Parse_Error; + } self.templates[permanent_name] = temp; return .None; diff --git a/src/html-templates/src/parser.onyx b/src/html-templates/src/parser.onyx index 7ed451e..c48935f 100644 --- a/src/html-templates/src/parser.onyx +++ b/src/html-templates/src/parser.onyx @@ -51,6 +51,8 @@ TemplateToken :: struct { String_Literal; Variable; + Symbol; + Dot; } type: Type; @@ -78,7 +80,36 @@ TemplateToken :: struct { return tkn; } - return self->eat_next_token(); + tkn := self->eat_next_token(); + return tkn; + } + + eat_characters :: (self: ^TemplateLexer, chars := 1) -> str { + for chars { + if self.s.data[it] == #char "\n" { + self.line += 1; + self.col = 0; + } + + self.col += 1; + } + + defer string.advance(self.s, chars); + return self.s.data[0 .. chars]; + } + + eat_whitespace :: (self: ^TemplateLexer) { + while !string.empty(*self.s) { + switch self.s.data[0] { + case #char "\n", #char "\t", #char "\r", #char " " { + self->eat_characters(1); + } + + case #default { + break break; + } + } + } } eat_next_token :: (self: ^TemplateLexer) -> TemplateToken { @@ -86,7 +117,7 @@ TemplateToken :: struct { tkn.line = self.line; tkn.col = self.col; - string.strip_leading_whitespace(self.s); + self->eat_whitespace(); if string.empty(*self.s) { self.hit_eof = true; @@ -130,7 +161,7 @@ TemplateToken :: struct { } if self.inside_command || self.inside_expression { - string.strip_leading_whitespace(self.s); + self->eat_whitespace(); token_consume("block", .Keyword_Block); token_consume("endblock", .Keyword_EndBlock); @@ -138,32 +169,51 @@ TemplateToken :: struct { token_consume("endforeach", .Keyword_EndForeach); token_consume("in", .Keyword_In); token_consume("extends", .Keyword_Extends); + token_consume(".", .Dot); if self.s.data[0] == #char "\"" { // :TODO add escaped strings - string.advance(self.s, 1); + self->eat_characters(1); - tkn.text, *self.s = string.bisect(*self.s, #char "\""); + index := string.index_of(*self.s, #char "\""); + tkn.text = self->eat_characters(index); + self->eat_characters(1); yield_token(.String_Literal); } if self.s.data[0] == #char "$" { - string.advance(self.s, 1); + self->eat_characters(1); + + chars := 0; + while chars < self.s.length && self.s.data[chars]->is_alphanum() { + chars += 1; + } - tkn.text = string.read_alphanum(self.s); + tkn.text = self->eat_characters(chars); yield_token(.Variable); } + if self.s.data[0]->is_alphanum() { + chars := 0; + while chars < self.s.length && + (self.s.data[chars]->is_alphanum() || self.s.data[chars] == #char "_") { + chars += 1; + } + + tkn.text = self->eat_characters(chars); + + yield_token(.Symbol); + } + } else { length := 1; while self.s.data[length] != #char "{" && length < self.s.length { length += 1; } - tkn.text = self.s.data[0 .. length]; - string.advance(self.s, length); + tkn.text = self->eat_characters(length); yield_token(.Text); } @@ -174,7 +224,7 @@ TemplateToken :: struct { token_match :: macro (t: str, body: Code) { if string.starts_with(*self.s, t) { - string.advance(self.s, t.length); + tkn.text = self->eat_characters(t.length); #unquote body; } @@ -182,8 +232,7 @@ TemplateToken :: struct { token_consume :: macro (t: str, kind: TemplateToken.Type) { if string.starts_with(*self.s, t) { - tkn.text = self.s.data[0 .. t.length]; - string.advance(self.s, t.length); + tkn.text = self->eat_characters(t.length); yield_token(kind); } } @@ -332,6 +381,9 @@ parse_statement :: (use p: ^TemplateParser) -> ParseError { #local parse_expression :: (use p: ^TemplateParser) -> (^TExpr, ParseError) { + retval: ^TExpr = null; + err := ParseError.None; + switch tkn := p.l->consume(); tkn.type { case .Keyword_Block { name, err := parse_string(p); @@ -340,7 +392,7 @@ parse_expression :: (use p: ^TemplateParser) -> (^TExpr, ParseError) { block_expr := make_expr(t, TExprBlock); block_expr.block_name = name; - return block_expr, .None; + retval = block_expr; } case .Variable { @@ -349,11 +401,40 @@ parse_expression :: (use p: ^TemplateParser) -> (^TExpr, ParseError) { var_expr := make_expr(t, TExprVar); var_expr.var_name = name; - return var_expr, .None; + retval = var_expr; + } + } + + if retval == null { + err = .Unexpected_Token; + } + + while true do switch tkn := p.l->peek(); tkn.type { + case .Dot { + p.l->consume(); + + sym_tkn: TemplateToken; + + // this is gross... + if err := do { + expect_token(p, .Symbol, #(sym_tkn)); + return .None; + }; err != .None { + err = .Unexpected_Token; + break break; + } + + select_expr := make_expr(t, TExprSelector); + select_expr.var = retval; + select_expr.field = sym_tkn.text |> string.alloc_copy(as_allocator(^t.node_storage)); + + retval = select_expr; } + + case #default do break break; } - return null, .Unexpected_Token; + return retval, err; } #local @@ -374,7 +455,7 @@ expect_token :: #match #local {} #overload expect_token :: macro (p: ^TemplateParser, type: TemplateToken.Type, out: Code) { if (p.l->peek()).type != type { - return .Expected_Token; + return ParseError.Expected_Token; } (#unquote out) = p.l->consume(); @@ -383,7 +464,7 @@ expect_token :: macro (p: ^TemplateParser, type: TemplateToken.Type, out: Code) #overload expect_token :: macro (p: ^TemplateParser, type: TemplateToken.Type) { if (p.l->peek()).type != type { - return .Expected_Token; + return ParseError.Expected_Token; } p.l->consume(); diff --git a/src/html-templates/src/render.onyx b/src/html-templates/src/render.onyx index cc3b985..a24a095 100644 --- a/src/html-templates/src/render.onyx +++ b/src/html-templates/src/render.onyx @@ -1,6 +1,7 @@ package otmp use core {io, tprintf} +use core.misc {any_iter, any_dereference, any_selector} #package TemplateRenderer :: struct { @@ -54,6 +55,22 @@ render_instructions :: (use r: ^TemplateRenderer, instrs: [..] ^TNode) -> Error } case TNodeForeach { + // :Temporary :TemplateVariables + for_node := cast(^TNodeForeach) it; + var := cast(^TExprVar) for_node.list; + if !(scope->has(var.var_name)) { + continue; + } + + for any_iter(scope->get(var.var_name)) { + scope->put(for_node.var_name, it); + + if err := render_instructions(r, for_node.body); err != .None { + return err; + } + } + + scope->delete(for_node.var_name); } @@ -69,14 +86,11 @@ render_instructions :: (use r: ^TemplateRenderer, instrs: [..] ^TNode) -> Error } } - case TExprVar { - // :Temporary :TemplateVariables - var := cast(^TExprVar) it; - if !(scope->has(var.var_name)) { - continue; - } + case TExprVar, TExprSelector { + var := resolve_expr_to_any(r, cast(^TExpr) it); + if !var.data do continue; - io.write_format_va(w, "{}", .[scope->get(var.var_name)]); + io.write_format_va(w, "{}", .[var]); } case #default { @@ -88,3 +102,24 @@ render_instructions :: (use r: ^TemplateRenderer, instrs: [..] ^TNode) -> Error return .None; } + +#local +resolve_expr_to_any :: (use r: ^TemplateRenderer, expr: ^TExpr) -> any { + switch expr.type { + case TExprVar { + var := cast(^TExprVar) expr; + // :ErrorHandling if variable does not exist + return scope->get(var.var_name); + } + + case TExprSelector { + selector := cast(^TExprSelector) expr; + + sub_any := resolve_expr_to_any(r, selector.var); + sub_any = any_dereference(sub_any); + return any_selector(sub_any, selector.field); + } + } + + return .{null, void}; +} diff --git a/src/html-templates/src/types.onyx b/src/html-templates/src/types.onyx index 8e1388f..90286bb 100644 --- a/src/html-templates/src/types.onyx +++ b/src/html-templates/src/types.onyx @@ -79,6 +79,8 @@ Error :: enum { Template_Not_Found; Duplicate_Template; + Template_Parse_Error; + Render_Error; } diff --git a/www/static/css/style.css b/www/static/css/style.css index 8000e72..1cefa94 100644 --- a/www/static/css/style.css +++ b/www/static/css/style.css @@ -2,6 +2,9 @@ padding: 0; margin: 0; box-sizing: border-box; + + background-color: #111; + color: #fff; } diff --git a/www/templates/base.html b/www/templates/base.html index a1ae781..41f8552 100644 --- a/www/templates/base.html +++ b/www/templates/base.html @@ -11,4 +11,4 @@ {% block "content" %} - \ No newline at end of file + diff --git a/www/templates/index.html b/www/templates/index.html index 1ef7886..484b9c5 100644 --- a/www/templates/index.html +++ b/www/templates/index.html @@ -12,6 +12,28 @@
  • {% $name %}
  • {{endforeach}} + +
      + {{foreach $num in $numbers}} +
    1. {% $num %}
    2. + {{endforeach}} +
    + + + + {{foreach $row in $matrix}} + + {{foreach $col in $row}} + + {{endforeach}} + + {{endforeach}} + +
    {% $col %}
    + +

    Name: {% $test.templates %}

    +

    Age: {% $test.arena %}

    + {{endblock}} {{extends "base"}}