add basic config structure parsing and add some validation, mostly to demonstrate

This commit is contained in:
elkowar 2021-07-18 19:48:16 +02:00
parent de9d979ce5
commit bfb7c5a27b
No known key found for this signature in database
GPG key ID: E321AD71B1D1F27F
14 changed files with 300 additions and 46 deletions

View file

@ -1,23 +1,28 @@
use eww_config::{ast::*, config::*, format_diagnostic::ToDiagnostic}; use eww_config::{
config::*,
format_diagnostic::ToDiagnostic,
parser::{ast::*, element::FromAst},
};
fn main() { fn main() {
let mut files = codespan_reporting::files::SimpleFiles::new(); let mut files = codespan_reporting::files::SimpleFiles::new();
let input = r#" let input = r#"
(heyho :foo { "foo \" } bar " } (heyho ; :foo { "foo \" } bar " }
:baz {(foo == bar ? 12.2 : 12)} ; :baz {(foo == bar ? 12.2 : 12)}
(foo) (foo)
(defwidget foo [something bla] "foo")
(baz))"#; (baz))"#;
let file_id = files.add("foo.eww", input); let file_id = files.add("foo.eww", input);
let ast = eww_config::parse_string(file_id, input); let ast = eww_config::parser::parse_string(file_id, input);
match ast.and_then(Element::<Ast, Ast>::from_ast) { match ast.and_then(eww_config::parser::element::Element::<Ast, Ast>::from_ast) {
Ok(ast) => { Ok(ast) => {
println!("{:?}", ast); println!("{:?}", ast);
} }
Err(err) => { Err(err) => {
dbg!(&err); dbg!(&err);
let diag = err.to_diagnostic(&files); let diag = err.to_diagnostic();
use codespan_reporting::term; use codespan_reporting::term;
let config = term::Config::default(); let config = term::Config::default();
let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always);

40
examples/validation.rs Normal file
View file

@ -0,0 +1,40 @@
use eww_config::{
config::{widget_definition::WidgetDefinition, widget_use::WidgetUse, *},
error::AstError,
format_diagnostic::ToDiagnostic,
parser::{ast::*, element::FromAst},
};
fn main() {
let mut files = codespan_reporting::files::SimpleFiles::new();
let input_use = r#"
(foo :something 12
:bla "bruh"
"some text")
"#;
let input_def = r#"
(defwidget foo [something bla] "foo")
"#;
let file_id_use = files.add("use.eww", input_use);
let file_id_def = files.add("def.eww", input_def);
let parsed_use = WidgetUse::from_ast(eww_config::parser::parse_string(file_id_use, input_use).unwrap()).unwrap();
let parsed_def = WidgetDefinition::from_ast(eww_config::parser::parse_string(file_id_def, input_def).unwrap()).unwrap();
let defs = maplit::hashmap! {
"foo".to_string() => parsed_def,
};
match validate::validate(&defs, &parsed_use) {
Ok(ast) => {
println!("{:?}", ast);
}
Err(err) => {
let err = AstError::ValidationError(err);
let diag = err.to_diagnostic();
use codespan_reporting::term;
let config = term::Config::default();
let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always);
term::emit(&mut writer, &config, &files, &diag).unwrap();
}
}
}

View file

@ -0,0 +1,3 @@
pub mod validate;
pub mod widget_definition;
pub mod widget_use;

42
src/config/validate.rs Normal file
View file

@ -0,0 +1,42 @@
use std::collections::HashMap;
use simplexpr::SimplExpr;
use crate::{
error::AstResult,
parser::{
ast::{Ast, AstIterator, Span},
element::{Element, FromAst},
},
spanned,
value::{AttrName, VarName},
};
use super::{widget_definition::WidgetDefinition, widget_use::WidgetUse};
#[derive(Debug, thiserror::Error)]
pub enum ValidationError {
#[error("Unknown widget referenced: {1}")]
UnknownWidget(Span, String),
#[error("Missing attribute `{arg_name}` in use of widget `{widget_name}`")]
MissingAttr { widget_name: String, arg_name: AttrName, arg_list_span: Span, use_span: Span },
}
pub fn validate(defs: &HashMap<String, WidgetDefinition>, content: &WidgetUse) -> Result<(), ValidationError> {
if let Some(def) = defs.get(&content.name) {
for expected in def.expected_args.iter() {
if !content.attrs.contains_key(expected) {
return Err(ValidationError::MissingAttr {
widget_name: def.name.to_string(),
arg_name: expected.clone(),
arg_list_span: def.args_span,
use_span: content.span,
});
}
}
} else {
return Err(ValidationError::UnknownWidget(content.span, content.name.to_string()));
}
Ok(())
}

View file

@ -0,0 +1,44 @@
use std::collections::HashMap;
use simplexpr::SimplExpr;
use crate::{
error::AstResult,
parser::{
ast::{Ast, AstIterator, Span},
element::{Element, FromAst},
},
spanned,
value::{AttrName, VarName},
};
use super::widget_use::WidgetUse;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct WidgetDefinition {
pub name: String,
pub expected_args: Vec<AttrName>,
pub widget: WidgetUse,
pub span: Span,
pub args_span: Span,
}
impl FromAst for WidgetDefinition {
fn from_ast(e: Ast) -> AstResult<Self> {
let span = e.span();
spanned!(e.span(), {
let list = e.as_list()?;
let mut iter = AstIterator::new(list.into_iter());
let (_, def_type) = iter.expect_symbol()?;
assert!(def_type == "defwidget");
let (_, name) = iter.expect_symbol()?;
let (args_span, expected_args) = iter.expect_array()?;
let expected_args = expected_args.into_iter().map(|x| x.as_symbol().map(AttrName)).collect::<AstResult<_>>()?;
let widget = iter.expect_any().and_then(WidgetUse::from_ast)?;
// TODO verify that this was the last element in the list
// iter.expect_done()?;
Self { name, expected_args, widget, span, args_span }
})
}
}

43
src/config/widget_use.rs Normal file
View file

@ -0,0 +1,43 @@
use std::collections::HashMap;
use simplexpr::SimplExpr;
use crate::{
error::AstResult,
parser::{
ast::{Ast, AstIterator, Span},
element::{Element, FromAst},
},
spanned,
value::AttrName,
};
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct WidgetUse {
pub name: String,
pub attrs: HashMap<AttrName, SimplExpr>,
pub children: Vec<WidgetUse>,
pub span: Span,
}
impl FromAst for WidgetUse {
fn from_ast(e: Ast) -> AstResult<Self> {
let span = e.span();
spanned!(e.span(), {
if let Ok(text) = e.as_value_ref() {
Self {
name: "text".to_string(),
attrs: maplit::hashmap! { AttrName("text".to_string()) => SimplExpr::Literal(span.into(), text.clone()) },
children: Vec::new(),
span,
}
} else {
let list = e.as_list()?;
let mut iter = AstIterator::new(list.into_iter());
let (_, name) = iter.expect_symbol()?;
let attrs = iter.expect_key_values()?.into_iter().map(|(k, v)| (AttrName(k), v)).collect();
let children = iter.map(WidgetUse::from_ast).collect::<AstResult<Vec<_>>>()?;
Self { name, attrs, children, span }
}
})
}
}

View file

@ -1,6 +1,9 @@
use crate::parser::{ use crate::{
ast::{Ast, AstType, Span}, config::validate::ValidationError,
lexer, parse_error, parser::{
ast::{Ast, AstType, Span},
lexer, parse_error,
},
}; };
use codespan_reporting::{diagnostic, files}; use codespan_reporting::{diagnostic, files};
use thiserror::Error; use thiserror::Error;
@ -11,10 +14,15 @@ pub type AstResult<T> = Result<T, AstError>;
pub enum AstError { pub enum AstError {
#[error("Definition invalid")] #[error("Definition invalid")]
InvalidDefinition(Option<Span>), InvalidDefinition(Option<Span>),
#[error("Expected a {1}, but got nothing")] #[error("Expected another element, but got nothing")]
MissingNode(Option<Span>, AstType), MissingNode(Option<Span>),
#[error("Wrong type of expression: Expected {1} but got {2}")] #[error("Wrong type of expression: Expected {1} but got {2}")]
WrongExprType(Option<Span>, AstType, AstType), WrongExprType(Option<Span>, AstType, AstType),
#[error("Expected to get a value, but got {1}")]
NotAValue(Option<Span>, AstType),
#[error(transparent)]
ValidationError(#[from] ValidationError),
#[error("Parse error: {source}")] #[error("Parse error: {source}")]
ParseError { file_id: Option<usize>, source: lalrpop_util::ParseError<usize, lexer::Token, parse_error::ParseError> }, ParseError { file_id: Option<usize>, source: lalrpop_util::ParseError<usize, lexer::Token, parse_error::ParseError> },
@ -24,8 +32,10 @@ impl AstError {
pub fn get_span(&self) -> Option<Span> { pub fn get_span(&self) -> Option<Span> {
match self { match self {
AstError::InvalidDefinition(span) => *span, AstError::InvalidDefinition(span) => *span,
AstError::MissingNode(span, _) => *span, AstError::MissingNode(span) => *span,
AstError::WrongExprType(span, ..) => *span, AstError::WrongExprType(span, ..) => *span,
AstError::NotAValue(span, ..) => *span,
AstError::ValidationError(error) => None, // TODO none here is stupid
AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)), AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)),
} }
} }
@ -58,18 +68,18 @@ pub fn spanned(span: Span, err: impl Into<AstError>) -> AstError {
use AstError::*; use AstError::*;
match err.into() { match err.into() {
AstError::InvalidDefinition(None) => AstError::InvalidDefinition(Some(span)), AstError::InvalidDefinition(None) => AstError::InvalidDefinition(Some(span)),
AstError::MissingNode(None, x) => AstError::MissingNode(Some(span), x), AstError::MissingNode(None) => AstError::MissingNode(Some(span)),
AstError::WrongExprType(None, x, y) => AstError::WrongExprType(Some(span), x, y), AstError::WrongExprType(None, x, y) => AstError::WrongExprType(Some(span), x, y),
x => x, x => x,
} }
} }
pub trait OptionAstErrorExt<T> { pub trait OptionAstErrorExt<T> {
fn or_missing(self, t: AstType) -> Result<T, AstError>; fn or_missing(self) -> Result<T, AstError>;
} }
impl<T> OptionAstErrorExt<T> for Option<T> { impl<T> OptionAstErrorExt<T> for Option<T> {
fn or_missing(self, t: AstType) -> Result<T, AstError> { fn or_missing(self) -> Result<T, AstError> {
self.ok_or(AstError::MissingNode(None, t)) self.ok_or(AstError::MissingNode(None))
} }
} }
@ -87,7 +97,7 @@ impl<T, E: Into<AstError>> AstResultExt<T> for Result<T, E> {
macro_rules! spanned { macro_rules! spanned {
($span:expr, $block:expr) => {{ ($span:expr, $block:expr) => {{
let span = $span; let span = $span;
let result: Result<_, AstError> = try { $block }; let result: Result<_, crate::error::AstError> = try { $block };
result.at(span) crate::error::AstResultExt::at(result, span)
}}; }};
} }

View file

@ -16,8 +16,8 @@ macro_rules! gen_diagnostic {
Diagnostic::error() Diagnostic::error()
$(.with_message($msg))? $(.with_message($msg))?
$(.with_labels(vec![ $(.with_labels(vec![
Label::primary($span.2, $span.0..$span.1) Label::primary($span.2, $span.0..$span.1)
$(.with_message($label))? $(.with_message($label))?
]))? ]))?
$(.with_notes(vec![$note]))? $(.with_notes(vec![$note]))?
}; };
@ -34,15 +34,29 @@ pub trait ToDiagnostic {
impl ToDiagnostic for AstError { impl ToDiagnostic for AstError {
fn to_diagnostic(&self) -> Diagnostic<usize> { fn to_diagnostic(&self) -> Diagnostic<usize> {
let diag = Diagnostic::error(); if let AstError::ValidationError(error) = self {
if let Some(span) = self.get_span() { match error {
use lalrpop_util::ParseError::*; crate::config::validate::ValidationError::UnknownWidget(span, name) => gen_diagnostic! {
msg = format!("No widget named {} exists", name),
label = span => "Used here",
},
crate::config::validate::ValidationError::MissingAttr { widget_name, arg_name, arg_list_span, use_span } => {
let diag = gen_diagnostic! {
msg = format!("{}", error),
};
diag.with_labels(vec![
Label::secondary(use_span.2, use_span.0..use_span.1).with_message("Argument missing here"),
Label::secondary(arg_list_span.2, arg_list_span.0..arg_list_span.1).with_message("but is required here"),
])
}
}
} else if let Some(span) = self.get_span() {
match self { match self {
AstError::InvalidDefinition(_) => todo!(), AstError::InvalidDefinition(_) => todo!(),
AstError::MissingNode(_, expected) => gen_diagnostic! { AstError::MissingNode(_) => gen_diagnostic! {
msg = format!("Missing {}", expected), msg = "Expected another element",
label = span => format!("Expected `{}` here", expected), label = span => "Expected another element here",
}, },
AstError::WrongExprType(_, expected, actual) => gen_diagnostic! { AstError::WrongExprType(_, expected, actual) => gen_diagnostic! {
@ -50,14 +64,20 @@ impl ToDiagnostic for AstError {
label = span => format!("Expected a `{}` here", expected), label = span => format!("Expected a `{}` here", expected),
note = format!("Expected: {}\nGot: {}", expected, actual), note = format!("Expected: {}\nGot: {}", expected, actual),
}, },
AstError::NotAValue(_, actual) => gen_diagnostic! {
msg = format!("Expected value, but got {}", actual),
label = span => "Expected some value here",
note = format!("Got: {}", actual),
},
AstError::ParseError { file_id, source } => lalrpop_error_to_diagnostic(source, span, |error| match error { AstError::ParseError { file_id, source } => lalrpop_error_to_diagnostic(source, span, |error| match error {
parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, span), parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, span),
parse_error::ParseError::LexicalError(_) => lexical_error_to_diagnostic(span), parse_error::ParseError::LexicalError(_) => lexical_error_to_diagnostic(span),
}), }),
_ => panic!(),
} }
} else { } else {
diag.with_message(format!("{}", self)) Diagnostic::error().with_message(format!("{}", self))
} }
} }
} }
@ -86,7 +106,7 @@ fn lalrpop_error_to_diagnostic<T: std::fmt::Display, E>(
fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, span: Span) -> Diagnostic<usize> { fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, span: Span) -> Diagnostic<usize> {
use simplexpr::error::Error::*; use simplexpr::error::Error::*;
match error { match error {
ParseError { source } => lalrpop_error_to_diagnostic(source, span, move |error| lexical_error_to_diagnostic(span)), ParseError { source, .. } => lalrpop_error_to_diagnostic(source, span, move |error| lexical_error_to_diagnostic(span)),
ConversionError(error) => conversion_error_to_diagnostic(error, span), ConversionError(error) => conversion_error_to_diagnostic(error, span),
Eval(error) => gen_diagnostic!(format!("{}", error), span), Eval(error) => gen_diagnostic!(format!("{}", error), span),
Other(error) => gen_diagnostic!(format!("{}", error), span), Other(error) => gen_diagnostic!(format!("{}", error), span),

View file

@ -1,15 +1,21 @@
use itertools::Itertools; use itertools::Itertools;
use simplexpr::ast::SimplExpr; use simplexpr::{ast::SimplExpr, dynval::DynVal};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Display; use std::fmt::Display;
use super::element::FromAst; use super::element::FromAst;
use crate::error::{AstError, AstResult}; use crate::error::{AstError, AstResult, OptionAstErrorExt};
#[derive(Eq, PartialEq, Clone, Copy)] #[derive(Eq, PartialEq, Clone, Copy)]
pub struct Span(pub usize, pub usize, pub usize); pub struct Span(pub usize, pub usize, pub usize);
impl Into<simplexpr::Span> for Span {
fn into(self) -> simplexpr::Span {
simplexpr::Span(self.0, self.1, self.2)
}
}
impl std::fmt::Display for Span { impl std::fmt::Display for Span {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}..{}", self.0, self.1) write!(f, "{}..{}", self.0, self.1)
@ -45,7 +51,7 @@ pub enum Ast {
Array(Span, Vec<Ast>), Array(Span, Vec<Ast>),
Keyword(Span, String), Keyword(Span, String),
Symbol(Span, String), Symbol(Span, String),
Value(Span, String), Value(Span, DynVal),
SimplExpr(Span, SimplExpr), SimplExpr(Span, SimplExpr),
Comment(Span), Comment(Span),
} }
@ -69,7 +75,7 @@ macro_rules! as_func {
} }
impl Ast { impl Ast {
as_func!(AstType::Value, as_value as_value_ref<String> = Ast::Value(_, x) => x); as_func!(AstType::Value, as_value as_value_ref<DynVal> = Ast::Value(_, x) => x);
as_func!(AstType::Symbol, as_symbol as_symbol_ref<String> = Ast::Symbol(_, x) => x); as_func!(AstType::Symbol, as_symbol as_symbol_ref<String> = Ast::Symbol(_, x) => x);
@ -155,7 +161,7 @@ macro_rules! return_or_put_back {
self.iter.put_back(other); self.iter.put_back(other);
Err(AstError::WrongExprType(Some(span), expr_type, actual_type)) Err(AstError::WrongExprType(Some(span), expr_type, actual_type))
} }
None => Err(AstError::MissingNode(None, expr_type)), None => Err(AstError::MissingNode(None)),
} }
} }
}; };
@ -164,14 +170,20 @@ macro_rules! return_or_put_back {
impl<I: Iterator<Item = Ast>> AstIterator<I> { impl<I: Iterator<Item = Ast>> AstIterator<I> {
return_or_put_back!(expect_symbol, AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)); return_or_put_back!(expect_symbol, AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x));
return_or_put_back!(expect_string, AstType::Value, (Span, String) = Ast::Value(span, x) => (span, x)); return_or_put_back!(expect_value, AstType::Value, (Span, DynVal) = Ast::Value(span, x) => (span, x));
return_or_put_back!(expect_list, AstType::List, (Span, Vec<Ast>) = Ast::List(span, x) => (span, x)); return_or_put_back!(expect_list, AstType::List, (Span, Vec<Ast>) = Ast::List(span, x) => (span, x));
return_or_put_back!(expect_array, AstType::Array, (Span, Vec<Ast>) = Ast::Array(span, x) => (span, x));
pub fn new(iter: I) -> Self { pub fn new(iter: I) -> Self {
AstIterator { iter: itertools::put_back(iter) } AstIterator { iter: itertools::put_back(iter) }
} }
pub fn expect_any<T: FromAst>(&mut self) -> AstResult<T> {
self.iter.next().or_missing().and_then(T::from_ast)
}
pub fn expect_key_values<T: FromAst>(&mut self) -> AstResult<HashMap<String, T>> { pub fn expect_key_values<T: FromAst>(&mut self) -> AstResult<HashMap<String, T>> {
parse_key_values(&mut self.iter) parse_key_values(&mut self.iter)
} }

View file

@ -1,16 +1,13 @@
use super::ast::{Ast, AstIterator, AstType, Span}; use super::ast::{Ast, AstIterator, AstType, Span};
use crate::{error::*, parser, spanned}; use crate::{error::*, parser, spanned, value::AttrName};
use itertools::Itertools; use itertools::Itertools;
use simplexpr::ast::SimplExpr;
use std::{ use std::{
collections::{HashMap, LinkedList}, collections::{HashMap, LinkedList},
iter::FromIterator, iter::FromIterator,
str::FromStr, str::FromStr,
}; };
type VarName = String;
type AttrValue = String;
type AttrName = String;
pub trait FromAst: Sized { pub trait FromAst: Sized {
fn from_ast(e: Ast) -> AstResult<Self>; fn from_ast(e: Ast) -> AstResult<Self>;
} }
@ -21,6 +18,17 @@ impl FromAst for Ast {
} }
} }
impl FromAst for SimplExpr {
fn from_ast(e: Ast) -> AstResult<Self> {
match e {
Ast::Symbol(span, x) => Ok(SimplExpr::VarRef(span.into(), x)),
Ast::Value(span, x) => Ok(SimplExpr::Literal(span.into(), x)),
Ast::SimplExpr(span, x) => Ok(x),
_ => Err(AstError::NotAValue(Some(e.span()), e.expr_type())),
}
}
}
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct Element<C, A> { pub struct Element<C, A> {
name: String, name: String,
@ -36,7 +44,7 @@ impl<C: FromAst, A: FromAst> FromAst for Element<C, A> {
let list = e.as_list()?; let list = e.as_list()?;
let mut iter = AstIterator::new(list.into_iter()); let mut iter = AstIterator::new(list.into_iter());
let (_, name) = iter.expect_symbol()?; let (_, name) = iter.expect_symbol()?;
let attrs = iter.expect_key_values()?; let attrs = iter.expect_key_values()?.into_iter().map(|(k, v)| (AttrName(k), v)).collect();
let children = iter.map(C::from_ast).collect::<AstResult<Vec<_>>>()?; let children = iter.map(C::from_ast).collect::<AstResult<Vec<_>>>()?;
Element { span, name, attrs, children } Element { span, name, attrs, children }
}) })

View file

@ -61,12 +61,12 @@ regex_rules! {
r"\(" => |_| Token::LPren, r"\(" => |_| Token::LPren,
r"\)" => |_| Token::RPren, r"\)" => |_| Token::RPren,
r"\[" => |_| Token::LBrack, r"\[" => |_| Token::LBrack,
r"\]" => |_| Token::LBrack, r"\]" => |_| Token::RBrack,
r"true" => |_| Token::True, r"true" => |_| Token::True,
r"false" => |_| Token::False, r"false" => |_| Token::False,
r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(x), r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(x),
r#"[+-]?(?:[0-9]+[.])?[0-9]+"# => |x| Token::NumLit(x), r#"[+-]?(?:[0-9]+[.])?[0-9]+"# => |x| Token::NumLit(x),
r#"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)]*"# => |x| Token::Symbol(x), r#"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)\[\](){}]*"# => |x| Token::Symbol(x),
r#":\S+"# => |x| Token::Keyword(x), r#":\S+"# => |x| Token::Keyword(x),
r#";.*"# => |_| Token::Comment, r#";.*"# => |_| Token::Comment,
r"[ \t\n\f]+" => |_| Token::Skip r"[ \t\n\f]+" => |_| Token::Skip
@ -141,7 +141,9 @@ impl Iterator for Lexer {
self.pos += len; self.pos += len;
match LEXER_FNS[i](tok_str.to_string()) { match LEXER_FNS[i](tok_str.to_string()) {
Token::Skip => {} Token::Skip => {}
token => return Some(Ok((old_pos, token, self.pos))), token => {
return Some(Ok((old_pos, token, self.pos)));
}
} }
} }
} }

View file

@ -33,7 +33,7 @@ pub Ast: Ast = {
<l:@L> <expr:SimplExpr> <r:@R> => Ast::SimplExpr(Span(l, r, file_id), expr), <l:@L> <expr:SimplExpr> <r:@R> => Ast::SimplExpr(Span(l, r, file_id), expr),
<x:Keyword> => x, <x:Keyword> => x,
<x:Symbol> => x, <x:Symbol> => x,
<l:@L> <x:Value> <r:@R> => Ast::Value(Span(l, r, file_id), x), <l:@L> <x:Value> <r:@R> => Ast::Value(Span(l, r, file_id), x.into()),
<l:@L> "comment" <r:@R> => Ast::Comment(Span(l, r, file_id)), <l:@L> "comment" <r:@R> => Ast::Comment(Span(l, r, file_id)),
}; };
@ -55,8 +55,8 @@ StrLit: String = {
SimplExpr: SimplExpr = { SimplExpr: SimplExpr = {
<l:@L> <x:"simplexpr"> =>? { <l:@L> <x:"simplexpr"> =>? {
let expr = x[1..x.len() - 1].to_string(); let expr = x[1..x.len() - 1].to_string();
simplexpr::parse_string(&expr).map_err(|e| { simplexpr::parse_string(file_id, &expr).map_err(|e| {
let span = e.get_span().map(|simplexpr::Span(simpl_l, simpl_r)| Span(1 + l + simpl_l, 1 + l + simpl_r, file_id)); let span = e.get_span().map(|simplexpr::Span(simpl_l, simpl_r, file_id)| Span(1 + l + simpl_l, 1 + l + simpl_r, file_id));
ParseError::User { error: parse_error::ParseError::SimplExpr(span, e) }}) ParseError::User { error: parse_error::ParseError::SimplExpr(span, e) }})
} }
} }

View file

@ -0,0 +1,17 @@
---
source: src/parser/element.rs
expression: "Element::<Ast, Ast>::from_ast(parser.parse(0, lexer).unwrap()).unwrap()"
---
Element {
name: "box",
attrs: {
":bar": "12",
":baz": "hi",
},
children: [
foo,
(bar),
],
span: 0..33,
}

View file

@ -0,0 +1,8 @@
---
source: src/parser/mod.rs
expression: "p.parse(0, Lexer::new(0, \"1\".to_string()))"
---
Ok(
"1",
)