Add some docs
This commit is contained in:
parent
a6ea4009ff
commit
c61c2dba37
|
|
@ -1,2 +1,5 @@
|
||||||
|
//! NOTE: Changes to this module's public API are unlikely to result in semver
|
||||||
|
//! major version bumps.
|
||||||
|
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
/// The different types of AST nodes
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum AstNodeKind {
|
pub enum AstNodeKind {
|
||||||
If(If),
|
If(If),
|
||||||
|
|
@ -8,9 +9,11 @@ pub enum AstNodeKind {
|
||||||
Text(String),
|
Text(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A lookup path. `.foo.bar`
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct Path(pub Vec<String>);
|
pub struct Path(pub Vec<String>);
|
||||||
|
|
||||||
|
/// An if block. `{if .foo}Hello{else}Goodbye{/if}`
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct If {
|
pub struct If {
|
||||||
pub path: Path,
|
pub path: Path,
|
||||||
|
|
@ -18,12 +21,14 @@ pub struct If {
|
||||||
pub else_: Option<Vec<AstNodeKind>>,
|
pub else_: Option<Vec<AstNodeKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A for block. `{for .names}Hello, {.}! {/for}`
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct For {
|
pub struct For {
|
||||||
pub path: Path,
|
pub path: Path,
|
||||||
pub body: Vec<AstNodeKind>,
|
pub body: Vec<AstNodeKind>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A has block. `{has .optional}{.}{/has}`
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Has {
|
pub struct Has {
|
||||||
pub path: Path,
|
pub path: Path,
|
||||||
|
|
@ -31,17 +36,20 @@ pub struct Has {
|
||||||
pub else_: Option<Vec<AstNodeKind>>,
|
pub else_: Option<Vec<AstNodeKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A with block. `{with .name}Hello {.}!{/with}`
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct With {
|
pub struct With {
|
||||||
pub path: Path,
|
pub path: Path,
|
||||||
pub body: Vec<AstNodeKind>,
|
pub body: Vec<AstNodeKind>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A print block. `{.foo.bar}`
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Print {
|
pub struct Print {
|
||||||
pub path: Path,
|
pub path: Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a source string into a [Vec] of [AstNodeKind].
|
||||||
pub fn parse(source: &str) -> Vec<AstNodeKind> {
|
pub fn parse(source: &str) -> Vec<AstNodeKind> {
|
||||||
parse_many_until_eof(source).1
|
parse_many_until_eof(source).1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,48 +2,73 @@ use std::fmt::Write;
|
||||||
|
|
||||||
use crate::Value;
|
use crate::Value;
|
||||||
|
|
||||||
use self::ops::{Op, OpRef, Ops};
|
use self::ops::{Op, OpIdx, Ops};
|
||||||
|
|
||||||
pub(crate) mod ops {
|
pub mod ops {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::go;
|
use crate::go;
|
||||||
|
|
||||||
use crate::internal::parse::{AstNodeKind, Path};
|
use crate::internal::parse::{AstNodeKind, Path};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub(super) struct OpRef(pub(super) u32);
|
pub struct OpIdx(pub(super) u32);
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub(super) enum Op {
|
pub enum Op {
|
||||||
|
/// Do nothing.
|
||||||
Noop,
|
Noop,
|
||||||
Jump(OpRef),
|
/// Unconditionally set the program counter to the [`OpIdx`].
|
||||||
JumpFalse(OpRef),
|
Jump(OpIdx),
|
||||||
|
/// Set the program counter to the [`OpIdx`] if cond register is false.
|
||||||
|
JumpFalse(OpIdx),
|
||||||
|
/// Exit the program
|
||||||
Exit,
|
Exit,
|
||||||
|
|
||||||
|
/// Emit the contained string to the output string
|
||||||
Emit(String),
|
Emit(String),
|
||||||
|
|
||||||
|
/// Duplicate the current value to the top of the value stack.
|
||||||
ValueDup,
|
ValueDup,
|
||||||
|
/// Pop the top of the value stack and set the current value to it.
|
||||||
ValuePop,
|
ValuePop,
|
||||||
|
/// Lookup the name in the current value
|
||||||
|
///
|
||||||
|
/// See also [`crate::Value::lookup`]
|
||||||
ValueLookup(String),
|
ValueLookup(String),
|
||||||
|
/// Index the current value by the current index register.
|
||||||
|
///
|
||||||
|
/// See also [`crate::Value::index`]
|
||||||
ValueIndex,
|
ValueIndex,
|
||||||
|
/// Print the current value to the output string.
|
||||||
|
///
|
||||||
|
/// See also [`crate::Value::print`]
|
||||||
ValuePrint,
|
ValuePrint,
|
||||||
|
/// Check if the current value is truthy.
|
||||||
|
///
|
||||||
|
/// See also [`crate::Value::is_truthy`]
|
||||||
ValueTruthy,
|
ValueTruthy,
|
||||||
|
/// Set the current value to the value returned from [`crate::Value::has`].
|
||||||
ValueHas,
|
ValueHas,
|
||||||
|
|
||||||
CounterDup,
|
/// Push the current counter register value to the top of the counter
|
||||||
|
/// stack, then set the register to zero.
|
||||||
|
CounterPushZero,
|
||||||
|
/// Pop the top of the counter stack and set the counter register to
|
||||||
|
/// the resulting value.
|
||||||
CounterPop,
|
CounterPop,
|
||||||
|
/// Increment the current counter register
|
||||||
CounterIncrement,
|
CounterIncrement,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub(crate) struct Ops(Vec<Op>);
|
pub struct Ops(Vec<Op>);
|
||||||
impl Ops {
|
impl Ops {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self(vec![])
|
Self(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_ref(&self) -> OpRef {
|
fn next_idx(&self) -> OpIdx {
|
||||||
let next = OpRef(
|
let next = OpIdx(
|
||||||
self.0
|
self.0
|
||||||
.len()
|
.len()
|
||||||
.try_into()
|
.try_into()
|
||||||
|
|
@ -53,23 +78,23 @@ pub(crate) mod ops {
|
||||||
next
|
next
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit(&mut self, op: Op) -> OpRef {
|
fn emit(&mut self, op: Op) -> OpIdx {
|
||||||
let next = self.next_ref();
|
let next = self.next_idx();
|
||||||
self.0.push(op);
|
self.0.push(op);
|
||||||
|
|
||||||
next
|
next
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_mut(&mut self, rf: OpRef) -> &mut Op {
|
fn get_mut(&mut self, idx: OpIdx) -> &mut Op {
|
||||||
self.0.get_mut(rf.0 as usize).expect("OpRef out of bounds")
|
self.0.get_mut(idx.0 as usize).expect("OpIdx out of bounds")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get(&self, rf: OpRef) -> &Op {
|
pub(super) fn get(&self, idx: OpIdx) -> &Op {
|
||||||
self.0.get(rf.0 as usize).expect("OpRef out of bounds")
|
self.0.get(idx.0 as usize).expect("OpIdx out of bounds")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lower_ast(ast: &[AstNodeKind]) -> Ops {
|
pub fn lower_ast(ast: &[AstNodeKind]) -> Ops {
|
||||||
let mut ops = Ops::new();
|
let mut ops = Ops::new();
|
||||||
lower_many(&mut ops, ast);
|
lower_many(&mut ops, ast);
|
||||||
ops.emit(Op::Exit);
|
ops.emit(Op::Exit);
|
||||||
|
|
@ -94,7 +119,7 @@ pub(crate) mod ops {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
AstNodeKind::For(f) => path_stack(ops, &f.path, |ops| {
|
AstNodeKind::For(f) => path_stack(ops, &f.path, |ops| {
|
||||||
ops.emit(Op::CounterDup);
|
ops.emit(Op::CounterPushZero);
|
||||||
let dup = ops.emit(Op::ValueDup);
|
let dup = ops.emit(Op::ValueDup);
|
||||||
ops.emit(Op::ValueIndex);
|
ops.emit(Op::ValueIndex);
|
||||||
let jump_end = ops.emit(Op::Noop);
|
let jump_end = ops.emit(Op::Noop);
|
||||||
|
|
@ -118,26 +143,26 @@ pub(crate) mod ops {
|
||||||
lower_many(ops, &i.then);
|
lower_many(ops, &i.then);
|
||||||
if let Some(else_) = i.else_.as_ref() {
|
if let Some(else_) = i.else_.as_ref() {
|
||||||
let jump_end = ops.emit(Op::Noop);
|
let jump_end = ops.emit(Op::Noop);
|
||||||
*ops.get_mut(jump_else) = Op::JumpFalse(ops.next_ref());
|
*ops.get_mut(jump_else) = Op::JumpFalse(ops.next_idx());
|
||||||
lower_many(ops, else_);
|
lower_many(ops, else_);
|
||||||
*ops.get_mut(jump_end) = Op::Jump(ops.next_ref());
|
*ops.get_mut(jump_end) = Op::Jump(ops.next_idx());
|
||||||
} else {
|
} else {
|
||||||
*ops.get_mut(jump_else) = Op::JumpFalse(ops.next_ref());
|
*ops.get_mut(jump_else) = Op::JumpFalse(ops.next_idx());
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
AstNodeKind::Has(h) => path_stack(ops, &h.path, |ops| {
|
AstNodeKind::Has(h) => path_stack(ops, &h.path, |ops| {
|
||||||
ops.emit(Op::ValueHas);
|
ops.emit(Op::ValueHas);
|
||||||
let jump_else = ops.emit(Op::Noop);
|
let jump_else = ops.emit(Op::Noop);
|
||||||
lower_many(ops, &h.then);
|
lower_many(ops, &h.then);
|
||||||
let next_ref = ops.next_ref();
|
let next_ref = ops.next_idx();
|
||||||
*ops.get_mut(jump_else) = Op::JumpFalse(next_ref);
|
*ops.get_mut(jump_else) = Op::JumpFalse(next_ref);
|
||||||
if let Some(else_) = h.else_.as_ref() {
|
if let Some(else_) = h.else_.as_ref() {
|
||||||
let jump_end = ops.emit(Op::Noop);
|
let jump_end = ops.emit(Op::Noop);
|
||||||
*ops.get_mut(jump_else) = Op::JumpFalse(ops.next_ref());
|
*ops.get_mut(jump_else) = Op::JumpFalse(ops.next_idx());
|
||||||
lower_many(ops, else_);
|
lower_many(ops, else_);
|
||||||
*ops.get_mut(jump_end) = Op::Jump(ops.next_ref());
|
*ops.get_mut(jump_end) = Op::Jump(ops.next_idx());
|
||||||
} else {
|
} else {
|
||||||
*ops.get_mut(jump_else) = Op::JumpFalse(ops.next_ref());
|
*ops.get_mut(jump_else) = Op::JumpFalse(ops.next_idx());
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
@ -205,14 +230,14 @@ pub(crate) mod ops {
|
||||||
vec![
|
vec![
|
||||||
Op::ValueDup,
|
Op::ValueDup,
|
||||||
Op::ValueLookup("test".to_string()),
|
Op::ValueLookup("test".to_string()),
|
||||||
Op::CounterDup,
|
Op::CounterPushZero,
|
||||||
Op::ValueDup,
|
Op::ValueDup,
|
||||||
Op::ValueIndex,
|
Op::ValueIndex,
|
||||||
Op::JumpFalse(OpRef(10)),
|
Op::JumpFalse(OpIdx(10)),
|
||||||
Op::CounterIncrement,
|
Op::CounterIncrement,
|
||||||
Op::ValuePrint,
|
Op::ValuePrint,
|
||||||
Op::ValuePop,
|
Op::ValuePop,
|
||||||
Op::Jump(OpRef(3)),
|
Op::Jump(OpIdx(3)),
|
||||||
Op::CounterPop,
|
Op::CounterPop,
|
||||||
Op::ValuePop,
|
Op::ValuePop,
|
||||||
Op::ValuePop,
|
Op::ValuePop,
|
||||||
|
|
@ -244,9 +269,9 @@ pub(crate) mod ops {
|
||||||
Op::ValueLookup("test".to_string()),
|
Op::ValueLookup("test".to_string()),
|
||||||
Op::ValueTruthy,
|
Op::ValueTruthy,
|
||||||
Op::ValuePop,
|
Op::ValuePop,
|
||||||
Op::JumpFalse(OpRef(7)),
|
Op::JumpFalse(OpIdx(7)),
|
||||||
Op::Emit("Then".to_string()),
|
Op::Emit("Then".to_string()),
|
||||||
Op::Jump(OpRef(8)),
|
Op::Jump(OpIdx(8)),
|
||||||
Op::Emit("Else".to_string()),
|
Op::Emit("Else".to_string()),
|
||||||
Op::Exit,
|
Op::Exit,
|
||||||
]
|
]
|
||||||
|
|
@ -262,7 +287,7 @@ pub(crate) mod ops {
|
||||||
Op::ValueLookup("test".to_string()),
|
Op::ValueLookup("test".to_string()),
|
||||||
Op::ValueTruthy,
|
Op::ValueTruthy,
|
||||||
Op::ValuePop,
|
Op::ValuePop,
|
||||||
Op::JumpFalse(OpRef(6)),
|
Op::JumpFalse(OpIdx(6)),
|
||||||
Op::Emit("Then".to_string()),
|
Op::Emit("Then".to_string()),
|
||||||
Op::Exit,
|
Op::Exit,
|
||||||
]
|
]
|
||||||
|
|
@ -271,13 +296,25 @@ pub(crate) mod ops {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct VmState<'a> {
|
pub struct VmState<'a> {
|
||||||
|
/// The register used to check if the VM should jump.
|
||||||
|
///
|
||||||
|
/// See also [`ops::Op::Jump`] and [`ops::Op::JumpFalse`]
|
||||||
reg_cond: bool,
|
reg_cond: bool,
|
||||||
|
/// The program counter.
|
||||||
reg_pc: u32,
|
reg_pc: u32,
|
||||||
|
/// The current value.
|
||||||
|
///
|
||||||
|
/// See also the [`ops::Op`] variants that start with `Value`.
|
||||||
reg_value: &'a dyn Value,
|
reg_value: &'a dyn Value,
|
||||||
|
/// The current loop counter.
|
||||||
|
///
|
||||||
|
/// See also the [`ops::Op`] variants that start with `Counter`.
|
||||||
reg_counter: usize,
|
reg_counter: usize,
|
||||||
|
|
||||||
|
/// The stack for storing values.
|
||||||
stack_values: Vec<&'a dyn Value>,
|
stack_values: Vec<&'a dyn Value>,
|
||||||
|
/// The stack for storing counters.
|
||||||
stack_counters: Vec<usize>,
|
stack_counters: Vec<usize>,
|
||||||
|
|
||||||
output: String,
|
output: String,
|
||||||
|
|
@ -285,7 +322,7 @@ pub(crate) struct VmState<'a> {
|
||||||
ops: &'a Ops,
|
ops: &'a Ops,
|
||||||
}
|
}
|
||||||
impl<'a> VmState<'a> {
|
impl<'a> VmState<'a> {
|
||||||
pub(crate) fn new(ops: &'a Ops, value: &'a dyn Value) -> Self {
|
pub fn new(ops: &'a Ops, value: &'a dyn Value) -> Self {
|
||||||
Self {
|
Self {
|
||||||
reg_cond: false,
|
reg_cond: false,
|
||||||
reg_pc: 0,
|
reg_pc: 0,
|
||||||
|
|
@ -301,7 +338,7 @@ impl<'a> VmState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn execute(mut self) -> String {
|
pub fn execute(mut self) -> String {
|
||||||
self.execute_loop();
|
self.execute_loop();
|
||||||
self.output
|
self.output
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +353,7 @@ impl<'a> VmState<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_one(&mut self) -> bool {
|
fn execute_one(&mut self) -> bool {
|
||||||
let op = self.ops.get(OpRef(self.reg_pc));
|
let op = self.ops.get(OpIdx(self.reg_pc));
|
||||||
let mut op_offset = 1;
|
let mut op_offset = 1;
|
||||||
match op {
|
match op {
|
||||||
Op::Exit => return true,
|
Op::Exit => return true,
|
||||||
|
|
@ -378,8 +415,9 @@ impl<'a> VmState<'a> {
|
||||||
self.reg_cond = false;
|
self.reg_cond = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Op::CounterDup => {
|
Op::CounterPushZero => {
|
||||||
self.stack_counters.push(self.reg_counter);
|
self.stack_counters.push(self.reg_counter);
|
||||||
|
self.reg_counter = 0;
|
||||||
},
|
},
|
||||||
Op::CounterPop => {
|
Op::CounterPop => {
|
||||||
self.reg_counter =
|
self.reg_counter =
|
||||||
|
|
|
||||||
47
src/lib.rs
47
src/lib.rs
|
|
@ -1,3 +1,13 @@
|
||||||
|
//! # Introduction
|
||||||
|
//! Newt is a small templating library for Rust. The templating language aims
|
||||||
|
//! to be simple to understand.
|
||||||
|
//!
|
||||||
|
//! See [`Newt`] for the primary entrypoint into the library. Additionally,
|
||||||
|
//! some parts of [`internal`] are exposed for advanced use cases.
|
||||||
|
//!
|
||||||
|
//! # A note on versioning
|
||||||
|
//! NOTE: Changes to modules in [`internal`] are unlikely to result in a semver
|
||||||
|
//! major version bump.
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
|
||||||
pub mod internal;
|
pub mod internal;
|
||||||
|
|
@ -10,11 +20,35 @@ use internal::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The primary trait exposed by this crate. Implement this trait if you would
|
||||||
|
/// like to be able to render a type in a template.
|
||||||
pub trait Value: core::fmt::Debug {
|
pub trait Value: core::fmt::Debug {
|
||||||
|
/// Print this value to the given string.
|
||||||
|
///
|
||||||
|
/// See also [`internal::parse::Print`] and [`internal::vm::ops::Op::ValuePrint`]
|
||||||
fn print(&self, out: &mut String);
|
fn print(&self, out: &mut String);
|
||||||
|
|
||||||
|
/// Lookup a field name in this value. The returned [`Value`] will be set as
|
||||||
|
/// the current value inside the block.
|
||||||
|
///
|
||||||
|
/// See also [internal::vm::ops::Op::ValueLookup]
|
||||||
fn lookup(&self, name: &str) -> Option<&dyn Value>;
|
fn lookup(&self, name: &str) -> Option<&dyn Value>;
|
||||||
|
|
||||||
|
/// Like [`Option::map`], but for a value. The returned [`Value`] will be set
|
||||||
|
/// as the current value inside the block.
|
||||||
|
///
|
||||||
|
/// See also [`internal::parse::Has`] and [`internal::vm::ops::Op::ValueHas`]
|
||||||
fn has(&self) -> Option<&dyn Value>;
|
fn has(&self) -> Option<&dyn Value>;
|
||||||
|
|
||||||
|
/// Check if a value is truthy.
|
||||||
|
///
|
||||||
|
/// See also [`internal::parse::If`] and [`internal::vm::ops::Op::ValueTruthy`]
|
||||||
fn is_truthy(&self) -> bool;
|
fn is_truthy(&self) -> bool;
|
||||||
|
|
||||||
|
/// Lookup the current index in this value. The returned [`Value`] will be
|
||||||
|
/// set as the current value inside the block.
|
||||||
|
///
|
||||||
|
/// See also [`internal::parse::For`] and [`internal::vm::ops::Op::ValueIndex`]
|
||||||
fn index(&self, index: usize) -> Option<&dyn Value>;
|
fn index(&self, index: usize) -> Option<&dyn Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,8 +239,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A map from [`&str`] to `&dyn` [`Value`]s.
|
||||||
|
///
|
||||||
|
/// NOTE: This is best used when the number of keys is relatively small, since
|
||||||
|
/// internally this is a [`Vec`] of `(&str, &dyn Value)`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ValuesListMap<'a>(pub &'a [(&'a str, &'a dyn Value)]);
|
pub struct ValuesListMap<'a>(&'a [(&'a str, &'a dyn Value)]);
|
||||||
impl<'a> Value for ValuesListMap<'a> {
|
impl<'a> Value for ValuesListMap<'a> {
|
||||||
fn print(&self, w: &mut String) {
|
fn print(&self, w: &mut String) {
|
||||||
for (name, v) in self.0 {
|
for (name, v) in self.0 {
|
||||||
|
|
@ -374,11 +412,12 @@ impl Value for bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The primary entrypoint to this library.
|
||||||
pub struct Newt {
|
pub struct Newt {
|
||||||
ops: Ops,
|
ops: Ops,
|
||||||
}
|
}
|
||||||
impl Newt {
|
impl Newt {
|
||||||
#[must_use]
|
/// Parse and compile the given template.
|
||||||
pub fn build(tmpl: &str) -> Self {
|
pub fn build(tmpl: &str) -> Self {
|
||||||
let ast = parse(tmpl);
|
let ast = parse(tmpl);
|
||||||
let ops = lower_ast(&ast);
|
let ops = lower_ast(&ast);
|
||||||
|
|
@ -386,11 +425,14 @@ impl Newt {
|
||||||
Self { ops }
|
Self { ops }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute this template with `value` as the initial value.
|
||||||
pub fn execute(&self, value: &dyn Value) -> String {
|
pub fn execute(&self, value: &dyn Value) -> String {
|
||||||
VmState::new(&self.ops, value).execute()
|
VmState::new(&self.ops, value).execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A helper macro to construct a map of [&str] names to `&dyn` [`Value`]s.
|
||||||
|
/// See also [`ValuesListMap`].
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! values_map {
|
macro_rules! values_map {
|
||||||
($($key:path: $val:expr),* $(,)?) => {{
|
($($key:path: $val:expr),* $(,)?) => {{
|
||||||
|
|
@ -404,6 +446,7 @@ macro_rules! values_map {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A helper macro to construct a list of `&dyn` [`Value`]s.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! values {
|
macro_rules! values {
|
||||||
($($val:expr),* $(,)?) => {{
|
($($val:expr),* $(,)?) => {{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue