Fully include simplexpr
This commit is contained in:
parent
5899489250
commit
8405d01303
12 changed files with 412 additions and 27 deletions
172
Cargo.lock
generated
172
Cargo.lock
generated
|
@ -11,6 +11,15 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
|
@ -128,6 +137,12 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.4"
|
||||
|
@ -145,6 +160,29 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.12"
|
||||
|
@ -194,13 +232,20 @@ name = "eww_config"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"codespan-reporting",
|
||||
"derive_more",
|
||||
"insta",
|
||||
"itertools",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"lazy_static",
|
||||
"logos",
|
||||
"maplit",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"simplexpr",
|
||||
"smart-default",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
@ -233,6 +278,15 @@ version = "0.9.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.18"
|
||||
|
@ -383,6 +437,24 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "output_vt100"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
dependencies = [
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.5.1"
|
||||
|
@ -414,6 +486,18 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"ctor",
|
||||
"diff",
|
||||
"output_vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.27"
|
||||
|
@ -478,12 +562,39 @@ dependencies = [
|
|||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||
dependencies = [
|
||||
"pest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.126"
|
||||
|
@ -533,12 +644,40 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec"
|
||||
|
||||
[[package]]
|
||||
name = "simplexpr"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"codespan-reporting",
|
||||
"itertools",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"logos",
|
||||
"maplit",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27"
|
||||
|
||||
[[package]]
|
||||
name = "smart-default"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.1"
|
||||
|
@ -551,6 +690,27 @@ dependencies = [
|
|||
"precomputed-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.73"
|
||||
|
@ -621,6 +781,18 @@ dependencies = [
|
|||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -17,6 +17,16 @@ maplit = "1.0"
|
|||
codespan-reporting = "0.11"
|
||||
logos = "0.12"
|
||||
|
||||
derive_more = "0.99"
|
||||
smart-default = "0.6"
|
||||
serde = {version = "1.0", features = ["derive"]}
|
||||
serde_json = "1.0"
|
||||
lazy_static = "1.4"
|
||||
pretty_assertions = "0.7"
|
||||
|
||||
|
||||
simplexpr = { path = "../../projects/simplexpr" }
|
||||
|
||||
[build-dependencies]
|
||||
lalrpop = "0.19.5"
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use eww_config::{ast::*, config::*};
|
|||
fn main() {
|
||||
let mut files = codespan_reporting::files::SimpleFiles::new();
|
||||
|
||||
let input = "(12 :bar 22 (foo) (baz))";
|
||||
let input = r#"(hi :bar 22 :baz {"hi" asdfasdf * 2} (foo) (baz))"#;
|
||||
|
||||
let file_id = files.add("foo.eww", input);
|
||||
let ast = eww_config::parse_string(file_id, input);
|
||||
|
|
14
src/ast.rs
14
src/ast.rs
|
@ -1,4 +1,5 @@
|
|||
use itertools::Itertools;
|
||||
use simplexpr::ast::SimplExpr;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{config::FromAst, error::*};
|
||||
|
@ -22,9 +23,11 @@ impl std::fmt::Debug for Span {
|
|||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum AstType {
|
||||
List,
|
||||
Array,
|
||||
Keyword,
|
||||
Symbol,
|
||||
Value,
|
||||
SimplExpr,
|
||||
Comment,
|
||||
}
|
||||
|
||||
|
@ -37,10 +40,11 @@ impl Display for AstType {
|
|||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub enum Ast {
|
||||
List(Span, Vec<Ast>),
|
||||
// ArgList(Span, Vec<Ast>),
|
||||
Array(Span, Vec<Ast>),
|
||||
Keyword(Span, String),
|
||||
Symbol(Span, String),
|
||||
Value(Span, String),
|
||||
SimplExpr(Span, SimplExpr),
|
||||
Comment(Span),
|
||||
}
|
||||
|
||||
|
@ -74,9 +78,11 @@ impl Ast {
|
|||
pub fn expr_type(&self) -> AstType {
|
||||
match self {
|
||||
Ast::List(..) => AstType::List,
|
||||
Ast::Array(..) => AstType::Array,
|
||||
Ast::Keyword(..) => AstType::Keyword,
|
||||
Ast::Symbol(..) => AstType::Symbol,
|
||||
Ast::Value(..) => AstType::Value,
|
||||
Ast::SimplExpr(..) => AstType::SimplExpr,
|
||||
Ast::Comment(_) => AstType::Comment,
|
||||
}
|
||||
}
|
||||
|
@ -84,9 +90,11 @@ impl Ast {
|
|||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Ast::List(span, _) => *span,
|
||||
Ast::Array(span, _) => *span,
|
||||
Ast::Keyword(span, _) => *span,
|
||||
Ast::Symbol(span, _) => *span,
|
||||
Ast::Value(span, _) => *span,
|
||||
Ast::SimplExpr(span, _) => *span,
|
||||
Ast::Comment(span) => *span,
|
||||
}
|
||||
}
|
||||
|
@ -104,9 +112,11 @@ impl std::fmt::Display for Ast {
|
|||
use Ast::*;
|
||||
match self {
|
||||
List(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")),
|
||||
Array(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")),
|
||||
Keyword(_, x) => write!(f, "{}", x),
|
||||
Symbol(_, x) => write!(f, "{}", x),
|
||||
Value(_, x) => write!(f, "{}", x),
|
||||
SimplExpr(_, x) => write!(f, "{{{}}}", x),
|
||||
Comment(_) => write!(f, ""),
|
||||
}
|
||||
}
|
||||
|
@ -116,9 +126,11 @@ impl std::fmt::Debug for Ast {
|
|||
use Ast::*;
|
||||
match self {
|
||||
List(span, x) => f.debug_tuple(&format!("List<{}>", span)).field(x).finish(),
|
||||
Array(span, x) => f.debug_tuple(&format!("Array<{}>", span)).field(x).finish(),
|
||||
Keyword(span, x) => write!(f, "Number<{}>({})", span, x),
|
||||
Symbol(span, x) => write!(f, "Symbol<{}>({})", span, x),
|
||||
Value(span, x) => write!(f, "Value<{}>({})", span, x),
|
||||
SimplExpr(span, x) => write!(f, "SimplExpr<{}>({})", span, x),
|
||||
Comment(span) => write!(f, "Comment<{}>", span),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ mod test {
|
|||
fn test() {
|
||||
let parser = parser::AstParser::new();
|
||||
insta::with_settings!({sort_maps => true}, {
|
||||
let lexer = lexer::Lexer::new("(box :bar 12 :baz \"hi\" foo (bar))");
|
||||
let lexer = lexer::Lexer::new(0, "(box :bar 12 :baz \"hi\" foo (bar))");
|
||||
insta::assert_debug_snapshot!(
|
||||
Element::<Ast, Ast>::from_ast(parser.parse(0, lexer).unwrap()).unwrap()
|
||||
);
|
||||
|
|
18
src/error.rs
18
src/error.rs
|
@ -1,13 +1,13 @@
|
|||
use crate::{
|
||||
ast::{Ast, AstType, Span},
|
||||
lexer,
|
||||
lexer, parse_error,
|
||||
};
|
||||
use codespan_reporting::{diagnostic, files};
|
||||
use thiserror::Error;
|
||||
|
||||
pub type AstResult<T> = Result<T, AstError>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Error)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum AstError {
|
||||
#[error("Definition invalid")]
|
||||
InvalidDefinition(Option<Span>),
|
||||
|
@ -17,7 +17,7 @@ pub enum AstError {
|
|||
WrongExprType(Option<Span>, AstType, AstType),
|
||||
|
||||
#[error("Parse error: {source}")]
|
||||
ParseError { file_id: Option<usize>, source: lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError> },
|
||||
ParseError { file_id: Option<usize>, source: lalrpop_util::ParseError<usize, lexer::Token, parse_error::ParseError> },
|
||||
}
|
||||
|
||||
impl AstError {
|
||||
|
@ -39,21 +39,27 @@ impl AstError {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_parse_error(file_id: usize, err: lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError>) -> AstError {
|
||||
pub fn from_parse_error(
|
||||
file_id: usize,
|
||||
err: lalrpop_util::ParseError<usize, lexer::Token, parse_error::ParseError>,
|
||||
) -> AstError {
|
||||
AstError::ParseError { file_id: Some(file_id), source: err }
|
||||
}
|
||||
}
|
||||
|
||||
fn get_parse_error_span(
|
||||
file_id: usize,
|
||||
err: &lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError>,
|
||||
err: &lalrpop_util::ParseError<usize, lexer::Token, parse_error::ParseError>,
|
||||
) -> Option<Span> {
|
||||
match err {
|
||||
lalrpop_util::ParseError::InvalidToken { location } => Some(Span(*location, *location, file_id)),
|
||||
lalrpop_util::ParseError::UnrecognizedEOF { location, expected } => Some(Span(*location, *location, file_id)),
|
||||
lalrpop_util::ParseError::UnrecognizedToken { token, expected } => Some(Span(token.0, token.2, file_id)),
|
||||
lalrpop_util::ParseError::ExtraToken { token } => Some(Span(token.0, token.2, file_id)),
|
||||
lalrpop_util::ParseError::User { error } => None,
|
||||
lalrpop_util::ParseError::User { error } => match error {
|
||||
parse_error::ParseError::SimplExpr(span, error) => *span,
|
||||
parse_error::ParseError::LexicalError(span) => Some(*span),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
31
src/lexer.rs
31
src/lexer.rs
|
@ -1,12 +1,17 @@
|
|||
use logos::Logos;
|
||||
|
||||
use crate::{ast::Span, parse_error};
|
||||
|
||||
#[derive(Logos, Debug, PartialEq, Eq, Clone)]
|
||||
pub enum Token {
|
||||
#[token("(")]
|
||||
LPren,
|
||||
|
||||
#[token(")")]
|
||||
RPren,
|
||||
#[token("[")]
|
||||
LBrack,
|
||||
#[token("]")]
|
||||
RBrack,
|
||||
|
||||
#[token("true")]
|
||||
True,
|
||||
|
@ -26,6 +31,9 @@ pub enum Token {
|
|||
#[regex(r#":\S+"#, |x| x.slice().to_string())]
|
||||
Keyword(String),
|
||||
|
||||
#[regex(r#"\{[^}]*\}"#, |x| x.slice().to_string())]
|
||||
SimplExpr(String),
|
||||
|
||||
#[regex(r#";.*"#)]
|
||||
Comment,
|
||||
|
||||
|
@ -39,46 +47,41 @@ impl std::fmt::Display for Token {
|
|||
match self {
|
||||
Token::LPren => write!(f, "'('"),
|
||||
Token::RPren => write!(f, "')'"),
|
||||
Token::LBrack => write!(f, "'['"),
|
||||
Token::RBrack => write!(f, "']'"),
|
||||
Token::True => write!(f, "true"),
|
||||
Token::False => write!(f, "false"),
|
||||
Token::StrLit(x) => write!(f, "\"{}\"", x),
|
||||
Token::NumLit(x) => write!(f, "{}", x),
|
||||
Token::Symbol(x) => write!(f, "{}", x),
|
||||
Token::Keyword(x) => write!(f, "{}", x),
|
||||
Token::SimplExpr(x) => write!(f, "{{{}}}", x),
|
||||
Token::Comment => write!(f, ""),
|
||||
Token::Error => write!(f, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
pub struct LexicalError(usize, usize);
|
||||
|
||||
impl std::fmt::Display for LexicalError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Lexical error at {}..{}", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
pub type SpannedResult<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>;
|
||||
|
||||
pub struct Lexer<'input> {
|
||||
file_id: usize,
|
||||
lexer: logos::SpannedIter<'input, Token>,
|
||||
}
|
||||
|
||||
impl<'input> Lexer<'input> {
|
||||
pub fn new(text: &'input str) -> Self {
|
||||
Lexer { lexer: logos::Lexer::new(text).spanned() }
|
||||
pub fn new(file_id: usize, text: &'input str) -> Self {
|
||||
Lexer { file_id, lexer: logos::Lexer::new(text).spanned() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'input> Iterator for Lexer<'input> {
|
||||
type Item = SpannedResult<Token, usize, LexicalError>;
|
||||
type Item = SpannedResult<Token, usize, parse_error::ParseError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (token, range) = self.lexer.next()?;
|
||||
if token == Token::Error {
|
||||
Some(Err(LexicalError(range.start, range.end)))
|
||||
Some(Err(parse_error::ParseError::LexicalError(Span(range.start, range.end, self.file_id))))
|
||||
} else {
|
||||
Some(Ok((range.start, token, range.end)))
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ pub mod ast;
|
|||
pub mod config;
|
||||
pub mod error;
|
||||
mod lexer;
|
||||
mod parse_error;
|
||||
pub mod value;
|
||||
use ast::Ast;
|
||||
use error::{AstError, AstResult};
|
||||
|
||||
|
@ -18,7 +20,7 @@ use lalrpop_util::lalrpop_mod;
|
|||
lalrpop_mod!(pub parser);
|
||||
|
||||
pub fn parse_string(file_id: usize, s: &str) -> AstResult<Ast> {
|
||||
let lexer = lexer::Lexer::new(s);
|
||||
let lexer = lexer::Lexer::new(file_id, s);
|
||||
let parser = parser::AstParser::new();
|
||||
Ok(parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e))?)
|
||||
}
|
||||
|
@ -30,7 +32,7 @@ macro_rules! test_parser {
|
|||
|
||||
::insta::with_settings!({sort_maps => true}, {
|
||||
$(
|
||||
::insta::assert_debug_snapshot!(p.parse(0, Lexer::new($text)));
|
||||
::insta::assert_debug_snapshot!(p.parse(0, Lexer::new(0, $text)));
|
||||
)*
|
||||
});
|
||||
}}
|
||||
|
|
10
src/parse_error.rs
Normal file
10
src/parse_error.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use crate::ast::Span;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ParseError {
|
||||
#[error("{1}")]
|
||||
SimplExpr(Option<Span>, simplexpr::error::Error),
|
||||
|
||||
#[error("Unknown token")]
|
||||
LexicalError(Span),
|
||||
}
|
|
@ -1,22 +1,28 @@
|
|||
use std::str::FromStr;
|
||||
use crate::lexer::{Token, LexicalError};
|
||||
use crate::lexer::{Token};
|
||||
use crate::ast::{Ast, Span};
|
||||
use simplexpr::ast::SimplExpr;
|
||||
use simplexpr;
|
||||
use lalrpop_util::ParseError;
|
||||
|
||||
grammar(file_id: usize);
|
||||
|
||||
extern {
|
||||
type Location = usize;
|
||||
type Error = LexicalError;
|
||||
type Error = crate::parse_error::ParseError;
|
||||
|
||||
enum Token {
|
||||
"(" => Token::LPren,
|
||||
")" => Token::RPren,
|
||||
"[" => Token::LBrack,
|
||||
"]" => Token::RBrack,
|
||||
"true" => Token::True,
|
||||
"false" => Token::False,
|
||||
"string" => Token::StrLit(<String>),
|
||||
"number" => Token::NumLit(<String>),
|
||||
"symbol" => Token::Symbol(<String>),
|
||||
"keyword" => Token::Keyword(<String>),
|
||||
"simplexpr" => Token::SimplExpr(<String>),
|
||||
"comment" => Token::Comment,
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +30,8 @@ extern {
|
|||
|
||||
pub Ast: Ast = {
|
||||
<l:@L> "(" <elems:(<Ast>)+> ")" <r:@R> => Ast::List(Span(l, r, file_id), elems),
|
||||
<l:@L> "[" <elems:(<Ast>)+> "]" <r:@R> => Ast::Array(Span(l, r, file_id), elems),
|
||||
<l:@L> <expr:SimplExpr> <r:@R> => Ast::SimplExpr(Span(l, r, file_id), expr),
|
||||
<x:Keyword> => x,
|
||||
<x:Symbol> => x,
|
||||
<l:@L> <x:Value> <r:@R> => Ast::Value(Span(l, r, file_id), x),
|
||||
|
@ -45,6 +53,15 @@ StrLit: String = {
|
|||
},
|
||||
};
|
||||
|
||||
SimplExpr: SimplExpr = {
|
||||
<l:@L> <x:"simplexpr"> =>? {
|
||||
let expr = x[1..x.len() - 1].to_string();
|
||||
simplexpr::parse_string(&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));
|
||||
ParseError::User { error: crate::parse_error::ParseError::SimplExpr(span, e) }})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Num: String = <"number"> => <>.to_string();
|
||||
Bool: String = {
|
||||
|
|
112
src/value/coords.rs
Normal file
112
src/value/coords.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
use derive_more::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smart_default::SmartDefault;
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Failed to parse \"{0}\" as a length value")]
|
||||
NumParseFailed(String),
|
||||
#[error("Inalid unit \"{0}\", must be either % or px")]
|
||||
InvalidUnit(String),
|
||||
#[error("Invalid format. Coordinates must be formated like 200x100")]
|
||||
MalformedCoords,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Display, DebugCustom, SmartDefault)]
|
||||
pub enum NumWithUnit {
|
||||
#[display(fmt = "{}%", .0)]
|
||||
#[debug(fmt = "{}%", .0)]
|
||||
Percent(i32),
|
||||
#[display(fmt = "{}px", .0)]
|
||||
#[debug(fmt = "{}px", .0)]
|
||||
#[default]
|
||||
Pixels(i32),
|
||||
}
|
||||
|
||||
impl NumWithUnit {
|
||||
pub fn relative_to(&self, max: i32) -> i32 {
|
||||
match *self {
|
||||
NumWithUnit::Percent(n) => ((max as f64 / 100.0) * n as f64) as i32,
|
||||
NumWithUnit::Pixels(n) => n,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for NumWithUnit {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
lazy_static::lazy_static! {
|
||||
static ref PATTERN: regex::Regex = regex::Regex::new("^(-?\\d+)(.*)$").unwrap();
|
||||
};
|
||||
|
||||
let captures = PATTERN.captures(s).ok_or_else(|| Error::NumParseFailed(s.to_string()))?;
|
||||
let value = captures.get(1).unwrap().as_str().parse::<i32>().map_err(|_| Error::NumParseFailed(s.to_string()))?;
|
||||
match captures.get(2).unwrap().as_str() {
|
||||
"px" | "" => Ok(NumWithUnit::Pixels(value)),
|
||||
"%" => Ok(NumWithUnit::Percent(value)),
|
||||
unit => Err(Error::InvalidUnit(unit.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Display, Default)]
|
||||
#[display(fmt = "{}*{}", x, y)]
|
||||
pub struct Coords {
|
||||
pub x: NumWithUnit,
|
||||
pub y: NumWithUnit,
|
||||
}
|
||||
|
||||
impl FromStr for Coords {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (x, y) = s
|
||||
.split_once(|x: char| x.to_ascii_lowercase() == 'x' || x.to_ascii_lowercase() == '*')
|
||||
.ok_or_else(|| Error::MalformedCoords)?;
|
||||
Coords::from_strs(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Coords {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "CoordsWithUnits({}, {})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Coords {
|
||||
pub fn from_pixels(x: i32, y: i32) -> Self {
|
||||
Coords { x: NumWithUnit::Pixels(x), y: NumWithUnit::Pixels(y) }
|
||||
}
|
||||
|
||||
/// parse a string for x and a string for y into a [`Coords`] object.
|
||||
pub fn from_strs(x: &str, y: &str) -> Result<Coords, Error> {
|
||||
Ok(Coords { x: x.parse()?, y: y.parse()? })
|
||||
}
|
||||
|
||||
/// resolve the possibly relative coordinates relative to a given containers size
|
||||
pub fn relative_to(&self, width: i32, height: i32) -> (i32, i32) {
|
||||
(self.x.relative_to(width), self.y.relative_to(height))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_parse_num_with_unit() {
|
||||
assert_eq!(NumWithUnit::Pixels(55), NumWithUnit::from_str("55").unwrap());
|
||||
assert_eq!(NumWithUnit::Pixels(55), NumWithUnit::from_str("55px").unwrap());
|
||||
assert_eq!(NumWithUnit::Percent(55), NumWithUnit::from_str("55%").unwrap());
|
||||
assert!(NumWithUnit::from_str("55pp").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_coords() {
|
||||
assert_eq!(Coords { x: NumWithUnit::Pixels(50), y: NumWithUnit::Pixels(60) }, Coords::from_str("50x60").unwrap());
|
||||
assert!(Coords::from_str("5060").is_err());
|
||||
}
|
||||
}
|
41
src/value/mod.rs
Normal file
41
src/value/mod.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use derive_more::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod coords;
|
||||
pub use coords::*;
|
||||
|
||||
/// The name of a variable
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, DebugCustom)]
|
||||
#[debug(fmt = "VarName({})", .0)]
|
||||
pub struct VarName(pub String);
|
||||
|
||||
impl std::borrow::Borrow<str> for VarName {
|
||||
fn borrow(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for VarName {
|
||||
fn from(s: &str) -> Self {
|
||||
VarName(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of an attribute
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, DebugCustom)]
|
||||
#[debug(fmt="AttrName({})", .0)]
|
||||
pub struct AttrName(pub String);
|
||||
|
||||
impl std::borrow::Borrow<str> for AttrName {
|
||||
fn borrow(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for AttrName {
|
||||
fn from(s: &str) -> Self {
|
||||
AttrName(s.to_owned())
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue