Skip to content

Commit

Permalink
Merge b2b73f8 into 0ad1317
Browse files Browse the repository at this point in the history
  • Loading branch information
morenol committed Jul 26, 2020
2 parents 0ad1317 + b2b73f8 commit 23d9639
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 2 deletions.
16 changes: 16 additions & 0 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,32 @@ use std::sync::{Arc, RwLock};
pub struct Context {
global_scope: Arc<RwLock<ValuesMap>>,
external_scope: ValuesMap,
scopes: Vec<Arc<RwLock<ValuesMap>>>,
}

impl Context {
pub fn new(external_scope: ValuesMap) -> Self {
Self {
global_scope: Arc::new(RwLock::new(ValuesMap::default())),
external_scope,
scopes: vec![],
}
}
pub fn enter_scope(&mut self) -> Arc<RwLock<ValuesMap>> {
let scope = Arc::new(RwLock::new(ValuesMap::default()));
self.scopes.push(scope.clone());
scope
}
pub fn exit_scope(&mut self) -> Option<&Arc<RwLock<ValuesMap>>> {
self.scopes.pop();
self.scopes.last()
}
pub fn find(&self, key: &str) -> Value {
for scope in &self.scopes {
if let Some(value) = scope.read().unwrap().get(key) {
return value.clone();
}
}
if let Some(value) = self.external_scope.get(key) {
value.clone()
} else if let Some(value) = self.global_scope.read().unwrap().get(key) {
Expand Down
70 changes: 69 additions & 1 deletion src/statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::expression_evaluator::Evaluate;
use crate::lexer::Token;
use crate::renderer::ComposedRenderer;
use crate::renderer::Render;
use crate::value::Value;
use crate::value::{Value, ValuesList, ValuesMap};
use std::io::Write;
use std::sync::Arc;
pub mod parser;
Expand Down Expand Up @@ -80,22 +80,88 @@ impl<'a> Render for ElseStatement<'a> {
self.body.as_ref().unwrap().render(out, params)
}
}
pub struct ForStatement<'a> {
vars: Vec<String>,
value: Box<dyn Evaluate + 'a>,
body: Option<Arc<ComposedRenderer<'a>>>,
}

impl<'a> ForStatement<'a> {
pub fn new(vars: Vec<String>, value: Box<dyn Evaluate + 'a>) -> Self {
Self {
vars,
value,
body: None,
}
}
fn set_main_body(&mut self, body: Arc<ComposedRenderer<'a>>) {
let for_body = body.clone();
self.body = Some(for_body);
}
fn render_loop(
&self,
loop_value: Value,
out: &mut dyn Write,
mut params: Context,
_level: usize,
) -> Result<()> {
let loop_items: ValuesList = loop_value.into();
let items_size = loop_items.len();
let context = params.enter_scope();
for (item_idx, item) in loop_items.iter().enumerate() {
let mut loop_map = ValuesMap::default();
loop_map.insert("index".to_string(), Value::Integer((item_idx + 1) as i64));
loop_map.insert("index0".to_string(), Value::Integer(item_idx as i64));
loop_map.insert("first".to_string(), Value::Boolean(item_idx == 0));
loop_map.insert(
"last".to_string(),
Value::Boolean(item_idx == items_size - 1),
);

{
let mut context = context.write().unwrap();
if self.vars.len() > 1 {
todo!();
} else {
context.insert(self.vars[0].clone(), item.clone());
}
context.insert("loop".to_string(), Value::ValuesMap(loop_map));
}
params.enter_scope();
self.body.as_ref().unwrap().render(out, params.clone())?;
params.exit_scope();
}

params.exit_scope();
Ok(())
}
}
impl<'a> Render for ForStatement<'a> {
fn render(&self, out: &mut dyn Write, params: Context) -> Result<()> {
let loop_value = self.value.evaluate(params.clone())?;
self.render_loop(loop_value, out, params, 0)?;
Ok(())
}
}

pub enum Statement<'a> {
If(IfStatement<'a>),
Else(ElseStatement<'a>),
For(ForStatement<'a>),
}
impl<'a> Statement<'a> {
pub fn set_main_body(&mut self, body: Arc<ComposedRenderer<'a>>) {
match self {
Statement::If(statement) => statement.set_main_body(body),
Statement::Else(statement) => statement.set_main_body(body),
Statement::For(statement) => statement.set_main_body(body),
}
}
pub fn add_else_branch(&mut self, branch: Statement<'a>) {
match self {
Statement::If(statement) => statement.add_else_branch(branch),
Statement::Else(_statement) => todo!(),
_ => unreachable!(),
}
}
}
Expand All @@ -104,6 +170,7 @@ impl<'a> Render for Statement<'a> {
match self {
Statement::If(statement) => statement.render(out, params),
Statement::Else(statement) => statement.render(out, params),
Statement::For(statement) => statement.render(out, params),
}
}
}
Expand All @@ -120,6 +187,7 @@ pub enum StatementInfoType {
TemplateRoot,
IfStatement,
ElseIfStatement,
ForStatement,
}

impl<'a> StatementInfo<'a> {
Expand Down
77 changes: 76 additions & 1 deletion src/statement/parser.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::{
ElseStatement, IfStatement, Statement, StatementInfo, StatementInfoList, StatementInfoType,
ElseStatement, ForStatement, IfStatement, Statement, StatementInfo, StatementInfoList,
StatementInfoType,
};
use crate::error::{Error, ErrorKind, Result, SourceLocation};
use crate::expression_parser::ExpressionParser;
Expand All @@ -24,6 +25,11 @@ impl StatementParser {
Some(Token::Else) => StatementParser::parse_else(&mut statementinfo_list),
Some(Token::EndIf) => StatementParser::parse_endif(&mut statementinfo_list),
Some(Token::ElIf) => StatementParser::parse_elif(&mut lexer, &mut statementinfo_list),
Some(Token::For) => StatementParser::parse_for(&mut lexer, &mut statementinfo_list),
Some(Token::EndFor) => {
StatementParser::parse_endfor(&mut lexer, &mut statementinfo_list)
}

_ => todo!(),
}
}
Expand Down Expand Up @@ -105,4 +111,73 @@ impl StatementParser {
.add_renderer(Box::new(renderer));
Ok(())
}
fn parse_for<'a>(
lexer: &mut Peekable<Lexer<'a, Token<'a>>>,
statementinfo_list: &mut StatementInfoList<'a>,
) -> Result<()> {
let mut vars = vec![];
loop {
if let Some(Token::Identifier(identifier)) = lexer.next() {
vars.push(identifier.to_string());
} else {
return Err(Error::from(ErrorKind::ExpectedIdentifier(
SourceLocation::new(1, 2),
)));
}
if let Some(Token::Comma) = lexer.peek() {
lexer.next();
} else {
break;
}
}
if let Some(Token::In) = lexer.next() {
let expression = ExpressionParser::full_expresion_parser(lexer)?;
if let Some(_) = lexer.next() {
Err(Error::from(ErrorKind::ExpectedToken(SourceLocation::new(
1, 2,
))))
} else {
let composed_renderer = Arc::new(ComposedRenderer::new());
let renderer = Statement::For(ForStatement::new(vars, Box::new(expression)));
let mut statement_info = StatementInfo::new(
StatementInfoType::ForStatement,
Token::For,
composed_renderer,
);
statement_info.renderer = Some(renderer);
statementinfo_list.push(statement_info);
Ok(())
}
} else {
Err(Error::from(ErrorKind::ExpectedToken(SourceLocation::new(
1, 2,
))))
}
}
fn parse_endfor<'a>(
_lexer: &mut Peekable<Lexer<'a, Token<'a>>>,
statementinfo_list: &mut StatementInfoList<'a>,
) -> Result<()> {
if statementinfo_list.len() <= 1 {
return Err(Error::from(ErrorKind::UnexpectedStatement(
SourceLocation::new(1, 2),
)));
}
let mut info = statementinfo_list.pop().unwrap();
if let StatementInfoType::ForStatement = info.mode {
let mut renderer = info.renderer.unwrap();
let body = info.compositions.remove(0);
renderer.set_main_body(body);
statementinfo_list
.last_mut()
.unwrap()
.current_composition
.add_renderer(Box::new(renderer));
Ok(())
} else {
Err(Error::from(ErrorKind::UnexpectedStatement(
SourceLocation::new(1, 2),
)))
}
}
}
17 changes: 17 additions & 0 deletions src/value/from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,20 @@ impl From<ValuesList> for Value {
Value::ValuesList(f)
}
}

impl From<Value> for ValuesList {
fn from(f: Value) -> Self {
match f {
Value::ValuesList(value_list) => value_list,
Value::ValuesMap(value_map) => value_map
.keys()
.map(|key| Value::String(key.to_string()))
.collect(),
Value::String(value_string) => value_string
.chars()
.map(|ch| Value::String(ch.to_string()))
.collect(),
_ => vec![],
}
}
}
1 change: 1 addition & 0 deletions tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod error;
mod expressions;
mod filters;
mod scoped_context;
mod statement_for;
mod statement_if;
mod utils;
mod whitespace_control;
27 changes: 27 additions & 0 deletions tests/statement_for.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use super::utils::assert_render_template_eq;
use temple::error::Result;
use temple::value::{Value, ValuesMap};
use temple::Context;

#[test]
fn for_over_string() -> Result<()> {
let mut context = ValuesMap::default();
context.insert("word".to_string(), Value::String("hello".to_string()));
let context = Context::new(context);
assert_render_template_eq(
"{% for letter in word %} {{ letter }}{% endfor %}",
" h e l l o",
Some(context),
)
}

#[test]
fn for_over_list_of_numbers() -> Result<()> {
let context = ValuesMap::default();
let context = Context::new(context);
assert_render_template_eq(
"{% for even in [2, 4, 6, 8, 10] %}{% if not loop[\"first\"] %} {%endif %}{{ even // 2 }}{% if loop[\"last\"] %}.{% else %},{% endif %}{% endfor %}",
"1, 2, 3, 4, 5.",
Some(context),
)
}

0 comments on commit 23d9639

Please sign in to comment.