Implement {if}
This commit is contained in:
parent
eb382c4126
commit
728d013c6d
|
|
@ -2,7 +2,7 @@ use core::fmt::Write;
|
||||||
|
|
||||||
use crate::Value;
|
use crate::Value;
|
||||||
|
|
||||||
use super::parse::{Ast, AstKind, For, Has, Print, ValuePath};
|
use super::parse::{Ast, AstKind, For, Has, If, Print, ValuePath};
|
||||||
|
|
||||||
pub fn execute(ast: &Ast, value: &dyn Value) -> String {
|
pub fn execute(ast: &Ast, value: &dyn Value) -> String {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
|
|
@ -18,6 +18,7 @@ pub fn execute_(out: &mut String, ast: &Ast, value: &dyn Value) {
|
||||||
AstKind::Print(p) => print(out, p, value),
|
AstKind::Print(p) => print(out, p, value),
|
||||||
AstKind::For(f) => for_(out, f, value),
|
AstKind::For(f) => for_(out, f, value),
|
||||||
AstKind::Has(h) => has(out, h, value),
|
AstKind::Has(h) => has(out, h, value),
|
||||||
|
AstKind::If(h) => if_(out, h, value),
|
||||||
e => todo!("{e:?}"),
|
e => todo!("{e:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +72,20 @@ pub fn has(out: &mut String, f: &Has, value: &dyn Value) {
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
while let Some(value) = value.has() {
|
if let Some(value) = value.has() {
|
||||||
execute_(out, &f.body, value);
|
execute_(out, &f.body, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn if_(out: &mut String, f: &If, value: &dyn Value) {
|
||||||
|
let value = match lookup(out, &f.expr, value) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if value.if_() {
|
||||||
|
execute_(out, &f.then, value);
|
||||||
|
} else if let Some(else_) = f.else_.as_ref() {
|
||||||
|
execute_(out, else_, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ pub type Expr = ValuePath;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct If {
|
pub struct If {
|
||||||
expr: Expr,
|
pub expr: Expr,
|
||||||
then: Ast,
|
pub then: Ast,
|
||||||
else_: Option<Ast>,
|
pub else_: Option<Ast>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|
@ -110,6 +110,8 @@ impl<'a> ParseState<'a> {
|
||||||
self.maybe_parse_for();
|
self.maybe_parse_for();
|
||||||
} else if remaining.starts_with("has") {
|
} else if remaining.starts_with("has") {
|
||||||
self.maybe_parse_has();
|
self.maybe_parse_has();
|
||||||
|
} else if remaining.starts_with("if") {
|
||||||
|
self.maybe_parse_if();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,6 +214,78 @@ impl<'a> ParseState<'a> {
|
||||||
self.new_empty_text();
|
self.new_empty_text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn maybe_parse_if(&mut self) {
|
||||||
|
assert!(self.remaining().starts_with("if"));
|
||||||
|
self.current_byte_offset += "if".len();
|
||||||
|
self.skip_whitespace();
|
||||||
|
let path = match self.parse_path() {
|
||||||
|
None => return,
|
||||||
|
Some(vp) => vp,
|
||||||
|
};
|
||||||
|
self.skip_whitespace();
|
||||||
|
if !self.remaining().starts_with('}') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.current_byte_offset += 1;
|
||||||
|
|
||||||
|
let mut inner = ParseState {
|
||||||
|
template: self.template,
|
||||||
|
last_byte_offset: self.last_byte_offset,
|
||||||
|
speculative_text_starts_at: self.current_byte_offset,
|
||||||
|
speculative_text_ends_at: self.current_byte_offset,
|
||||||
|
current_byte_offset: self.current_byte_offset,
|
||||||
|
ast: Ast::new(),
|
||||||
|
};
|
||||||
|
while inner.has_input() {
|
||||||
|
if inner.remaining().starts_with("{/if}") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if inner.remaining().starts_with("{else}") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
inner.parse_one();
|
||||||
|
}
|
||||||
|
inner.text_might_end();
|
||||||
|
inner.add_current_text_to_ast();
|
||||||
|
self.current_byte_offset = inner.current_byte_offset;
|
||||||
|
|
||||||
|
let else_ = if self.remaining().starts_with("{else}") {
|
||||||
|
self.current_byte_offset += "{else}".len();
|
||||||
|
let mut inner = ParseState {
|
||||||
|
template: self.template,
|
||||||
|
last_byte_offset: self.last_byte_offset,
|
||||||
|
speculative_text_starts_at: self.current_byte_offset,
|
||||||
|
speculative_text_ends_at: self.current_byte_offset,
|
||||||
|
current_byte_offset: self.current_byte_offset,
|
||||||
|
ast: Ast::new(),
|
||||||
|
};
|
||||||
|
while inner.has_input() {
|
||||||
|
if inner.remaining().starts_with("{/if}") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
inner.parse_one();
|
||||||
|
}
|
||||||
|
inner.text_might_end();
|
||||||
|
inner.add_current_text_to_ast();
|
||||||
|
self.current_byte_offset = inner.current_byte_offset;
|
||||||
|
Some(inner.ast)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if !self.remaining().starts_with("{/if}") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.current_byte_offset += "{/if}".len();
|
||||||
|
let body = inner.ast;
|
||||||
|
|
||||||
|
self.push_node_and_text(AstKind::If(If {
|
||||||
|
expr: path,
|
||||||
|
then: body,
|
||||||
|
else_,
|
||||||
|
}));
|
||||||
|
self.new_empty_text();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_path(&mut self) -> Option<ValuePath> {
|
pub fn parse_path(&mut self) -> Option<ValuePath> {
|
||||||
assert!(self.remaining().starts_with('.'));
|
assert!(self.remaining().starts_with('.'));
|
||||||
self.current_byte_offset += 1;
|
self.current_byte_offset += 1;
|
||||||
|
|
@ -268,7 +342,7 @@ pub fn parse(template: &str) -> Ast {
|
||||||
mod test {
|
mod test {
|
||||||
use crate::internal::parse::{AstKind, Print, ValuePath};
|
use crate::internal::parse::{AstKind, Print, ValuePath};
|
||||||
|
|
||||||
use super::{parse, Ast, For, Has};
|
use super::{parse, Ast, For, Has, If};
|
||||||
|
|
||||||
fn go(tmpl: &str, rhs: Ast) {
|
fn go(tmpl: &str, rhs: Ast) {
|
||||||
let ast = parse(tmpl);
|
let ast = parse(tmpl);
|
||||||
|
|
@ -326,6 +400,30 @@ mod test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_if() {
|
||||||
|
go(
|
||||||
|
"{if .}{.}{/if}",
|
||||||
|
vec![AstKind::If(If {
|
||||||
|
expr: ValuePath(vec![]),
|
||||||
|
then: vec![AstKind::Print(Print(ValuePath(vec![])))],
|
||||||
|
else_: None,
|
||||||
|
})],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_if_else() {
|
||||||
|
go(
|
||||||
|
"{if .}{.}{else}Empty{/if}",
|
||||||
|
vec![AstKind::If(If {
|
||||||
|
expr: ValuePath(vec![]),
|
||||||
|
then: vec![AstKind::Print(Print(ValuePath(vec![])))],
|
||||||
|
else_: Some(vec![AstKind::Text("Empty".to_string())]),
|
||||||
|
})],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_for_2() {
|
fn parse_for_2() {
|
||||||
go(
|
go(
|
||||||
|
|
|
||||||
19
src/lib.rs
19
src/lib.rs
|
|
@ -50,7 +50,7 @@ impl Value for &str {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has(&self) -> Option<&dyn Value> {
|
fn has(&self) -> Option<&dyn Value> {
|
||||||
if self.is_empty() {
|
if self.if_() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(self)
|
Some(self)
|
||||||
|
|
@ -58,7 +58,7 @@ impl Value for &str {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn if_(&self) -> bool {
|
fn if_(&self) -> bool {
|
||||||
self.is_empty()
|
!self.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn for_(&self, index: usize) -> Option<&dyn Value> {
|
fn for_(&self, index: usize) -> Option<&dyn Value> {
|
||||||
|
|
@ -316,4 +316,19 @@ mod test {
|
||||||
fn print_has_none() {
|
fn print_has_none() {
|
||||||
go("{has .}{.}{/has}", &None::<&str>, "");
|
go("{has .}{.}{/has}", &None::<&str>, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn print_if() {
|
||||||
|
go("{if .}{.}{/if}", &"Bob", "Bob");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn print_if_false() {
|
||||||
|
go("{if .}{.}{/if}", &"", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn print_if_false_else() {
|
||||||
|
go("{if .}{.}{else}Frank{/if}", &"", "Frank");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue