add basic config structure parsing and add some validation, mostly to demonstrate
This commit is contained in:
parent
de9d979ce5
commit
bfb7c5a27b
14 changed files with 300 additions and 46 deletions
|
@ -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
40
examples/validation.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod validate;
|
||||||
|
pub mod widget_definition;
|
||||||
|
pub mod widget_use;
|
42
src/config/validate.rs
Normal file
42
src/config/validate.rs
Normal 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(())
|
||||||
|
}
|
44
src/config/widget_definition.rs
Normal file
44
src/config/widget_definition.rs
Normal 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
43
src/config/widget_use.rs
Normal 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 }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
34
src/error.rs
34
src/error.rs
|
@ -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)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) }})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
8
src/parser/snapshots/eww_config__parser__test.snap.new
Normal file
8
src/parser/snapshots/eww_config__parser__test.snap.new
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
source: src/parser/mod.rs
|
||||||
|
expression: "p.parse(0, Lexer::new(0, \"1\".to_string()))"
|
||||||
|
|
||||||
|
---
|
||||||
|
Ok(
|
||||||
|
"1",
|
||||||
|
)
|
Loading…
Add table
Reference in a new issue