Insert Locators into the interesting AST nodes
This commit is contained in:
parent
67545f9e8e
commit
f54f2093de
@ -1,4 +1,4 @@
|
||||
use crate::IStr;
|
||||
use crate::{IStr, Locator};
|
||||
use serde::Serialize;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@ -24,6 +24,7 @@ pub struct HtmlElement {
|
||||
pub classes: Vec<IStr>,
|
||||
pub dom_id: Option<IStr>,
|
||||
pub attributes: BTreeMap<IStr, Expression>,
|
||||
pub loc: Locator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
@ -31,6 +32,7 @@ pub struct ComponentElement {
|
||||
pub name: IStr,
|
||||
pub slots: BTreeMap<IStr, Vec<Block>>,
|
||||
pub attributes: BTreeMap<IStr, Expression>,
|
||||
pub loc: Locator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
@ -38,18 +40,21 @@ pub struct IfBlock {
|
||||
pub condition: Expression,
|
||||
pub blocks: Vec<Block>,
|
||||
pub else_blocks: Vec<Block>,
|
||||
pub loc: Locator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub struct DefineExpandSlot {
|
||||
pub name: IStr,
|
||||
pub optional: bool,
|
||||
pub loc: Locator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub struct DefineFragment {
|
||||
pub name: IStr,
|
||||
pub blocks: Vec<Block>,
|
||||
pub loc: Locator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
@ -128,19 +133,23 @@ pub enum Expression {
|
||||
FieldLookup {
|
||||
obj: Box<Expression>,
|
||||
ident: IStr,
|
||||
loc: Locator,
|
||||
},
|
||||
MethodCall {
|
||||
obj: Box<Expression>,
|
||||
ident: IStr,
|
||||
args: Vec<Expression>,
|
||||
loc: Locator,
|
||||
},
|
||||
|
||||
// Other Primaries
|
||||
Variable {
|
||||
name: IStr,
|
||||
loc: Locator,
|
||||
},
|
||||
FunctionCall {
|
||||
name: IStr,
|
||||
args: Vec<Expression>,
|
||||
loc: Locator,
|
||||
},
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
pub mod ast;
|
||||
mod parser;
|
||||
use serde::Serialize;
|
||||
|
||||
use arc_interner::ArcIntern;
|
||||
pub use parser::parse_template;
|
||||
use pest::Span;
|
||||
|
||||
use parser::Rule;
|
||||
pub type ParseError = pest::error::Error<Rule>;
|
||||
@ -12,3 +14,21 @@ pub type IStr = ArcIntern<String>;
|
||||
pub fn intern(s: impl Into<String>) -> ArcIntern<String> {
|
||||
ArcIntern::new(s.into())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize)]
|
||||
pub struct Locator {
|
||||
pub filename: IStr,
|
||||
pub line: u16,
|
||||
pub column: u16,
|
||||
}
|
||||
|
||||
impl Locator {
|
||||
pub(crate) fn from_span(span: Span, file: IStr) -> Locator {
|
||||
let (line, col) = span.start_pos().line_col();
|
||||
Locator {
|
||||
filename: file,
|
||||
line: line.min(u16::MAX as usize) as u16,
|
||||
column: col.min(u16::MAX as usize) as u16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use crate::ast::{
|
||||
Block, ComponentElement, DefineExpandSlot, DefineFragment, Expression, HtmlElement, IfBlock,
|
||||
StringExpr, StringPiece, Template,
|
||||
};
|
||||
use crate::{intern, IStr};
|
||||
use crate::{intern, IStr, Locator};
|
||||
use lazy_static::lazy_static;
|
||||
use pest::error::ErrorVariant;
|
||||
use pest::pratt_parser::{Assoc, Op, PrattParser};
|
||||
@ -15,12 +15,21 @@ use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
type PCResult<T> = Result<T, PCError<Rule>>;
|
||||
type Node<'i> = pest_consume::Node<'i, Rule, ()>;
|
||||
type Node<'i> = pest_consume::Node<'i, Rule, ParserUserData>;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[grammar = "hornbeam.pest"]
|
||||
struct HornbeamParser;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct ParserUserData {
|
||||
pub(crate) file: IStr,
|
||||
}
|
||||
|
||||
fn nodeloc(node: &Node) -> Locator {
|
||||
Locator::from_span(node.as_span(), node.user_data().file.clone())
|
||||
}
|
||||
|
||||
fn error<R: Copy + Debug + Hash + Ord>(msg: &str, span: Span) -> PCError<R> {
|
||||
PCError::new_from_span(
|
||||
ErrorVariant::CustomError {
|
||||
@ -51,7 +60,8 @@ impl HornbeamParser {
|
||||
}
|
||||
|
||||
fn Element(input: Node) -> PCResult<Block> {
|
||||
let loc = input.as_span();
|
||||
let span = input.as_span();
|
||||
let loc = nodeloc(&input);
|
||||
let mut children = input.into_children();
|
||||
|
||||
let name = HornbeamParser::ElementName(children.next().unwrap())?;
|
||||
@ -86,7 +96,7 @@ impl HornbeamParser {
|
||||
|
||||
Ok(if name.chars().next().unwrap().is_ascii_lowercase() {
|
||||
if !supply_slots.is_empty() {
|
||||
return Err(error("You can't supply slots to HTML elements.", loc));
|
||||
return Err(error("You can't supply slots to HTML elements.", span));
|
||||
}
|
||||
|
||||
Block::HtmlElement(HtmlElement {
|
||||
@ -95,6 +105,7 @@ impl HornbeamParser {
|
||||
classes,
|
||||
dom_id,
|
||||
attributes,
|
||||
loc,
|
||||
})
|
||||
} else {
|
||||
if !supply_slots.is_empty() {
|
||||
@ -106,6 +117,7 @@ impl HornbeamParser {
|
||||
name,
|
||||
slots,
|
||||
attributes,
|
||||
loc,
|
||||
})
|
||||
} else {
|
||||
let mut slots = BTreeMap::new();
|
||||
@ -114,22 +126,25 @@ impl HornbeamParser {
|
||||
name,
|
||||
slots,
|
||||
attributes,
|
||||
loc,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn DefineFragment(input: Node) -> PCResult<Block> {
|
||||
let loc = nodeloc(&input);
|
||||
Ok(match_nodes!(input.into_children();
|
||||
[Identifier(name), blocks..] => {
|
||||
Block::DefineFragment(DefineFragment {
|
||||
name, blocks: HornbeamParser::helper_blocks(blocks)?
|
||||
name, blocks: HornbeamParser::helper_blocks(blocks)?, loc
|
||||
})
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
fn DefineExpandSlot(input: Node) -> PCResult<Block> {
|
||||
let loc = nodeloc(&input);
|
||||
let (optional, name) = match_nodes!(input.into_children();
|
||||
[SlotOptional(_), Identifier(name)] => {
|
||||
(true, name)
|
||||
@ -138,7 +153,11 @@ impl HornbeamParser {
|
||||
(false, name)
|
||||
}
|
||||
);
|
||||
Ok(Block::DefineExpandSlot(DefineExpandSlot { name, optional }))
|
||||
Ok(Block::DefineExpandSlot(DefineExpandSlot {
|
||||
name,
|
||||
optional,
|
||||
loc,
|
||||
}))
|
||||
}
|
||||
|
||||
fn SlotOptional(_input: Node) -> PCResult<()> {
|
||||
@ -231,19 +250,23 @@ impl HornbeamParser {
|
||||
}
|
||||
|
||||
fn Variable(input: Node) -> PCResult<Expression> {
|
||||
let loc = nodeloc(&input);
|
||||
let name = intern(input.into_children().single()?.as_str());
|
||||
Ok(Expression::Variable { name })
|
||||
Ok(Expression::Variable { name, loc })
|
||||
}
|
||||
|
||||
fn Expr(input: Node) -> PCResult<Expression> {
|
||||
PRATT_PARSER
|
||||
.map_primary(|primary| Ok(match primary.as_rule() {
|
||||
Rule::IntLiteral => Expression::IntLiteral { val: primary.as_str().parse().map_err(|e| error(&format!("can't parse int: {e:?}"), primary.as_span()))? },
|
||||
Rule::String => Expression::StringExpr(HornbeamParser::String(Node::new(primary))?),
|
||||
Rule::Variable => HornbeamParser::Variable(Node::new(primary))?,
|
||||
Rule::FunctionCall => HornbeamParser::FunctionCall(Node::new(primary))?,
|
||||
let ud = input.user_data().clone();
|
||||
let result = PRATT_PARSER
|
||||
.map_primary(|primary| {
|
||||
let node = Node::new_with_user_data(primary, ud.clone());
|
||||
Ok(match node.as_rule() {
|
||||
Rule::IntLiteral => Expression::IntLiteral { val: node.as_str().parse().map_err(|e| error(&format!("can't parse int: {e:?}"), node.as_span()))? },
|
||||
Rule::String => Expression::StringExpr(HornbeamParser::String(node)?),
|
||||
Rule::Variable => HornbeamParser::Variable(node)?,
|
||||
Rule::FunctionCall => HornbeamParser::FunctionCall(node)?,
|
||||
other => unimplemented!("unimp primary {other:?}!"),
|
||||
}))
|
||||
})})
|
||||
.map_prefix(|op, rhs| Ok(match op.as_rule() {
|
||||
Rule::negation => Expression::Negate { sub: Box::new(rhs?) },
|
||||
other => unimplemented!("unimp prefix {other:?}!"),
|
||||
@ -258,28 +281,33 @@ impl HornbeamParser {
|
||||
Rule::band => Expression::BAnd { left: Box::new(lhs?), right: Box::new(rhs?) },
|
||||
other => unimplemented!("unimp infix {other:?}!"),
|
||||
}))
|
||||
.map_postfix(|lhs, op| Ok(match op.as_rule() {
|
||||
.map_postfix(|lhs, op| {
|
||||
let node = Node::new_with_user_data(op, ud.clone());
|
||||
let loc = nodeloc(&node);
|
||||
Ok(match node.as_rule() {
|
||||
Rule::unwrap => unimplemented!("unimp unwrap"),
|
||||
Rule::FieldLookup => {
|
||||
let ident = intern(Node::new(op).into_children().single()?.as_str());
|
||||
Expression::FieldLookup { obj: Box::new(lhs?), ident }
|
||||
let ident = intern(node.into_children().single()?.as_str());
|
||||
Expression::FieldLookup { obj: Box::new(lhs?), ident, loc }
|
||||
},
|
||||
Rule::MethodCall => {
|
||||
match_nodes!(Node::new(op).into_children();
|
||||
match_nodes!(node.into_children();
|
||||
[Identifier(ident), Expr(args)..] => {
|
||||
Expression::MethodCall { obj: Box::new(lhs?), ident, args: args.collect() }
|
||||
Expression::MethodCall { obj: Box::new(lhs?), ident, args: args.collect(), loc }
|
||||
}
|
||||
)
|
||||
}
|
||||
other => unimplemented!("unimp postfix {other:?}!"),
|
||||
}))
|
||||
.parse(input.into_children().into_pairs())
|
||||
})})
|
||||
.parse(input.into_children().into_pairs());
|
||||
result
|
||||
}
|
||||
|
||||
fn FunctionCall(input: Node) -> PCResult<Expression> {
|
||||
let loc = nodeloc(&input);
|
||||
Ok(match_nodes!(input.into_children();
|
||||
[Identifier(name), Expr(args)..] => {
|
||||
Expression::FunctionCall { name, args: args.collect() }
|
||||
Expression::FunctionCall { name, args: args.collect(), loc }
|
||||
}
|
||||
))
|
||||
}
|
||||
@ -289,6 +317,8 @@ impl HornbeamParser {
|
||||
}
|
||||
|
||||
fn IfBlock(input: Node) -> PCResult<Block> {
|
||||
let loc = nodeloc(&input);
|
||||
|
||||
let (cond, blocks, else_blocks) = match_nodes!(input.into_children();
|
||||
[IfCondition(cond), blocks.., ElseBlock(else_blocks)] => {
|
||||
(cond, blocks, else_blocks)
|
||||
@ -302,6 +332,7 @@ impl HornbeamParser {
|
||||
condition: cond,
|
||||
blocks: HornbeamParser::helper_blocks(blocks)?,
|
||||
else_blocks,
|
||||
loc,
|
||||
}))
|
||||
}
|
||||
|
||||
@ -339,8 +370,14 @@ impl HornbeamParser {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_template(input: &str) -> PCResult<Template> {
|
||||
let res = HornbeamParser::parse(Rule::Hornbeam, input)?;
|
||||
pub fn parse_template(input: &str, filename: &str) -> PCResult<Template> {
|
||||
let res = HornbeamParser::parse_with_userdata(
|
||||
Rule::Hornbeam,
|
||||
input,
|
||||
ParserUserData {
|
||||
file: intern(filename),
|
||||
},
|
||||
)?;
|
||||
let hornbeam = res.single()?;
|
||||
HornbeamParser::Hornbeam(hornbeam)
|
||||
}
|
||||
@ -356,7 +393,8 @@ mod tests {
|
||||
r#"
|
||||
// This is a simple Hornbeam template that just shows a <div>
|
||||
div
|
||||
"#
|
||||
"#,
|
||||
"inp"
|
||||
)
|
||||
.unwrap());
|
||||
}
|
||||
@ -368,7 +406,8 @@ div
|
||||
div
|
||||
:someslot
|
||||
"Oops!"
|
||||
"#
|
||||
"#,
|
||||
"inp"
|
||||
));
|
||||
|
||||
assert_yaml_snapshot!(parse_template(
|
||||
@ -376,7 +415,8 @@ div
|
||||
MyComponent
|
||||
:someslot
|
||||
"That's better!"
|
||||
"#
|
||||
"#,
|
||||
"inp"
|
||||
)
|
||||
.unwrap());
|
||||
}
|
||||
@ -391,7 +431,8 @@ MyComponent
|
||||
${"abc" + "def${ 1 + 1 }"}
|
||||
Not too bad now.
|
||||
''
|
||||
"#
|
||||
"#,
|
||||
"inp"
|
||||
)
|
||||
.unwrap());
|
||||
}
|
||||
@ -402,7 +443,8 @@ MyComponent
|
||||
r#"
|
||||
if 10 / 2 == 5 or $point.x == 42 // div for a div?
|
||||
div
|
||||
"#
|
||||
"#,
|
||||
"inp"
|
||||
)
|
||||
.unwrap());
|
||||
|
||||
@ -416,7 +458,8 @@ else if 1 + 2 - 1 == 3 // not too far off, I suppose
|
||||
"Not quite, but fairly close. What kind of world is this?"
|
||||
else // peculiar.
|
||||
"Not even close, eh?"
|
||||
"#
|
||||
"#,
|
||||
"inp"
|
||||
)
|
||||
.unwrap());
|
||||
}
|
||||
@ -433,7 +476,8 @@ div
|
||||
slot :main
|
||||
div
|
||||
optional slot :footer
|
||||
"#
|
||||
"#,
|
||||
"inp"
|
||||
)
|
||||
.unwrap());
|
||||
}
|
||||
@ -452,7 +496,8 @@ div
|
||||
''
|
||||
Footer
|
||||
@footer-copyright{year = today().year}
|
||||
"#
|
||||
"#,
|
||||
"inp"
|
||||
)
|
||||
.unwrap());
|
||||
}
|
||||
|
@ -415,7 +415,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
|
||||
}
|
||||
Ok(Value::Str(Arc::new(output)))
|
||||
}
|
||||
Expression::FieldLookup { obj, ident } => {
|
||||
Expression::FieldLookup { obj, ident, loc } => {
|
||||
let obj_val = self.evaluate_expression(scope_idx, obj)?;
|
||||
match obj_val {
|
||||
Value::Reflective(reflective) => match reflective.reflect_ref() {
|
||||
@ -468,7 +468,7 @@ impl<'a, L: Sync + Send, O: OutputSystem + Send, LS: LocalisationSystem + Sync +
|
||||
Expression::MethodCall { .. } => {
|
||||
unimplemented!()
|
||||
}
|
||||
Expression::Variable { name } => {
|
||||
Expression::Variable { name, loc } => {
|
||||
let locals = &self.scopes[scope_idx].variables;
|
||||
match locals.get(name as &str) {
|
||||
Some(variable_value) => Ok(variable_value.clone()),
|
||||
|
@ -88,8 +88,9 @@ impl<'a, LS> LoadedTemplates<(), LS> {
|
||||
&mut self,
|
||||
template_name: &str,
|
||||
template: &'a str,
|
||||
filename: &str,
|
||||
) -> Result<(), InterpreterError<Infallible, Infallible>> {
|
||||
let template = parse_template(template)?;
|
||||
let template = parse_template(template, filename)?;
|
||||
let ir = ast_to_optimised_ir(template_name, template)?;
|
||||
for (k, v) in ir {
|
||||
self.template_functions.insert(k, Arc::new(v));
|
||||
|
@ -317,6 +317,7 @@ div
|
||||
fragment Footer
|
||||
"Or even adjacent ones"
|
||||
"#,
|
||||
"inp",
|
||||
)
|
||||
.unwrap();
|
||||
assert_yaml_snapshot!(pull_out_entrypoints(template, "TemplateName").unwrap());
|
||||
@ -336,6 +337,7 @@ div
|
||||
fragment Footer
|
||||
"Or even adjacent ones"
|
||||
"#,
|
||||
"inp",
|
||||
)
|
||||
.unwrap();
|
||||
assert_yaml_snapshot!(compile_functions(
|
||||
@ -352,6 +354,7 @@ div.stylish#myid {size=42, stringy="yup", arb=$ritrary}
|
||||
"This is a div with a few extras"
|
||||
OtherComponent {param1=1, param2="two", param3=$three}
|
||||
"#,
|
||||
"inp",
|
||||
)
|
||||
.unwrap();
|
||||
assert_yaml_snapshot!(compile_functions(
|
||||
|
@ -168,7 +168,7 @@ mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
fn parse_ir_and_peephole(text: &str) -> BTreeMap<String, Vec<Step<()>>> {
|
||||
let template = parse_template(text).unwrap();
|
||||
let template = parse_template(text, "inp").unwrap();
|
||||
let entrypoints = pull_out_entrypoints(template, "TemplateName").unwrap();
|
||||
let mut compiled = compile_functions(&entrypoints).unwrap();
|
||||
compiled.values_mut().for_each(apply_all_peephole_passes);
|
||||
|
Loading…
Reference in New Issue
Block a user