From 062bd8abd71d39c761ed57358834aa427fb5d950 Mon Sep 17 00:00:00 2001 From: Olivier Date: Mon, 7 Aug 2023 22:31:02 +0100 Subject: [PATCH] Allow more complicated types of bindings in For and use $ for var bindings --- hornbeam_grammar/src/ast.rs | 8 +++- hornbeam_grammar/src/hornbeam.pest | 4 +- hornbeam_grammar/src/parser.rs | 24 +++++++--- ...am_grammar__parser__tests__for_blocks.snap | 16 ++++--- hornbeam_interpreter/src/engine.rs | 46 +++++++++++++++---- hornbeam_ir/src/ir.rs | 4 +- 6 files changed, 76 insertions(+), 26 deletions(-) diff --git a/hornbeam_grammar/src/ast.rs b/hornbeam_grammar/src/ast.rs index 5c99f1f..43c51ba 100644 --- a/hornbeam_grammar/src/ast.rs +++ b/hornbeam_grammar/src/ast.rs @@ -46,13 +46,19 @@ pub struct IfBlock { #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct ForBlock { - pub binding: IStr, + pub binding: Binding, pub iterable: Expression, pub blocks: Vec, pub empty_blocks: Vec, pub loc: Locator, } +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum Binding { + Variable(IStr), + Ignore, +} + #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct DefineExpandSlot { pub name: IStr, diff --git a/hornbeam_grammar/src/hornbeam.pest b/hornbeam_grammar/src/hornbeam.pest index cdb3a95..b4e2120 100644 --- a/hornbeam_grammar/src/hornbeam.pest +++ b/hornbeam_grammar/src/hornbeam.pest @@ -185,8 +185,10 @@ MapLiteral = { "{" ~ commaSeparatedKVPairs ~ "}" } // As in a let binding or for binding. // More options in the future, but for now you just get one identifier and that's it! Binding = { - Identifier + VarBinding | IgnoreBinding } +VarBinding = { "$" ~ Identifier } +IgnoreBinding = { "_" } LocalisationIdentifier = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_" | "-")+ } ParameterisedLocalisation = { "@" ~ LocalisationIdentifier ~ (!"{" | MapLiteral) } diff --git a/hornbeam_grammar/src/parser.rs b/hornbeam_grammar/src/parser.rs index 13c71cd..2657088 100644 --- a/hornbeam_grammar/src/parser.rs +++ b/hornbeam_grammar/src/parser.rs @@ -1,8 +1,8 @@ #![allow(non_snake_case)] use crate::ast::{ - Block, ComponentElement, DefineExpandSlot, DefineFragment, Expression, ForBlock, HtmlElement, - IfBlock, StringExpr, StringPiece, Template, + Binding, Block, ComponentElement, DefineExpandSlot, DefineFragment, Expression, ForBlock, + HtmlElement, IfBlock, StringExpr, StringPiece, Template, }; use crate::{intern, IStr, Locator}; use lazy_static::lazy_static; @@ -374,8 +374,20 @@ impl HornbeamParser { })) } - fn Binding(input: Node) -> PCResult { - Ok(intern(input.as_str())) + fn Binding(input: Node) -> PCResult { + Ok(match_nodes!(input.into_children(); + [VarBinding(b)] => b, + [IgnoreBinding(b)] => b, + )) + } + + fn VarBinding(input: Node) -> PCResult { + let str = Self::Identifier(input.into_children().single()?)?; + Ok(Binding::Variable(str)) + } + + fn IgnoreBinding(input: Node) -> PCResult { + Ok(Binding::Ignore) } fn EmptyForBlock(input: Node) -> PCResult> { @@ -515,8 +527,8 @@ else // peculiar. fn for_blocks() { assert_yaml_snapshot!(parse_template( r#" -for x in $xs - for y in $ys +for $x in $xs + for $y in $ys "Woot" empty "no ys" diff --git a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__for_blocks.snap b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__for_blocks.snap index ba5108b..8112862 100644 --- a/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__for_blocks.snap +++ b/hornbeam_grammar/src/snapshots/hornbeam_grammar__parser__tests__for_blocks.snap @@ -1,27 +1,29 @@ --- source: hornbeam_grammar/src/parser.rs -expression: "parse_template(r#\"\nfor x in $xs\n for y in $ys\n \"Woot\"\n empty\n \"no ys\"\nempty\n \"no xs\"\n \"#,\n \"inp\").unwrap()" +expression: "parse_template(r#\"\nfor $x in $xs\n for $y in $ys\n \"Woot\"\n empty\n \"no ys\"\nempty\n \"no xs\"\n \"#,\n \"inp\").unwrap()" --- blocks: - ForBlock: - binding: x - iterator: + binding: + Variable: x + iterable: Variable: name: xs loc: filename: inp line: 2 - column: 10 + column: 11 blocks: - ForBlock: - binding: y - iterator: + binding: + Variable: y + iterable: Variable: name: ys loc: filename: inp line: 3 - column: 14 + column: 15 blocks: - Text: pieces: diff --git a/hornbeam_interpreter/src/engine.rs b/hornbeam_interpreter/src/engine.rs index 9c2af1c..e30b9ef 100644 --- a/hornbeam_interpreter/src/engine.rs +++ b/hornbeam_interpreter/src/engine.rs @@ -3,6 +3,7 @@ use crate::InterpreterError; use async_recursion::async_recursion; use bevy_reflect::{FromReflect, Reflect, ReflectRef}; use fluent_templates::lazy_static::lazy_static; +use hornbeam_grammar::ast::Binding; use hornbeam_grammar::Locator; use hornbeam_ir::ir::{Expression, Step, StepDef, StringPiece}; use itertools::Itertools; @@ -195,12 +196,26 @@ impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpret self.run_steps(scope_idx, empty_steps).await?; } else { for val in list { - self.scopes[scope_idx] - .variables - .insert(String::from(binding as &str), val); + // TODO duplicated code + match binding { + Binding::Variable(var) => { + self.scopes[scope_idx] + .variables + .insert(String::from(var as &str), val); + } + Binding::Ignore => {} + } + self.run_steps(scope_idx, body_steps).await?; } - self.scopes[scope_idx].variables.remove(binding as &str); + + // TODO duplicated code + match binding { + Binding::Variable(var) => { + self.scopes[scope_idx].variables.remove(var as &str); + } + Binding::Ignore => {} + } } } Value::Reflective(reflective) => match reflective.reflect_ref() { @@ -212,13 +227,26 @@ impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpret // TODO(performance) I'd like to remove this clone! // Can possibly do so with a Yoke or something... let val = list.get(idx).expect("checked iter").clone_value(); - self.scopes[scope_idx].variables.insert( - String::from(binding as &str), - Value::from_reflect(val), - ); + // TODO duplicated code + match binding { + Binding::Variable(var) => { + self.scopes[scope_idx].variables.insert( + String::from(var as &str), + Value::from_reflect(val), + ); + } + Binding::Ignore => {} + } self.run_steps(scope_idx, body_steps).await?; } - self.scopes[scope_idx].variables.remove(binding as &str); + + // TODO duplicated code + match binding { + Binding::Variable(var) => { + self.scopes[scope_idx].variables.remove(var as &str); + } + Binding::Ignore => {} + } } } other => { diff --git a/hornbeam_ir/src/ir.rs b/hornbeam_ir/src/ir.rs index dd9049f..30d831d 100644 --- a/hornbeam_ir/src/ir.rs +++ b/hornbeam_ir/src/ir.rs @@ -1,3 +1,4 @@ +use hornbeam_grammar::ast::Binding; pub use hornbeam_grammar::ast::{Expression, StringPiece}; use hornbeam_grammar::{IStr, Locator}; use serde::Serialize; @@ -34,8 +35,7 @@ pub enum StepDef { }, For { iterable: Expression, - // TODO! - binding: IStr, + binding: Binding, body_steps: Vec, empty_steps: Vec, },