diff --git a/Cargo.lock b/Cargo.lock index db86e43b..fa7712ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,11 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "autocfg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "backtrace" version = "0.3.9" @@ -336,6 +341,36 @@ name = "nodrop" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nom" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-rational" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "owning_ref" version = "0.4.0" @@ -408,6 +443,32 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_core" version = "0.2.2" @@ -421,6 +482,60 @@ name = "rand_core" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "redox_syscall" version = "0.1.43" @@ -465,6 +580,18 @@ dependencies = [ "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rsass" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rusqlite" version = "0.14.0" @@ -657,6 +784,7 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rsass 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", @@ -756,6 +884,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" "checksum arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f405cc4c21cd8b784f6c8fc2adf9bc00f59558f0049b5ec21517f875963040cc" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" @@ -793,6 +922,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9c349f68f25f596b9f44cf0e7c69752a5c633b0550c3ff849518bfba0233774a" +"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" +"checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" +"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" "checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" @@ -801,13 +934,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" "checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" +"checksum rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3906503e80ac6cbcacb2c2973fa8e473f24d7e2747c8c92bb230c2441cad96b5" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46fbd5550acf75b0c2730f5dd1873751daf9beb8f11b44027778fae50d7feca" +"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "679da7508e9a6390aeaf7fbd02a800fdc64b73fe2204dd2c8ae66d22d9d5ad5d" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "214a97e49be64fd2c86f568dd0cb2c757d2cc53de95b273b6ad0a1c908482f26" "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1" +"checksum rsass 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7a5dde55023a6c19470f7aeb59f75f897d8b80cbe00d61dfcaf7bbbe3de4c0a6" "checksum rusqlite 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9d9118f1ce84d8d0b67f9779936432fb42bb620cef2122409d786892cce9a3c" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a2f546c4..2eabd88f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -23,6 +23,7 @@ serde = "1.0" serde_derive = "1.0" regex-syntax = "0.6.4" regex = "1" +rsass = "0.9" [dependencies.tree-sitter] path = "../lib" diff --git a/cli/src/error.rs b/cli/src/error.rs index 1b8b1a79..4769b481 100644 --- a/cli/src/error.rs +++ b/cli/src/error.rs @@ -31,6 +31,12 @@ impl From for Error { } } +impl From for Error { + fn from(error: rsass::Error) -> Self { + Error(error.to_string()) + } +} + impl From for Error { fn from(error: String) -> Self { Error(error) diff --git a/cli/src/generate/mod.rs b/cli/src/generate/mod.rs index 366d2495..0899d793 100644 --- a/cli/src/generate/mod.rs +++ b/cli/src/generate/mod.rs @@ -13,6 +13,7 @@ mod grammars; mod nfa; mod parse_grammar; mod prepare_grammar; +mod properties; mod render; mod rules; mod tables; @@ -21,30 +22,34 @@ pub fn generate_parser_for_grammar( repo_path: &PathBuf, minimize: bool, state_ids_to_log: Vec, + properties_only: bool, ) -> Result<()> { - let grammar_json = load_js_grammar_file(&repo_path.join("grammar.js")); - let input_grammar = parse_grammar(&grammar_json)?; - let (syntax_grammar, lexical_grammar, inlines, simple_aliases) = - prepare_grammar(&input_grammar)?; - let (parse_table, main_lex_table, keyword_lex_table, keyword_capture_token) = build_tables( - &syntax_grammar, - &lexical_grammar, - &simple_aliases, - &inlines, - minimize, - state_ids_to_log, - )?; - let c_code = render_c_code( - &input_grammar.name, - parse_table, - main_lex_table, - keyword_lex_table, - keyword_capture_token, - syntax_grammar, - lexical_grammar, - simple_aliases, - ); - fs::write(repo_path.join("src").join("parser.c"), c_code)?; + if !properties_only { + let grammar_json = load_js_grammar_file(&repo_path.join("grammar.js")); + let input_grammar = parse_grammar(&grammar_json)?; + let (syntax_grammar, lexical_grammar, inlines, simple_aliases) = + prepare_grammar(&input_grammar)?; + let (parse_table, main_lex_table, keyword_lex_table, keyword_capture_token) = build_tables( + &syntax_grammar, + &lexical_grammar, + &simple_aliases, + &inlines, + minimize, + state_ids_to_log, + )?; + let c_code = render_c_code( + &input_grammar.name, + parse_table, + main_lex_table, + keyword_lex_table, + keyword_capture_token, + syntax_grammar, + lexical_grammar, + simple_aliases, + ); + fs::write(repo_path.join("src").join("parser.c"), c_code)?; + } + properties::generate_property_sheets(repo_path)?; Ok(()) } diff --git a/cli/src/generate/properties.rs b/cli/src/generate/properties.rs new file mode 100644 index 00000000..c328526f --- /dev/null +++ b/cli/src/generate/properties.rs @@ -0,0 +1,327 @@ +use crate::error::{Error, Result}; +use rsass; +use rsass::sass::Value; +use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; +use std::fmt; +use std::fmt::Write; +use std::fs::{self, File}; +use std::hash::{Hash, Hasher}; +use std::path::{Path, PathBuf}; +use tree_sitter::{self, PropertyStateJSON, PropertyTransitionJSON}; + +#[derive(Debug, PartialEq, Eq, Hash, Serialize)] +#[serde(untagged)] +enum PropertyValue { + String(String), + Object(PropertySet), + Array(Vec), +} + +type PropertySet = BTreeMap; +type PropertySheetJSON = tree_sitter::PropertySheetJSON; +type StateId = u32; +type PropertySetId = u32; + +#[derive(Clone, PartialEq, Eq)] +struct SelectorStep { + kind: String, + is_named: bool, + is_immediate: bool, + child_index: Option, + text_pattern: Option, +} + +#[derive(PartialEq, Eq)] +struct Selector(Vec); + +#[derive(Debug, PartialEq, Eq)] +struct Rule { + selectors: Vec, + properties: PropertySet, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct PropertyItem { + rule_id: u32, + selector_id: u32, + step_id: u32, +} + +#[derive(PartialEq, Eq)] +struct PropertyItemSet(BTreeSet); + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +struct SelectorMatch { + specificity: u32, + rule_id: u32, + selector_id: u32, +} + +struct Builder { + rules: Vec, + output: PropertySheetJSON, + ids_by_item_set: HashMap, + ids_by_property_set: HashMap, + item_set_queue: VecDeque<(PropertyItemSet, StateId)>, +} + +impl Builder { + fn new(rules: Vec) -> Self { + Builder { + rules, + output: PropertySheetJSON { + states: Vec::new(), + property_sets: Vec::new(), + }, + ids_by_item_set: HashMap::new(), + ids_by_property_set: HashMap::new(), + item_set_queue: VecDeque::new(), + } + } + + fn build(self) -> PropertySheetJSON { + let mut start_item_set = PropertyItemSet(BTreeSet::new()); + + self.output + } +} + +impl Hash for PropertyItemSet { + fn hash(&self, h: &mut H) { + h.write_usize(self.0.len()); + for entry in &self.0 { + entry.hash(h); + } + } +} + +impl fmt::Debug for SelectorStep { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "(")?; + if self.is_named { + write!(f, "{}", self.kind)?; + } else { + write!(f, "\"{}\"", self.kind)?; + } + if let Some(n) = self.child_index { + write!(f, ":nth-child({})", n)?; + } + if let Some(t) = &self.text_pattern { + write!(f, "[text='{}']", t)?; + } + write!(f, ")")?; + Ok(()) + } +} + +impl fmt::Debug for Selector { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "[")?; + for (i, step) in self.0.iter().enumerate() { + if step.is_immediate { + write!(f, " > ")?; + } else if i > 0 { + write!(f, " ")?; + } + write!(f, "{:?}", step)?; + } + write!(f, "]")?; + Ok(()) + } +} + +pub fn generate_property_sheets(repo_path: &Path) -> Result<()> { + let src_dir_path = repo_path.join("src"); + let properties_dir_path = repo_path.join("properties"); + + for entry in fs::read_dir(properties_dir_path)? { + let property_sheet_css_path = entry?.path(); + let rules = parse_property_sheet(&property_sheet_css_path)?; + + for rule in &rules { + eprintln!("rule: {:?}", rule); + } + + let sheet = Builder::new(rules).build(); + let property_sheet_json_path = src_dir_path + .join(property_sheet_css_path.file_name().unwrap()) + .with_extension("json"); + let mut property_sheet_json_file = File::create(property_sheet_json_path)?; + serde_json::to_writer_pretty(&mut property_sheet_json_file, &sheet)?; + } + + Ok(()) +} + +fn parse_property_sheet(path: &Path) -> Result> { + let mut i = 0; + let mut items = rsass::parse_scss_file(path)?; + while i < items.len() { + match &items[i] { + rsass::Item::Import(arg) => { + if let Some(s) = get_sass_string(arg) { + let import_path = resolve_path(path, s)?; + let imported_items = rsass::parse_scss_file(&import_path)?; + items.splice(i..(i + 1), imported_items); + continue; + } else { + return Err(Error("@import arguments must be strings".to_string())); + } + } + rsass::Item::AtRule { name, args, .. } => match name.as_str() { + "schema" => { + if let Some(s) = get_sass_string(args) { + let schema_path = resolve_path(path, s)?; + eprintln!("schema path: {:?}", schema_path); + items.remove(i); + continue; + } else { + return Err(Error("@schema arguments must be strings".to_string())); + } + } + _ => return Err(Error(format!("Unsupported at-rule '{}'", name))), + }, + _ => {} + } + i += 1; + } + + let mut result = Vec::new(); + let selector_prefixes = vec![Vec::new()]; + parse_sass_items(items, &selector_prefixes, &mut result)?; + Ok(result) +} + +fn parse_sass_items( + items: Vec, + selector_prefixes: &Vec>, + result: &mut Vec, +) -> Result<()> { + let mut properties = PropertySet::new(); + for item in items { + match item { + rsass::Item::None | rsass::Item::Comment(_) => {} + rsass::Item::Property(name, value) => { + properties.insert(name.to_string(), parse_sass_value(&value)?); + } + rsass::Item::Rule(selectors, items) => { + let mut full_selectors = Vec::new(); + for prefix in selector_prefixes { + let mut part_string = String::new(); + let mut next_step_is_immediate = false; + for selector in &selectors.s { + let mut prefix = prefix.clone(); + for part in &selector.0 { + part_string.clear(); + write!(&mut part_string, "{}", part).unwrap(); + let part_string = part_string.trim(); + if !part_string.is_empty() { + if part_string == "&" { + continue; + } else if part_string.starts_with("[text=") { + if let Some(last_step) = prefix.last_mut() { + last_step.text_pattern = Some( + part_string[7..(part_string.len() - 2)].to_string(), + ) + } + } else if part_string == ">" { + next_step_is_immediate = true; + } else if part_string.starts_with("[token=") { + prefix.push(SelectorStep { + kind: part_string[8..(part_string.len() - 2)].to_string(), + is_named: false, + child_index: None, + text_pattern: None, + is_immediate: next_step_is_immediate, + }); + next_step_is_immediate = false; + } else { + prefix.push(SelectorStep { + kind: part_string.to_string(), + is_named: true, + child_index: None, + text_pattern: None, + is_immediate: next_step_is_immediate, + }); + next_step_is_immediate = false; + } + } + } + full_selectors.push(prefix); + } + } + parse_sass_items(items, &full_selectors, result)?; + } + _ => return Err(Error(format!("Unsupported syntax type {:?}", item))), + } + } + + if !properties.is_empty() { + result.push(Rule { + selectors: selector_prefixes.iter().cloned().map(Selector).collect(), + properties, + }); + } + + Ok(()) +} + +fn parse_sass_value(value: &Value) -> Result { + match value { + Value::Literal(s) => { + if let Some(s) = s.single_raw() { + Ok(PropertyValue::String(s.to_string())) + } else { + Err(Error("String interpolation is not supported".to_string())) + } + } + Value::Call(name, raw_args) => { + if let Some(name) = name.single_raw() { + let mut args = Vec::new(); + for (_, arg) in raw_args.iter() { + args.push(parse_sass_value(arg)?); + } + let mut result = PropertySet::new(); + result.insert("name".to_string(), PropertyValue::String(name.to_string())); + result.insert("args".to_string(), PropertyValue::Array(args)); + Ok(PropertyValue::Object(result)) + } else { + Err(Error("String interpolation is not supported".to_string())) + } + } + Value::List(elements, ..) => { + let mut result = Vec::new(); + for element in elements { + result.push(parse_sass_value(element)?); + } + Ok(PropertyValue::Array(result)) + } + Value::True => Ok(PropertyValue::String("true".to_string())), + Value::False => Ok(PropertyValue::String("false".to_string())), + _ => Err(Error( + "Property values must be strings or function calls".to_string(), + )), + } +} + +fn get_sass_string(value: &Value) -> Option<&str> { + if let Value::Literal(s) = value { + s.single_raw() + } else { + None + } +} + +fn resolve_path(base: &Path, path: impl AsRef) -> Result { + let mut result = base.to_owned(); + result.pop(); + result.push(path.as_ref()); + if result.exists() { + Ok(result) + } else { + Err(Error(format!( + "Could not resolve import path {:?}", + path.as_ref() + ))) + } +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 604d3068..334f06ef 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -6,6 +6,7 @@ extern crate log; extern crate serde_derive; extern crate hashbrown; extern crate regex; +extern crate rsass; extern crate serde_json; mod error; @@ -39,6 +40,7 @@ fn run() -> error::Result<()> { SubCommand::with_name("generate") .about("Generate a parser") .arg(Arg::with_name("log").long("log")) + .arg(Arg::with_name("properties-only").long("properties")) .arg( Arg::with_name("state-ids-to-log") .long("log-state") @@ -77,13 +79,14 @@ fn run() -> error::Result<()> { } let minimize = !matches.is_present("no-minimize"); + let properties_only = matches.is_present("properties-only"); let state_ids_to_log = matches .values_of("state-ids-to-log") .map_or(Vec::new(), |ids| { ids.filter_map(|id| usize::from_str_radix(id, 10).ok()) .collect() }); - generate::generate_parser_for_grammar(¤t_dir, minimize, state_ids_to_log)?; + generate::generate_parser_for_grammar(¤t_dir, minimize, state_ids_to_log, properties_only)?; } else if let Some(matches) = matches.subcommand_matches("test") { let debug = matches.is_present("debug"); let debug_graph = matches.is_present("debug-graph"); diff --git a/lib/binding/lib.rs b/lib/binding/lib.rs index ae3c979c..37748447 100644 --- a/lib/binding/lib.rs +++ b/lib/binding/lib.rs @@ -80,6 +80,29 @@ pub struct PropertySheet

> { text_regexes: Vec, } +#[derive(Debug, Deserialize, Serialize)] +pub struct PropertyTransitionJSON { + #[serde(rename = "type")] + pub kind: String, + pub named: bool, + pub index: Option, + pub text: Option, + pub state_id: usize, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct PropertyStateJSON { + pub transitions: Vec, + pub property_set_id: usize, + pub default_next_state_id: usize, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct PropertySheetJSON

{ + pub states: Vec, + pub property_sets: Vec

, +} + #[derive(Clone, Copy)] pub struct Node<'a>(ffi::TSNode, PhantomData<&'a ()>); @@ -189,7 +212,7 @@ impl Parser { } #[cfg(unix)] - pub fn print_dot_graphs(&mut self, file: & impl AsRawFd) { + pub fn print_dot_graphs(&mut self, file: &impl AsRawFd) { let fd = file.as_raw_fd(); unsafe { ffi::ts_parser_print_dot_graphs(self.0, ffi::dup(fd)) } } @@ -754,29 +777,6 @@ impl

PropertySheet

{ where P: DeserializeOwned, { - #[derive(Deserialize, Debug)] - struct PropertyTransitionJSON { - #[serde(rename = "type")] - kind: String, - named: bool, - index: Option, - text: Option, - state_id: usize, - } - - #[derive(Deserialize, Debug)] - struct PropertyStateJSON { - transitions: Vec, - property_set_id: usize, - default_next_state_id: usize, - } - - #[derive(Deserialize, Debug)] - struct PropertySheetJSON

{ - states: Vec, - property_sets: Vec

, - } - let input: PropertySheetJSON

= serde_json::from_str(json).map_err(PropertySheetError::InvalidJSON)?; let mut states = Vec::new();