diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 313bab4..e5d9d82 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -1,3 +1,4 @@
+use thiserror::Error;
use winnow::Parser;
use winnow::RecoverableParser;
use winnow::combinator::alt;
@@ -113,9 +114,15 @@ impl ParserError> for AstError {
}
}
-#[derive(Debug)]
+#[derive(Debug, Error)]
pub struct AstFailure {}
+impl std::fmt::Display for AstFailure {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str("TODO")
+ }
+}
+
impl AstFailure {
fn from_errors(_errors: Vec, _input: &[TemplateToken]) -> AstFailure {
AstFailure {}
@@ -217,7 +224,7 @@ mod tests {
fn check_only_content() {
let input = "Hello World";
- let parsed = crate::parser::parse(input).unwrap();
+ let parsed = crate::parser::parse(input.into()).unwrap();
let ast = parse(parsed.tokens()).unwrap();
@@ -239,7 +246,7 @@ mod tests {
fn check_simple_variable_interpolation() {
let input = "Hello {{= world }}";
- let parsed = crate::parser::parse(input).unwrap();
+ let parsed = crate::parser::parse(input.into()).unwrap();
let ast = parse(parsed.tokens()).unwrap();
diff --git a/src/emit/mod.rs b/src/emit/mod.rs
index 6c5dd9e..207866b 100644
--- a/src/emit/mod.rs
+++ b/src/emit/mod.rs
@@ -122,7 +122,7 @@ mod tests {
fn check_simple_variable_interpolation() {
let input = "Hello {{= world }}";
- let parsed = crate::parser::parse(input).unwrap();
+ let parsed = crate::parser::parse(input.into()).unwrap();
let ast = crate::ast::parse(parsed.tokens()).unwrap();
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index a52f148..8984e2b 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -8,14 +8,14 @@ use crate::emit::Instruction;
use crate::input::TempleInput;
#[derive(Debug, Error, Display)]
-enum EvalError {
+pub enum EvaluationError {
/// An unknown variable was encountered: .0
UnknownVariable(TempleInput),
/// An explicit abort was requested
ExplicitAbort,
}
-fn execute(instructions: &[Instruction], global_context: &Context) -> Result {
+pub fn execute(instructions: &[Instruction], global_context: &Context) -> Result {
let mut output = String::new();
let mut scopes: HashMap = HashMap::new();
@@ -27,7 +27,7 @@ fn execute(instructions: &[Instruction], global_context: &Context) -> Result Result todo!(),
- Instruction::Abort => return Err(EvalError::ExplicitAbort),
+ Instruction::Abort => return Err(EvaluationError::ExplicitAbort),
}
}
@@ -52,7 +52,7 @@ mod tests {
fn check_simple_variable_interpolation() {
let input = "Hello {{= world }}";
- let parsed = crate::parser::parse(input).unwrap();
+ let parsed = crate::parser::parse(input.into()).unwrap();
let ast = crate::ast::parse(parsed.tokens()).unwrap();
diff --git a/src/lib.rs b/src/lib.rs
index 9601158..d72e899 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,16 +5,36 @@ use displaydoc::Display;
use serde::Serialize;
use thiserror::Error;
+use crate::emit::Instruction;
+use crate::input::TempleInput;
+
pub mod ast;
pub mod emit;
pub mod eval;
-pub mod parser;
mod input;
+pub mod parser;
#[derive(Debug, Error, Display)]
pub enum TempleError {
/// Could not parse the given template
- ParseError {},
+ ParseError {
+ #[from]
+ source: parser::ParseFailure,
+ },
+ /// Invalid Template
+ AstError {
+ #[from]
+ source: ast::AstFailure,
+ },
+
+ /// An error occurred while evaluating
+ EvaluationError {
+ #[from]
+ source: eval::EvaluationError,
+ },
+
+ /// The template '{0}' could not be found
+ UnknownTemplate(String),
}
pub struct Temple {
@@ -37,17 +57,41 @@ impl Temple {
pub fn add_template(
&mut self,
name: impl Into,
- value: impl AsRef,
+ value: impl Into,
) -> Result<(), TempleError> {
+ let source = value.into();
+ let parse = parser::parse(source.clone())?;
+ let ast = ast::parse(parse.tokens())?;
+
+ let instructions = emit::emit_machine(ast);
+
+ self.templates.insert(
+ name.into(),
+ Template {
+ source,
+ instructions,
+ },
+ );
+
Ok(())
}
- fn render(&self, arg: &str, ctx: &Context) -> Result {
- Ok(String::new())
+ pub fn render(&self, name: &str, ctx: &Context) -> Result {
+ let template = self
+ .templates
+ .get(name)
+ .ok_or_else(|| TempleError::UnknownTemplate(name.to_string()))?;
+
+ let res = eval::execute(&template.instructions, ctx)?;
+
+ Ok(res)
}
}
-struct Template {}
+struct Template {
+ source: TempleInput,
+ instructions: Vec,
+}
pub struct Context {
values: BTreeMap,
@@ -155,6 +199,6 @@ mod tests {
let rendered = temp.render("base", &ctx).unwrap();
- insta::assert_snapshot!(rendered, @"")
+ insta::assert_snapshot!(rendered, @"Hello World")
}
}
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index ecf86d2..ce51f9f 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -4,6 +4,7 @@ use annotate_snippets::AnnotationKind;
use annotate_snippets::Level;
use annotate_snippets::Renderer;
use annotate_snippets::Snippet;
+use thiserror::Error;
use winnow::LocatingSlice;
use winnow::Parser;
use winnow::RecoverableParser;
@@ -37,16 +38,22 @@ use crate::resume_after_cut;
type Input<'input> = Recoverable, ParseError>;
type PResult<'input, T> = Result;
-#[derive(Debug)]
+#[derive(Debug, Error)]
pub struct ParseFailure {
- source: Arc,
+ input: Arc,
errors: Vec,
}
+impl std::fmt::Display for ParseFailure {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(&self.to_report())
+ }
+}
+
impl ParseFailure {
- fn from_errors(errors: Vec, input: &str) -> ParseFailure {
+ fn from_errors(errors: Vec, input: TempleInput) -> ParseFailure {
ParseFailure {
- source: Arc::from(input.to_string()),
+ input: Arc::from(input.to_string()),
errors,
}
}
@@ -64,7 +71,7 @@ impl ParseFailure {
.unwrap_or("An error occurred while parsing"),
)
.element(
- Snippet::source(self.source.as_ref()).annotation(
+ Snippet::source(self.input.as_ref()).annotation(
AnnotationKind::Primary
.span(error.span.clone().map(|s| s.range).unwrap_or_else(|| 0..0)),
),
@@ -294,9 +301,9 @@ impl TemplateToken {
}
}
-pub fn parse(input: &str) -> Result {
+pub fn parse(input: TempleInput) -> Result {
let (_remaining, val, errors) =
- parse_tokens.recoverable_parse(LocatingSlice::new(TempleInput::from(input)));
+ parse_tokens.recoverable_parse(LocatingSlice::new(input.clone()));
if errors.is_empty()
&& let Some(val) = val
@@ -418,7 +425,7 @@ mod tests {
#[test]
fn parse_simple() {
let input = "Hello There";
- let output = parse(input);
+ let output = parse(input.into());
insta::assert_debug_snapshot!(output, @r#"
Ok(
@@ -437,7 +444,7 @@ mod tests {
#[test]
fn parse_interpolate() {
let input = "Hello {{ there }}";
- let output = parse(input);
+ let output = parse(input.into());
insta::assert_debug_snapshot!(output, @r#"
Ok(
@@ -480,12 +487,12 @@ mod tests {
#[test]
fn parse_interpolate_bad() {
let input = "Hello {{ the2re }} {{ the@re }}";
- let output = parse(input);
+ let output = parse(input.into());
insta::assert_debug_snapshot!(output, @r#"
Err(
ParseFailure {
- source: "Hello {{ the2re }} {{ the@re }}",
+ input: "Hello {{ the2re }} {{ the@re }}",
errors: [
ParseError {
message: Some(