diff --git a/.changeset/heavy-hotels-complain.md b/.changeset/heavy-hotels-complain.md new file mode 100644 index 0000000000..deb4ae2e71 --- /dev/null +++ b/.changeset/heavy-hotels-complain.md @@ -0,0 +1,5 @@ +--- +"@nomicfoundation/slang": patch +--- + +refactor CST building and querying utilities into a separate `metaslang_cst` crate. diff --git a/.cspell.json b/.cspell.json index 355197a178..1381d7855e 100644 --- a/.cspell.json +++ b/.cspell.json @@ -13,6 +13,7 @@ "inheritdoc", "instanceof", "ipfs", + "metaslang", "mkdocs", "napi", "nomic", diff --git a/Cargo.lock b/Cargo.lock index 6c3b86fa32..7f8aa6a2eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -384,9 +384,9 @@ dependencies = [ "anyhow", "ariadne", "codegen_runtime_generator", + "metaslang_cst", "napi", "napi-derive", - "nom", "semver", "serde", "serde_json", @@ -417,10 +417,10 @@ name = "codegen_runtime_node_addon" version = "0.14.2" dependencies = [ "ariadne", + "metaslang_cst", "napi", "napi-build", "napi-derive", - "nom", "semver", "serde", "serde_json", @@ -1110,6 +1110,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metaslang_cst" +version = "0.14.2" +dependencies = [ + "nom", + "serde", + "strum", +] + [[package]] name = "mime" version = "0.3.17" @@ -1793,7 +1802,7 @@ dependencies = [ "clap", "codegen_runtime_generator", "infra_utils", - "nom", + "metaslang_cst", "semver", "serde", "serde_json", @@ -1808,10 +1817,10 @@ name = "slang_solidity_node_addon" version = "0.14.2" dependencies = [ "ariadne", + "metaslang_cst", "napi", "napi-build", "napi-derive", - "nom", "semver", "serde", "serde_json", @@ -1828,7 +1837,7 @@ dependencies = [ "ariadne", "codegen_runtime_generator", "infra_utils", - "nom", + "metaslang_cst", "semver", "serde", "strum", @@ -1842,10 +1851,10 @@ name = "slang_testlang_node_addon" version = "0.14.2" dependencies = [ "ariadne", + "metaslang_cst", "napi", "napi-build", "napi-derive", - "nom", "semver", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index c33003c0a6..7193ad9636 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,8 @@ members = [ "crates/infra/cli", "crates/infra/utils", + "crates/metaslang/cst", + "crates/solidity/inputs/language", "crates/solidity/outputs/cargo/slang_solidity_node_addon", "crates/solidity/outputs/cargo/slang_solidity", @@ -60,6 +62,8 @@ codegen_testing = { path = "crates/codegen/testing" } infra_cli = { path = "crates/infra/cli" } infra_utils = { path = "crates/infra/utils" } +metaslang_cst = { path = "crates/metaslang/cst" } + slang_solidity = { path = "crates/solidity/outputs/cargo/slang_solidity" } slang_solidity_node_addon = { path = "crates/solidity/outputs/cargo/slang_solidity_node_addon" } solidity_cargo_tests = { path = "crates/solidity/outputs/cargo/tests" } diff --git a/crates/codegen/language/definition/src/compiler/analysis/mod.rs b/crates/codegen/language/definition/src/compiler/analysis/mod.rs index 4a478ab9a2..564d363832 100644 --- a/crates/codegen/language/definition/src/compiler/analysis/mod.rs +++ b/crates/codegen/language/definition/src/compiler/analysis/mod.rs @@ -1,5 +1,4 @@ mod definitions; -mod queries; mod reachability; mod references; mod utils; @@ -10,7 +9,6 @@ use indexmap::IndexMap; use proc_macro2::Span; use crate::compiler::analysis::definitions::analyze_definitions; -use crate::compiler::analysis::queries::analyze_queries; use crate::compiler::analysis::reachability::analyze_reachability; use crate::compiler::analysis::references::analyze_references; use crate::compiler::version_set::VersionSet; @@ -64,7 +62,6 @@ impl Analysis { } analyze_reachability(&mut analysis); - analyze_queries(&mut analysis); analysis } diff --git a/crates/codegen/language/definition/src/compiler/analysis/queries.rs b/crates/codegen/language/definition/src/compiler/analysis/queries.rs deleted file mode 100644 index ee76fa688a..0000000000 --- a/crates/codegen/language/definition/src/compiler/analysis/queries.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::compiler::analysis::Analysis; - -pub(crate) fn analyze_queries(analysis: &mut Analysis) { - for query in analysis.language.queries.values() { - // TODO(#554): use the real parser to validate against existing kinds/names: - if query.contains("test") { - analysis.errors.add(query, &Errors::TestQuery); - } - } -} - -#[allow(clippy::enum_variant_names)] -#[derive(thiserror::Error, Debug)] -enum Errors { - #[error("Test queries should be replaced with the real thing.")] - TestQuery, -} diff --git a/crates/codegen/language/definition/src/model/manifest.rs b/crates/codegen/language/definition/src/model/manifest.rs index 1245e7531b..a26e9c2b7f 100644 --- a/crates/codegen/language/definition/src/model/manifest.rs +++ b/crates/codegen/language/definition/src/model/manifest.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; use std::path::PathBuf; use codegen_language_internal_macros::{derive_spanned_type, ParseInputTokens, WriteOutputTokens}; -use indexmap::{IndexMap, IndexSet}; +use indexmap::IndexSet; use semver::Version; use serde::{Deserialize, Serialize}; @@ -22,8 +22,6 @@ pub struct Language { pub versions: IndexSet, pub sections: Vec
, - - pub queries: IndexMap, } impl Language { diff --git a/crates/codegen/language/tests/src/fail/definitions/duplicate_expression_name/test.rs b/crates/codegen/language/tests/src/fail/definitions/duplicate_expression_name/test.rs index 620834bc41..d1e15e7cab 100644 --- a/crates/codegen/language/tests/src/fail/definitions/duplicate_expression_name/test.rs +++ b/crates/codegen/language/tests/src/fail/definitions/duplicate_expression_name/test.rs @@ -27,8 +27,7 @@ codegen_language_macros::compile!(Language( ) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/definitions/duplicate_item_name/test.rs b/crates/codegen/language/tests/src/fail/definitions/duplicate_item_name/test.rs index 72a45eb881..bb8ef50fa2 100644 --- a/crates/codegen/language/tests/src/fail/definitions/duplicate_item_name/test.rs +++ b/crates/codegen/language/tests/src/fail/definitions/duplicate_item_name/test.rs @@ -17,8 +17,7 @@ codegen_language_macros::compile!(Language( Struct(name = Bar2, fields = (field = Required(Bar1))) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/definitions/duplicate_variant_name/test.rs b/crates/codegen/language/tests/src/fail/definitions/duplicate_variant_name/test.rs index b9d524182e..556522ea2a 100644 --- a/crates/codegen/language/tests/src/fail/definitions/duplicate_variant_name/test.rs +++ b/crates/codegen/language/tests/src/fail/definitions/duplicate_variant_name/test.rs @@ -30,8 +30,7 @@ codegen_language_macros::compile!(Language( ) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/parsing/duplicate_map_key/test.rs b/crates/codegen/language/tests/src/fail/parsing/duplicate_map_key/test.rs index 8bec61a361..0144db5988 100644 --- a/crates/codegen/language/tests/src/fail/parsing/duplicate_map_key/test.rs +++ b/crates/codegen/language/tests/src/fail/parsing/duplicate_map_key/test.rs @@ -26,8 +26,7 @@ codegen_language_macros::compile!(Language( ) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/parsing/duplicate_set_entry/test.rs b/crates/codegen/language/tests/src/fail/parsing/duplicate_set_entry/test.rs index a10cf79ffc..7cb54ed37f 100644 --- a/crates/codegen/language/tests/src/fail/parsing/duplicate_set_entry/test.rs +++ b/crates/codegen/language/tests/src/fail/parsing/duplicate_set_entry/test.rs @@ -16,8 +16,7 @@ codegen_language_macros::compile!(Language( definitions = [TokenDefinition(scanner = Atom("bar"))] )] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/parsing/empty_string/test.rs b/crates/codegen/language/tests/src/fail/parsing/empty_string/test.rs index 3c7a1bc2b3..e4683cca95 100644 --- a/crates/codegen/language/tests/src/fail/parsing/empty_string/test.rs +++ b/crates/codegen/language/tests/src/fail/parsing/empty_string/test.rs @@ -16,8 +16,7 @@ codegen_language_macros::compile!(Language( definitions = [TokenDefinition(scanner = Atom(""))] )] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/parsing/missing_field/test.rs b/crates/codegen/language/tests/src/fail/parsing/missing_field/test.rs index a005b36832..8dcd1f77c9 100644 --- a/crates/codegen/language/tests/src/fail/parsing/missing_field/test.rs +++ b/crates/codegen/language/tests/src/fail/parsing/missing_field/test.rs @@ -10,8 +10,7 @@ codegen_language_macros::compile!(Language( sections = [Section( // title = "Section One" topics = [] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/parsing/unrecognized_field/test.rs b/crates/codegen/language/tests/src/fail/parsing/unrecognized_field/test.rs index 4c124c0304..64fb49aa65 100644 --- a/crates/codegen/language/tests/src/fail/parsing/unrecognized_field/test.rs +++ b/crates/codegen/language/tests/src/fail/parsing/unrecognized_field/test.rs @@ -17,8 +17,7 @@ codegen_language_macros::compile!(Language( )], unrecognized_field = true )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/parsing/unrecognized_variant/test.rs b/crates/codegen/language/tests/src/fail/parsing/unrecognized_variant/test.rs index 7373bfd01c..3a24bbff66 100644 --- a/crates/codegen/language/tests/src/fail/parsing/unrecognized_variant/test.rs +++ b/crates/codegen/language/tests/src/fail/parsing/unrecognized_variant/test.rs @@ -19,8 +19,7 @@ codegen_language_macros::compile!(Language( ), Topic(title = "Topic Two", items = [Unrecognized(true)]) ] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/reachability/unreachable/test.rs b/crates/codegen/language/tests/src/fail/reachability/unreachable/test.rs index 18d9b84d13..2ef831635d 100644 --- a/crates/codegen/language/tests/src/fail/reachability/unreachable/test.rs +++ b/crates/codegen/language/tests/src/fail/reachability/unreachable/test.rs @@ -20,8 +20,7 @@ codegen_language_macros::compile!(Language( Struct(name = Baz2, fields = (field = Required(Baz1))) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/reachability/unused_version/test.rs b/crates/codegen/language/tests/src/fail/reachability/unused_version/test.rs index aa792a690a..3b20b3ad95 100644 --- a/crates/codegen/language/tests/src/fail/reachability/unused_version/test.rs +++ b/crates/codegen/language/tests/src/fail/reachability/unused_version/test.rs @@ -22,8 +22,7 @@ codegen_language_macros::compile!(Language( ) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/references/disabled_too_late/test.rs b/crates/codegen/language/tests/src/fail/references/disabled_too_late/test.rs index d0e23aded6..bfd3b4d46c 100644 --- a/crates/codegen/language/tests/src/fail/references/disabled_too_late/test.rs +++ b/crates/codegen/language/tests/src/fail/references/disabled_too_late/test.rs @@ -33,8 +33,7 @@ codegen_language_macros::compile!(Language( ) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/references/enabled_too_early/test.rs b/crates/codegen/language/tests/src/fail/references/enabled_too_early/test.rs index 10e9907ffc..842eb3b6ef 100644 --- a/crates/codegen/language/tests/src/fail/references/enabled_too_early/test.rs +++ b/crates/codegen/language/tests/src/fail/references/enabled_too_early/test.rs @@ -33,8 +33,7 @@ codegen_language_macros::compile!(Language( ) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/references/invalid_filter/test.rs b/crates/codegen/language/tests/src/fail/references/invalid_filter/test.rs index 193a20c185..cf9e7eac2e 100644 --- a/crates/codegen/language/tests/src/fail/references/invalid_filter/test.rs +++ b/crates/codegen/language/tests/src/fail/references/invalid_filter/test.rs @@ -16,8 +16,7 @@ codegen_language_macros::compile!(Language( Fragment(name = Baz, scanner = Atom("baz")) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/references/invalid_version/test.rs b/crates/codegen/language/tests/src/fail/references/invalid_version/test.rs index 979c3ee5bb..2930071031 100644 --- a/crates/codegen/language/tests/src/fail/references/invalid_version/test.rs +++ b/crates/codegen/language/tests/src/fail/references/invalid_version/test.rs @@ -45,8 +45,7 @@ codegen_language_macros::compile!(Language( ) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/references/unknown_reference/test.rs b/crates/codegen/language/tests/src/fail/references/unknown_reference/test.rs index 6bf723d629..479e2b4d14 100644 --- a/crates/codegen/language/tests/src/fail/references/unknown_reference/test.rs +++ b/crates/codegen/language/tests/src/fail/references/unknown_reference/test.rs @@ -22,8 +22,7 @@ codegen_language_macros::compile!(Language( ) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/references/unordered_version_pair/test.rs b/crates/codegen/language/tests/src/fail/references/unordered_version_pair/test.rs index ae8428975b..e02446978d 100644 --- a/crates/codegen/language/tests/src/fail/references/unordered_version_pair/test.rs +++ b/crates/codegen/language/tests/src/fail/references/unordered_version_pair/test.rs @@ -28,8 +28,7 @@ codegen_language_macros::compile!(Language( ) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/fail/references/version_not_found/test.rs b/crates/codegen/language/tests/src/fail/references/version_not_found/test.rs index d97e2449cd..08e3651473 100644 --- a/crates/codegen/language/tests/src/fail/references/version_not_found/test.rs +++ b/crates/codegen/language/tests/src/fail/references/version_not_found/test.rs @@ -25,8 +25,7 @@ codegen_language_macros::compile!(Language( ) ] )] - )], - queries = () + )] )); fn main() {} diff --git a/crates/codegen/language/tests/src/pass/tiny_language.rs b/crates/codegen/language/tests/src/pass/tiny_language.rs index 870f179080..d39f12acfa 100644 --- a/crates/codegen/language/tests/src/pass/tiny_language.rs +++ b/crates/codegen/language/tests/src/pass/tiny_language.rs @@ -37,8 +37,7 @@ codegen_language_macros::compile!(Language( ) ] )] - )], - queries = () + )] )); #[test] @@ -118,8 +117,7 @@ fn definition() { ] .into() }], - }], - queries: [].into() + }] } .into(), ); diff --git a/crates/codegen/runtime/cargo/Cargo.toml b/crates/codegen/runtime/cargo/Cargo.toml index a22296ac93..56f20d4dfe 100644 --- a/crates/codegen/runtime/cargo/Cargo.toml +++ b/crates/codegen/runtime/cargo/Cargo.toml @@ -13,9 +13,9 @@ codegen_runtime_generator = { workspace = true } [dependencies] ariadne = { workspace = true } +metaslang_cst = { workspace = true } napi = { workspace = true, optional = true } napi-derive = { workspace = true, optional = true } -nom = { workspace = true } semver = { workspace = true } serde = { workspace = true } serde_json = { workspace = true, optional = true } diff --git a/crates/codegen/runtime/cargo/src/lib.rs b/crates/codegen/runtime/cargo/src/lib.rs index 9bccc0fff3..16f152c12c 100644 --- a/crates/codegen/runtime/cargo/src/lib.rs +++ b/crates/codegen/runtime/cargo/src/lib.rs @@ -1,6 +1,5 @@ #![allow(dead_code)] mod runtime; -mod user_defined; pub use runtime::*; diff --git a/crates/codegen/runtime/cargo/src/runtime/cst.rs b/crates/codegen/runtime/cargo/src/runtime/cst.rs deleted file mode 100644 index a7eab69d4e..0000000000 --- a/crates/codegen/runtime/cargo/src/runtime/cst.rs +++ /dev/null @@ -1,198 +0,0 @@ -use std::rc::Rc; - -use serde::Serialize; - -use crate::cursor::Cursor; -use crate::kinds::{NodeLabel, RuleKind, TokenKind}; -use crate::text_index::TextIndex; - -#[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub struct LabeledNode { - pub label: Option, - pub node: Node, -} - -impl LabeledNode { - /// Creates an anonymous node (without a label). - pub fn anonymous(node: Node) -> Self { - Self { label: None, node } - } -} - -impl std::ops::Deref for LabeledNode { - type Target = Node; - - fn deref(&self) -> &Self::Target { - &self.node - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub struct RuleNode { - pub kind: RuleKind, - pub text_len: TextIndex, - #[serde(skip_serializing_if = "Vec::is_empty")] - pub children: Vec, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub struct TokenNode { - pub kind: TokenKind, - pub text: String, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub enum Node { - Rule(Rc), - Token(Rc), -} - -impl Node { - pub fn rule(kind: RuleKind, children: Vec) -> Self { - let text_len = children.iter().map(|node| node.text_len()).sum(); - - Self::Rule(Rc::new(RuleNode { - kind, - text_len, - children, - })) - } - - pub fn token(kind: TokenKind, text: String) -> Self { - Self::Token(Rc::new(TokenNode { kind, text })) - } - - pub fn text_len(&self) -> TextIndex { - match self { - Self::Rule(node) => node.text_len, - Self::Token(node) => (&node.text).into(), - } - } - - /// Returns a slice of the children (not all descendants) of this node. - pub fn children(&self) -> &[LabeledNode] { - match self { - Self::Rule(node) => &node.children, - Self::Token(_) => &[], - } - } - - /// Creates a [`Cursor`] that starts at the current node as the root and a given initial `text_offset`. - pub fn cursor_with_offset(&self, text_offset: TextIndex) -> Cursor { - Cursor::new(self.clone(), text_offset) - } - - /// Reconstructs the original source code from the parse tree. - pub fn unparse(self) -> String { - match self { - Self::Rule(rule) => rule.unparse(), - Self::Token(token) => token.text.clone(), - } - } - - pub fn into_rule(self) -> Option> { - match self { - Self::Rule(rule) => Some(rule), - Self::Token(..) => None, - } - } - - pub fn is_rule(&self) -> bool { - self.as_rule().is_some() - } - - pub fn as_rule(&self) -> Option<&Rc> { - match self { - Self::Rule(rule) => Some(rule), - Self::Token(..) => None, - } - } - - pub fn is_rule_with_kind(&self, kind: RuleKind) -> bool { - self.as_rule_with_kind(kind).is_some() - } - - pub fn as_rule_with_kind(&self, kind: RuleKind) -> Option<&Rc> { - self.as_rule().filter(|rule| rule.kind == kind) - } - - pub fn is_rule_with_kinds(&self, kinds: &[RuleKind]) -> bool { - self.as_rule_with_kinds(kinds).is_some() - } - - pub fn as_rule_with_kinds(&self, kinds: &[RuleKind]) -> Option<&Rc> { - self.as_rule().filter(|rule| kinds.contains(&rule.kind)) - } - - pub fn into_token(self) -> Option> { - match self { - Self::Token(token) => Some(token), - Self::Rule(..) => None, - } - } - - pub fn is_token(&self) -> bool { - self.as_token().is_some() - } - - pub fn as_token(&self) -> Option<&Rc> { - match self { - Self::Token(token) => Some(token), - Self::Rule(..) => None, - } - } - - pub fn is_token_with_kind(&self, kind: TokenKind) -> bool { - self.as_token_with_kind(kind).is_some() - } - - pub fn as_token_with_kind(&self, kind: TokenKind) -> Option<&Rc> { - self.as_token().filter(|token| token.kind == kind) - } - - pub fn is_token_with_kinds(&self, kinds: &[TokenKind]) -> bool { - self.as_token_with_kinds(kinds).is_some() - } - - pub fn as_token_with_kinds(&self, kinds: &[TokenKind]) -> Option<&Rc> { - self.as_token().filter(|token| kinds.contains(&token.kind)) - } - - pub fn is_trivia(&self) -> bool { - match self { - Self::Rule(_) => false, - Self::Token(token) => token.kind.is_trivia(), - } - } -} - -impl From> for Node { - fn from(node: Rc) -> Self { - Self::Rule(node) - } -} - -impl From> for Node { - fn from(node: Rc) -> Self { - Self::Token(node) - } -} - -impl RuleNode { - /// Creates a [`Cursor`] that starts at the current node as the root and a given initial `text_offset`. - pub fn cursor_with_offset(self: Rc, text_offset: TextIndex) -> Cursor { - Cursor::new(Node::Rule(self), text_offset) - } - - /// Reconstructs the original source code from the parse tree. - pub fn unparse(self: Rc) -> String { - let acc = String::with_capacity(self.text_len.utf8); - - self.cursor_with_offset(TextIndex::ZERO) - .filter_map(|node| node.into_token()) - .fold(acc, |mut acc, token| { - acc.push_str(&token.text); - acc - }) - } -} diff --git a/crates/codegen/runtime/cargo/src/runtime/cursor.rs b/crates/codegen/runtime/cargo/src/runtime/cursor.rs deleted file mode 100644 index 2d97117130..0000000000 --- a/crates/codegen/runtime/cargo/src/runtime/cursor.rs +++ /dev/null @@ -1,462 +0,0 @@ -//! A cursor that can traverse a CST in a DFS pre-order fashion. - -use std::rc::Rc; - -use crate::cst::{LabeledNode, Node, RuleNode}; -use crate::kinds::{NodeLabel, RuleKind, TokenKind}; -use crate::text_index::{TextIndex, TextRange}; - -/// A node in the ancestor path of a [`Cursor`]. -#[derive(Clone, Debug, PartialEq, Eq)] -struct PathAncestor { - parent: Option>, - rule_node: Rc, - child_number: usize, - text_offset: TextIndex, -} - -/// A cursor that can traverse a CST. -/// -/// Nodes are visited in a DFS pre-order traversal. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Cursor { - /// The parent path of this cursor - parent: Option>, - /// The node the cursor is currently pointing to. - node: Node, - /// The index of the current child node in the parent's children. - // Required to go to the next/previous sibling. - child_number: usize, - /// Text offset that corresponds to the beginning of the currently pointed to node. - text_offset: TextIndex, - /// Whether the cursor is completed, i.e. at the root node as a result of traversal (or when `complete`d). - /// If `true`, the cursor cannot be moved. - is_completed: bool, -} - -impl Cursor { - fn as_ancestor_node(&self) -> Option> { - if let Node::Rule(rule_node) = &self.node { - Some(Rc::new(PathAncestor { - parent: self.parent.clone(), - rule_node: Rc::clone(rule_node), - child_number: self.child_number, - text_offset: self.text_offset, - })) - } else { - None - } - } - - fn set_from_ancestor_node(&mut self, ancestor: &Rc) { - self.parent = ancestor.parent.clone(); - self.node = Node::Rule(Rc::clone(&ancestor.rule_node)); - self.child_number = ancestor.child_number; - self.text_offset = ancestor.text_offset; - } -} - -impl Iterator for Cursor { - type Item = Node; - - fn next(&mut self) -> Option { - if self.is_completed { - None - } else { - let cur = self.node(); - self.go_to_next(); - - Some(cur) - } - } -} - -impl Cursor { - pub(crate) fn new(node: Node, text_offset: TextIndex) -> Self { - Self { - parent: None, - node, - child_number: 0, - text_offset, - is_completed: false, - } - } - - /// Resets the cursor to the root node. - pub fn reset(&mut self) { - self.complete(); - self.is_completed = false; - } - - /// Completes the cursor, setting it to the root node. - pub fn complete(&mut self) { - if let Some(parent) = &self.parent.clone() { - let mut parent = parent; - while let Some(grandparent) = &parent.parent { - parent = grandparent; - } - self.set_from_ancestor_node(parent); - } - self.is_completed = true; - } - - /// Unlike `clone`, this re-roots at the current node. - /// It does preserve the correct text offset however, - /// even though the path is reset. - #[must_use] - pub fn spawn(&self) -> Self { - Self { - is_completed: false, - parent: None, - node: self.node.clone(), - child_number: 0, - text_offset: self.text_offset, - } - } - - /// Whether the cursor can be moved. - pub fn is_completed(&self) -> bool { - self.is_completed - } - - /// Returns the currently pointed to [`Node`]. - pub fn node(&self) -> Node { - self.node.clone() - } - - pub fn label(&self) -> Option { - self.parent.as_ref().and_then(|parent| { - let this = &parent.rule_node.children[self.child_number]; - - this.label - }) - } - - /// Returns the text offset that corresponds to the beginning of the currently pointed to node. - pub fn text_offset(&self) -> TextIndex { - self.text_offset - } - - /// Returns the text range that corresponds to the currently pointed to node. - pub fn text_range(&self) -> TextRange { - let start = self.text_offset; - let end = start + self.node.text_len(); - start..end - } - - /// Returns the depth of the current node in the CST, i.e. the number of ancestors. - pub fn depth(&self) -> usize { - let mut depth = 0; - if let Some(parent) = &self.parent { - let mut parent = parent; - depth += 1; - while let Some(grandparent) = &parent.parent { - depth += 1; - parent = grandparent; - } - } - - depth - } - - /// Returns an iterator over the current node's ancestors, starting from the parent of the current node. - pub fn ancestors(&self) -> impl Iterator> { - struct Iter { - a: Option>, - } - impl Iterator for Iter { - type Item = Rc; - - fn next(&mut self) -> Option { - if let Some(a) = self.a.take() { - self.a = a.parent.clone(); - Some(Rc::clone(&a.rule_node)) - } else { - None - } - } - } - Iter { - a: self.parent.clone(), - } - } - - /// Attempts to go to current node's next one, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next(&mut self) -> bool { - if self.is_completed { - return false; - } - - self.go_to_first_child() || self.go_to_next_non_descendent() - } - - /// Attempts to go to current node's next non-descendent. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_non_descendent(&mut self) -> bool { - if self.is_completed { - return false; - } - - while !self.go_to_next_sibling() { - if !self.go_to_parent() { - return false; - } - } - - true - } - - /// Attempts to go to current node's previous one, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_previous(&mut self) -> bool { - if self.is_completed { - return false; - } - - while !self.go_to_previous_sibling() { - if !self.go_to_parent() { - return false; - } - } - - while self.go_to_last_child() {} - - true - } - - /// Attempts to go to current node's parent. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_parent(&mut self) -> bool { - if let Some(parent) = &self.parent.clone() { - self.set_from_ancestor_node(parent); - - true - } else { - self.is_completed = true; - - false - } - } - - /// Attempts to go to current node's first child. - /// - /// Returns `false` if the cursor is finished or there's no child to go to. - pub fn go_to_first_child(&mut self) -> bool { - if self.is_completed { - return false; - } - - // If the current cursor is a node and it has children, go to first children - if let Some(new_parent) = self.as_ancestor_node() { - if let Some(new_child) = new_parent.rule_node.children.first().cloned() { - self.parent = Some(new_parent); - self.node = new_child.node; - self.child_number = 0; - - return true; - } - } - - false - } - - /// Attempts to go to current node's last child. - /// - /// Returns `false` if the cursor is finished or there's no child to go to. - pub fn go_to_last_child(&mut self) -> bool { - if self.is_completed { - return false; - } - - if let Some(new_parent) = self.as_ancestor_node() { - if let Some(new_child) = new_parent.rule_node.children.last().cloned() { - self.child_number = new_parent.rule_node.children.len() - 1; - // This is cheaper than summing up the length of the children - self.text_offset += new_parent.rule_node.text_len - new_child.text_len(); - self.node = new_child.node; - self.parent = Some(new_parent); - - return true; - } - } - - false - } - - /// Attempts to go to current node's nth child. - /// - /// Returns `false` if the cursor is finished or there's no child to go to. - pub fn go_to_nth_child(&mut self, child_number: usize) -> bool { - if self.is_completed { - return false; - } - - if let Some(new_parent) = self.as_ancestor_node() { - if let Some(new_child) = new_parent.rule_node.children.get(child_number).cloned() { - self.node = new_child.node; - self.child_number = child_number; - // Sum up the length of the children before this child - // TODO(#871): it might sometimes be quicker to start from the end (like `go_to_last_child`) - self.text_offset += new_parent.rule_node.children[..child_number] - .iter() - .map(|node| node.text_len()) - .sum(); - self.parent = Some(new_parent); - - return true; - } - } - - false - } - - /// Attempts to go to current node's next sibling. - /// - /// Returns `false` if the cursor is finished or there's no sibling to go to. - pub fn go_to_next_sibling(&mut self) -> bool { - if self.is_completed { - return false; - } - - if let Some(parent) = &self.parent { - let new_child_number = self.child_number + 1; - if let Some(new_child) = parent.rule_node.children.get(new_child_number) { - self.text_offset += self.node.text_len(); - self.node = new_child.node.clone(); - self.child_number = new_child_number; - - return true; - } - } - - false - } - - /// Attempts to go to current node's previous sibling. - /// - /// Returns `false` if the cursor is finished or there's no sibling to go to. - pub fn go_to_previous_sibling(&mut self) -> bool { - if self.is_completed { - return false; - } - - if let Some(parent) = &self.parent { - if self.child_number > 0 { - let new_child_number = self.child_number - 1; - let new_child = &parent.rule_node.children[new_child_number]; - self.text_offset -= new_child.node.text_len(); - self.node = new_child.node.clone(); - self.child_number = new_child_number; - - return true; - } - } - - false - } - - /// Attempts to go to the next token, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_token(&mut self) -> bool { - self.go_to_next_matching(|node| node.is_token()) - } - - /// Attempts to go to the next token with the given kind, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_token_with_kind(&mut self, kind: TokenKind) -> bool { - self.go_to_next_matching(|node| node.is_token_with_kind(kind)) - } - - /// Attempts to go to the next token with any of the given kinds, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_token_with_kinds(&mut self, kinds: &[TokenKind]) -> bool { - self.go_to_next_matching(|node| node.is_token_with_kinds(kinds)) - } - - /// Attempts to go to the next rule, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_rule(&mut self) -> bool { - self.go_to_next_matching(|node| node.is_rule()) - } - - /// Attempts to go to the next rule with the given kind, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_rule_with_kind(&mut self, kind: RuleKind) -> bool { - self.go_to_next_matching(|node| node.is_rule_with_kind(kind)) - } - - /// Attempts to go to the next rule with any of the given kinds, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_rule_with_kinds(&mut self, kinds: &[RuleKind]) -> bool { - self.go_to_next_matching(|node| node.is_rule_with_kinds(kinds)) - } - - fn go_to_next_matching(&mut self, pred: impl Fn(&Node) -> bool) -> bool { - while self.go_to_next() { - if pred(&self.node) { - return true; - } - } - - false - } -} - -/// A [`Cursor`] that also keeps track of the labels of the nodes it visits. -pub struct CursorWithLabels { - cursor: Cursor, -} - -impl CursorWithLabels { - pub fn without_labels(self) -> Cursor { - self.cursor - } -} - -impl Iterator for CursorWithLabels { - type Item = LabeledNode; - - fn next(&mut self) -> Option { - let label = self.cursor.label(); - - self.cursor.next().map(|node| LabeledNode { label, node }) - } -} - -impl std::ops::Deref for CursorWithLabels { - type Target = Cursor; - - fn deref(&self) -> &Self::Target { - &self.cursor - } -} - -impl std::ops::DerefMut for CursorWithLabels { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cursor - } -} - -impl Cursor { - /// Returns a [`CursorWithLabels`] that wraps this cursor. - pub fn with_labels(self) -> CursorWithLabels { - CursorWithLabels::from(self) - } -} - -impl From for CursorWithLabels { - fn from(cursor: Cursor) -> Self { - CursorWithLabels { cursor } - } -} diff --git a/crates/codegen/runtime/cargo/src/runtime/generated/kinds.rs b/crates/codegen/runtime/cargo/src/runtime/generated/kinds.rs index 9d4219b298..e691d07dae 100644 --- a/crates/codegen/runtime/cargo/src/runtime/generated/kinds.rs +++ b/crates/codegen/runtime/cargo/src/runtime/generated/kinds.rs @@ -23,6 +23,8 @@ pub enum RuleKind { Stub3, } +impl metaslang_cst::NonTerminalKind for RuleKind {} + #[derive( Debug, Eq, @@ -55,6 +57,8 @@ pub enum NodeLabel { Stub3, } +impl metaslang_cst::EdgeKind for NodeLabel {} + #[derive( Debug, Eq, @@ -79,8 +83,8 @@ pub enum TokenKind { Stub3, } -impl TokenKind { - pub fn is_trivia(&self) -> bool { +impl metaslang_cst::TerminalKind for TokenKind { + fn is_trivia(&self) -> bool { false } } diff --git a/crates/codegen/runtime/cargo/src/runtime/kinds.rs.jinja2 b/crates/codegen/runtime/cargo/src/runtime/kinds.rs.jinja2 index c89e627052..e45543aee6 100644 --- a/crates/codegen/runtime/cargo/src/runtime/kinds.rs.jinja2 +++ b/crates/codegen/runtime/cargo/src/runtime/kinds.rs.jinja2 @@ -28,6 +28,8 @@ pub enum RuleKind { {%- endif -%} } +impl metaslang_cst::NonTerminalKind for RuleKind {} + #[derive( Debug, Eq, @@ -67,6 +69,8 @@ pub enum NodeLabel { {%- endif -%} } +impl metaslang_cst::EdgeKind for NodeLabel {} + #[derive( Debug, Eq, @@ -98,18 +102,18 @@ pub enum TokenKind { {%- endif -%} } -impl TokenKind { - pub fn is_trivia(&self) -> bool { - {%- if rendering_in_stubs -%} - false - {%- else -%} - matches!( - self, - {%- for variant in model.trivia_scanner_names -%} - | Self::{{ variant }} - {%- endfor -%} - ) - {%- endif -%} +impl metaslang_cst::TerminalKind for TokenKind { + fn is_trivia(&self) -> bool { + {%- if rendering_in_stubs -%} + false + {%- else -%} + matches!( + self, + {%- for variant in model.trivia_scanner_names -%} + | Self::{{ variant }} + {%- endfor -%} + ) + {%- endif -%} } } diff --git a/crates/codegen/runtime/cargo/src/runtime/mod.rs b/crates/codegen/runtime/cargo/src/runtime/mod.rs index eab63a3f88..9fe8614b84 100644 --- a/crates/codegen/runtime/cargo/src/runtime/mod.rs +++ b/crates/codegen/runtime/cargo/src/runtime/mod.rs @@ -2,17 +2,69 @@ pub(crate) mod parser_support; pub(crate) mod lexer; -pub mod cst; -pub mod cursor; -pub mod parse_error; -pub mod parse_output; -pub mod query; -pub mod text_index; - #[path = "generated/kinds.rs"] pub mod kinds; #[path = "generated/language.rs"] pub mod language; +pub mod parse_error; +pub mod parse_output; #[cfg(feature = "slang_napi_interfaces")] pub mod napi_interface; + +mod metaslang_cst { + #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] + // These derives are because default #[derive(...)] on a generic type implements only the trait + // with default bounds also implied for the generic types as well, i.e. + // + // #[derive(Clone)] // expands to `impl Clone for MyOption { ... }` (notice the `T: Clone`) + // struct MyOption(Option); + // + // This assumes that the underlying data type uses this internally, however it's only used as a + // type container/marker. + // + // A slightly more "correct" approach would be to implement the traits while skipping the bounds for + // the type marker, however this can be more noisy + pub enum KindTypes {} + + impl metaslang_cst::KindTypes for KindTypes { + type NonTerminalKind = crate::kinds::RuleKind; + type TerminalKind = crate::kinds::TokenKind; + type EdgeKind = crate::kinds::NodeLabel; + } +} + +pub mod cst { + use metaslang_cst::cst; + + use super::metaslang_cst::KindTypes; + + pub type Node = cst::Node; + pub type RuleNode = cst::NonTerminalNode; + pub type TokenNode = cst::TerminalNode; + pub type LabeledNode = cst::LabeledNode; +} + +pub mod cursor { + use metaslang_cst::cursor; + + use super::metaslang_cst::KindTypes; + + pub type Cursor = cursor::Cursor; + pub type CursorWithLabels = cursor::CursorWithLabels; +} + +pub mod query { + use metaslang_cst::query; + + use super::metaslang_cst::KindTypes; + + pub type Query = query::Query; + pub type QueryResult = query::QueryResult; + pub type QueryResultIterator = query::QueryResultIterator; +} + +pub mod text_index { + use metaslang_cst::text_index; + pub use text_index::{TextIndex, TextRange, TextRangeExtensions}; +} diff --git a/crates/codegen/runtime/cargo/src/runtime/napi_interface/ast_selectors.rs.jinja2 b/crates/codegen/runtime/cargo/src/runtime/napi_interface/ast_selectors.rs.jinja2 index 4add655bdf..c6e80611e7 100644 --- a/crates/codegen/runtime/cargo/src/runtime/napi_interface/ast_selectors.rs.jinja2 +++ b/crates/codegen/runtime/cargo/src/runtime/napi_interface/ast_selectors.rs.jinja2 @@ -5,7 +5,7 @@ use std::rc::Rc; use napi::Either; use napi_derive::napi; -use crate::napi_interface::cst::{RuleNode, TokenNode}; +use crate::napi_interface::cst::{NAPINodeExtensions, RuleNode, TokenNode}; use crate::napi_interface::{RuleKind, RustLabeledNode, RustNode, RustRuleNode, TokenKind}; // diff --git a/crates/codegen/runtime/cargo/src/runtime/napi_interface/cst.rs b/crates/codegen/runtime/cargo/src/runtime/napi_interface/cst.rs index 7f1e75d315..d67077fe98 100644 --- a/crates/codegen/runtime/cargo/src/runtime/napi_interface/cst.rs +++ b/crates/codegen/runtime/cargo/src/runtime/napi_interface/cst.rs @@ -15,9 +15,13 @@ pub enum NodeType { Token, } -impl RustNode { +pub trait NAPINodeExtensions { + fn into_js_either_node(self) -> Either; +} + +impl NAPINodeExtensions for RustNode { /// Converts the node into `napi` wrapper for `RuleNode | TokenNode` JS object. - pub fn into_js_either_node(self) -> Either { + fn into_js_either_node(self) -> Either { match self { RustNode::Rule(rule) => Either::A(RuleNode(rule)), RustNode::Token(token) => Either::B(TokenNode(token)), @@ -25,12 +29,6 @@ impl RustNode { } } -impl From for Either { - fn from(value: RustNode) -> Self { - value.into_js_either_node() - } -} - #[derive(Debug)] #[napi(namespace = "cst")] pub struct RuleNode(pub(crate) Rc); @@ -71,7 +69,7 @@ impl RuleNode { self.0 .children .iter() - .map(|child| child.node.clone().into()) + .map(|child| child.node.clone().into_js_either_node()) .collect() } diff --git a/crates/codegen/runtime/cargo/src/runtime/napi_interface/cursor.rs b/crates/codegen/runtime/cargo/src/runtime/napi_interface/cursor.rs index f3c91feaa4..d04b0759ad 100644 --- a/crates/codegen/runtime/cargo/src/runtime/napi_interface/cursor.rs +++ b/crates/codegen/runtime/cargo/src/runtime/napi_interface/cursor.rs @@ -7,7 +7,7 @@ use napi::Either; use napi_derive::napi; use text_index::{TextIndex, TextRange}; -use crate::napi_interface::cst::{self, RuleNode, TokenNode}; +use crate::napi_interface::cst::{self, NAPINodeExtensions, RuleNode, TokenNode}; use crate::napi_interface::{text_index, NodeLabel, RuleKind, RustCursor, TokenKind}; #[napi(namespace = "cursor")] @@ -53,7 +53,7 @@ impl Cursor { #[napi(ts_return_type = "cst.Node", catch_unwind)] pub fn node(&self) -> Either { - self.0.node().into() + self.0.node().into_js_either_node() } #[napi(getter, ts_return_type = "kinds.NodeLabel", catch_unwind)] diff --git a/crates/codegen/runtime/cargo/src/runtime/napi_interface/generated/ast_selectors.rs b/crates/codegen/runtime/cargo/src/runtime/napi_interface/generated/ast_selectors.rs index 0d49c77389..c21a8c4e55 100644 --- a/crates/codegen/runtime/cargo/src/runtime/napi_interface/generated/ast_selectors.rs +++ b/crates/codegen/runtime/cargo/src/runtime/napi_interface/generated/ast_selectors.rs @@ -7,7 +7,7 @@ use std::rc::Rc; use napi::Either; use napi_derive::napi; -use crate::napi_interface::cst::{RuleNode, TokenNode}; +use crate::napi_interface::cst::{NAPINodeExtensions, RuleNode, TokenNode}; use crate::napi_interface::{RuleKind, RustLabeledNode, RustNode, RustRuleNode, TokenKind}; // diff --git a/crates/codegen/runtime/cargo/src/runtime/napi_interface/parse_output.rs b/crates/codegen/runtime/cargo/src/runtime/napi_interface/parse_output.rs index aa2cb6c7a6..d12fbf8a7c 100644 --- a/crates/codegen/runtime/cargo/src/runtime/napi_interface/parse_output.rs +++ b/crates/codegen/runtime/cargo/src/runtime/napi_interface/parse_output.rs @@ -1,7 +1,7 @@ use napi::Either; use napi_derive::napi; -use crate::napi_interface::cst::{RuleNode, TokenNode}; +use crate::napi_interface::cst::{NAPINodeExtensions, RuleNode, TokenNode}; use crate::napi_interface::{cursor, parse_error, RustParseOutput}; #[napi(namespace = "parse_output")] @@ -17,7 +17,7 @@ impl From for ParseOutput { impl ParseOutput { #[napi(ts_return_type = "cst.Node", catch_unwind)] pub fn tree(&self) -> Either { - self.0.tree().into() + self.0.tree().into_js_either_node() } #[napi(ts_return_type = "Array", catch_unwind)] diff --git a/crates/codegen/runtime/cargo/src/runtime/parser_support/parser_result.rs b/crates/codegen/runtime/cargo/src/runtime/parser_support/parser_result.rs index 094cdbc373..8e951948fe 100644 --- a/crates/codegen/runtime/cargo/src/runtime/parser_support/parser_result.rs +++ b/crates/codegen/runtime/cargo/src/runtime/parser_support/parser_result.rs @@ -1,5 +1,7 @@ use std::ops::ControlFlow; +use metaslang_cst::TerminalKind; + use crate::cst::{self, LabeledNode, Node}; use crate::kinds::{NodeLabel, RuleKind, TokenKind}; use crate::text_index::TextIndex; diff --git a/crates/codegen/runtime/cargo/src/runtime/parser_support/sequence_helper.rs b/crates/codegen/runtime/cargo/src/runtime/parser_support/sequence_helper.rs index 646c03239c..724e8cd01a 100644 --- a/crates/codegen/runtime/cargo/src/runtime/parser_support/sequence_helper.rs +++ b/crates/codegen/runtime/cargo/src/runtime/parser_support/sequence_helper.rs @@ -1,5 +1,7 @@ use std::ops::ControlFlow; +use metaslang_cst::TerminalKind; + use crate::cst::{self, LabeledNode}; use crate::kinds::{NodeLabel, TokenKind}; use crate::parser_support::parser_result::{Match, ParserResult, PrattElement, SkippedUntil}; diff --git a/crates/codegen/runtime/cargo/src/runtime/query/engine.rs b/crates/codegen/runtime/cargo/src/runtime/query/engine.rs deleted file mode 100644 index 357a741ad2..0000000000 --- a/crates/codegen/runtime/cargo/src/runtime/query/engine.rs +++ /dev/null @@ -1,485 +0,0 @@ -use std::collections::HashMap; -use std::rc::Rc; - -// This crate is copied to another crate, so all imports should be relative -use super::super::cst; -use super::super::cursor::Cursor; -use super::model::{ - AlternativesMatcher, BindingMatcher, Kind, Matcher, NodeMatcher, NodeSelector, - OneOrMoreMatcher, OptionalMatcher, Query, SequenceMatcher, -}; - -impl Cursor { - pub fn query(self, queries: Vec) -> QueryResultIterator { - QueryResultIterator::new(self, queries) - } - - fn irrevocably_go_to_next_sibling(&mut self) -> bool { - if self.is_completed() { - false - } else { - if !self.go_to_next_sibling() { - self.complete(); - } - true - } - } - - fn matches_node_selector(&self, node_selector: &NodeSelector) -> bool { - match self.node() { - cst::Node::Rule(rule) => match node_selector { - NodeSelector::Anonymous => true, - NodeSelector::Kind { kind } => Kind::Rule(rule.kind) == *kind, - NodeSelector::Text { .. } => false, - NodeSelector::Label { label } => Some(*label) == self.label(), - NodeSelector::LabelAndKind { label, kind } => { - Some(*label) == self.label() && Kind::Rule(rule.kind) == *kind - } - NodeSelector::LabelAndText { .. } => false, - }, - - cst::Node::Token(token) => match node_selector { - NodeSelector::Anonymous => true, - NodeSelector::Kind { kind } => Kind::Token(token.kind) == *kind, - NodeSelector::Text { text } => token.text == *text, - NodeSelector::Label { label } => Some(*label) == self.label(), - NodeSelector::LabelAndKind { label, kind } => { - Some(*label) == self.label() && Kind::Token(token.kind) == *kind - } - NodeSelector::LabelAndText { label, text } => { - Some(*label) == self.label() && token.text == *text - } - }, - } - } -} - -impl Matcher { - // This allows for queries to pre-flight against a cursor without allocating - fn can_match(&self, cursor: &Cursor) -> bool { - match self { - Self::Binding(matcher) => matcher.child.can_match(cursor), - Self::Node(matcher) => cursor.matches_node_selector(&matcher.node_selector), - Self::Alternatives(matcher) => matcher.children.iter().any(|c| c.can_match(cursor)), - Self::Sequence(matcher) => matcher.children[0].can_match(cursor), - Self::OneOrMore(matcher) => matcher.child.can_match(cursor), - Self::Optional(_) => true, - Self::Ellipsis => true, - } - } - - fn create_combinator(&self, cursor: Cursor) -> CombinatorRef { - match self { - Self::Binding(matcher) => Box::new(BindingCombinator::new(Rc::clone(matcher), cursor)), - Self::Node(matcher) => Box::new(NodeCombinator::new(Rc::clone(matcher), cursor)), - Self::Sequence(matcher) => { - Box::new(SequenceCombinator::new(Rc::clone(matcher), cursor)) - } - Self::Alternatives(matcher) => { - Box::new(AlternativesCombinator::new(Rc::clone(matcher), cursor)) - } - Self::Optional(matcher) => { - Box::new(OptionalCombinator::new(Rc::clone(matcher), cursor)) - } - Self::OneOrMore(matcher) => { - Box::new(OneOrMoreCombinator::new(Rc::clone(matcher), cursor)) - } - Self::Ellipsis => Box::new(EllipsisCombinator::new(cursor)), - } - } -} - -pub struct QueryResult { - pub query_number: usize, - pub bindings: HashMap>, -} - -pub struct QueryResultIterator { - cursor: Cursor, - queries: Vec, - query_number: usize, - combinator: Option, -} - -impl QueryResultIterator { - fn new(cursor: Cursor, queries: Vec) -> Self { - Self { - cursor, - queries, - query_number: 0, - combinator: None, - } - } - - fn advance_to_next_possible_matching_query(&mut self) { - while !self.cursor.is_completed() { - while self.query_number < self.queries.len() { - let matcher = &self.queries[self.query_number].0; - if matcher.can_match(&self.cursor) { - self.combinator = Some(matcher.create_combinator(self.cursor.clone())); - return; - }; - self.query_number += 1; - } - self.cursor.go_to_next(); - self.query_number = 0; - } - } -} - -impl Iterator for QueryResultIterator { - type Item = QueryResult; - - fn next(&mut self) -> Option { - while !self.cursor.is_completed() { - if let Some(combinator) = self.combinator.as_mut() { - if combinator.next().is_some() { - let mut bindings = HashMap::new(); - combinator.accumulate_bindings(&mut bindings); - return Some(QueryResult { - query_number: self.query_number, - bindings, - }); - } - self.query_number += 1; - } - - self.advance_to_next_possible_matching_query(); - } - - None - } -} - -trait Combinator { - // None -> failed to match, you must backtrack. DO NOT call again - // Some(cursor) if cursor.is_complete -> matched, end of input - // Some(cursor) if !cursor.is_complete -> matched, more input to go - fn next(&mut self) -> Option; - fn accumulate_bindings(&self, bindings: &mut HashMap>); -} -type CombinatorRef = Box; - -struct BindingCombinator { - matcher: Rc, - cursor: Cursor, - child: CombinatorRef, -} - -impl BindingCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - let child = matcher.child.create_combinator(cursor.clone()); - Self { - matcher, - cursor, - child, - } - } -} - -impl Combinator for BindingCombinator { - fn next(&mut self) -> Option { - self.child.next() - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - bindings - .entry(self.matcher.name.clone()) - .or_default() - .push(self.cursor.clone()); - self.child.accumulate_bindings(bindings); - } -} - -struct NodeCombinator { - matcher: Rc, - child: Option, - cursor: Cursor, - is_initialised: bool, -} - -impl NodeCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - Self { - matcher, - child: None, - cursor, - is_initialised: false, - } - } -} - -impl Combinator for NodeCombinator { - fn next(&mut self) -> Option { - if self.cursor.is_completed() { - return None; - } - - if !self.is_initialised { - self.is_initialised = true; - - if !self - .cursor - .matches_node_selector(&self.matcher.node_selector) - { - return None; - } - - if let Some(child) = self.matcher.child.as_ref() { - let mut child_cursor = self.cursor.clone(); - if !child_cursor.go_to_first_child() { - return None; - } - - self.child = Some(child.create_combinator(child_cursor)); - } else { - let mut return_cursor = self.cursor.clone(); - return_cursor.irrevocably_go_to_next_sibling(); - return Some(return_cursor); - } - } - - if let Some(child) = self.child.as_mut() { - while let Some(cursor) = child.as_mut().next() { - if cursor.is_completed() { - let mut return_cursor = self.cursor.clone(); - return_cursor.irrevocably_go_to_next_sibling(); - return Some(return_cursor); - } - } - self.child = None; - } - - None - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - if let Some(child) = self.child.as_ref() { - child.accumulate_bindings(bindings); - } - } -} - -struct SequenceCombinator { - matcher: Rc, - children: Vec, - cursor: Cursor, - is_initialised: bool, -} - -impl SequenceCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - Self { - matcher, - children: vec![], - cursor, - is_initialised: false, - } - } -} - -impl Combinator for SequenceCombinator { - fn next(&mut self) -> Option { - if !self.is_initialised { - self.is_initialised = true; - - let child_cursor = self.cursor.clone(); - let child = self.matcher.children[0].create_combinator(child_cursor); - self.children.push(child); - } - - while !self.children.is_empty() { - if let Some(child_cursor) = self.children.last_mut().unwrap().next() { - if self.children.len() == self.matcher.children.len() { - return Some(child_cursor); - } - - let child = - self.matcher.children[self.children.len()].create_combinator(child_cursor); - self.children.push(child); - } else { - self.children.pop(); - } - } - - None - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - for child in &self.children { - child.accumulate_bindings(bindings); - } - } -} - -struct AlternativesCombinator { - matcher: Rc, - next_child_number: usize, - child: Option, - cursor: Cursor, -} - -impl AlternativesCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - Self { - matcher, - next_child_number: 0, - child: None, - cursor, - } - } -} - -impl Combinator for AlternativesCombinator { - fn next(&mut self) -> Option { - loop { - if self.child.is_none() { - match self.matcher.children.get(self.next_child_number) { - Some(child) => { - let child = child.create_combinator(self.cursor.clone()); - self.child = Some(child); - self.next_child_number += 1; - } - None => return None, - } - } - - match self.child.as_mut().unwrap().next() { - Some(cursor) => return Some(cursor), - None => self.child = None, - } - } - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - self.child.as_ref().unwrap().accumulate_bindings(bindings); - } -} - -struct OptionalCombinator { - matcher: Rc, - child: Option, - cursor: Cursor, - have_nonempty_match: bool, -} - -impl OptionalCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - Self { - matcher, - child: None, - cursor, - have_nonempty_match: false, - } - } -} - -impl Combinator for OptionalCombinator { - fn next(&mut self) -> Option { - if let Some(child) = self.child.as_mut() { - match child.next() { - result @ Some(_) => { - self.have_nonempty_match = true; - result - } - None => { - self.child = None; - None - } - } - } else { - let child_cursor = self.cursor.clone(); - let child = self.matcher.child.create_combinator(child_cursor); - self.child = Some(child); - Some(self.cursor.clone()) - } - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - if self.have_nonempty_match { - if let Some(child) = self.child.as_ref() { - child.accumulate_bindings(bindings); - } - } - } -} - -struct OneOrMoreCombinator { - matcher: Rc, - children: Vec, - cursor_for_next_repetition: Option, -} - -impl OneOrMoreCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - let cursor_for_next_repetition = Some(cursor); - Self { - matcher, - children: vec![], - cursor_for_next_repetition, - } - } -} - -impl Combinator for OneOrMoreCombinator { - fn next(&mut self) -> Option { - loop { - if let Some(cursor_for_next_repetition) = self.cursor_for_next_repetition.take() { - let next_child = self - .matcher - .child - .create_combinator(cursor_for_next_repetition); - self.children.push(next_child); - } else { - let tail = self.children.last_mut().unwrap(); - if let Some(cursor) = tail.next() { - if !cursor.is_completed() { - self.cursor_for_next_repetition = Some(cursor.clone()); - } - return Some(cursor); - } - self.children.pop(); - if self.children.is_empty() { - return None; - } - } - } - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - for child in &self.children { - child.accumulate_bindings(bindings); - } - } -} - -struct EllipsisCombinator { - cursor: Cursor, - has_returned_initial_empty_value: bool, -} - -impl EllipsisCombinator { - fn new(cursor: Cursor) -> Self { - Self { - cursor, - has_returned_initial_empty_value: false, - } - } -} - -impl Combinator for EllipsisCombinator { - fn next(&mut self) -> Option { - if !self.has_returned_initial_empty_value { - self.has_returned_initial_empty_value = true; - return Some(self.cursor.clone()); - } - - if self.cursor.irrevocably_go_to_next_sibling() { - return Some(self.cursor.clone()); - } - - None - } - - fn accumulate_bindings(&self, _bindings: &mut HashMap>) {} -} diff --git a/crates/codegen/runtime/cargo/src/runtime/query/generated/user_defined_queries.rs b/crates/codegen/runtime/cargo/src/runtime/query/generated/user_defined_queries.rs deleted file mode 100644 index be909afcf2..0000000000 --- a/crates/codegen/runtime/cargo/src/runtime/query/generated/user_defined_queries.rs +++ /dev/null @@ -1,3 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -pub(crate) trait UserDefinedQueries {} diff --git a/crates/codegen/runtime/cargo/src/runtime/query/mod.rs b/crates/codegen/runtime/cargo/src/runtime/query/mod.rs deleted file mode 100644 index 2abfca13d5..0000000000 --- a/crates/codegen/runtime/cargo/src/runtime/query/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod engine; -mod model; -mod parser; - -#[path = "generated/user_defined_queries.rs"] -mod user_defined_queries; - -pub use engine::{QueryResult, QueryResultIterator}; -pub use model::Query; -pub(crate) use user_defined_queries::UserDefinedQueries; diff --git a/crates/codegen/runtime/cargo/src/runtime/query/user_defined_queries.rs.jinja2 b/crates/codegen/runtime/cargo/src/runtime/query/user_defined_queries.rs.jinja2 deleted file mode 100644 index e725cf0b9b..0000000000 --- a/crates/codegen/runtime/cargo/src/runtime/query/user_defined_queries.rs.jinja2 +++ /dev/null @@ -1,16 +0,0 @@ -pub(crate) trait UserDefinedQueries { - {% if not rendering_in_stubs %} - {%- for query, captures in model.queries -%} - {# - # TODO(#554): not sure if this type is expected to be side-effect free implementation, - # or is it going to carry an incremental state. We can add other callbacks as needed. - #} - - fn {{ query }}( - {%- for capture in captures -%} - {{ capture }}: &crate::cursor::Cursor, - {%- endfor -%} - ); - {%- endfor -%} - {% endif %} -} diff --git a/crates/codegen/runtime/cargo/src/user_defined/mod.rs b/crates/codegen/runtime/cargo/src/user_defined/mod.rs deleted file mode 100644 index 67350db2a2..0000000000 --- a/crates/codegen/runtime/cargo/src/user_defined/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod query; diff --git a/crates/codegen/runtime/cargo/src/user_defined/query.rs b/crates/codegen/runtime/cargo/src/user_defined/query.rs deleted file mode 100644 index c2bf9fb672..0000000000 --- a/crates/codegen/runtime/cargo/src/user_defined/query.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::query::UserDefinedQueries; - -pub struct UserDefinedQueriesImpl; - -impl UserDefinedQueries for UserDefinedQueriesImpl { - // Empty Stub -} diff --git a/crates/codegen/runtime/generator/src/model.rs b/crates/codegen/runtime/generator/src/model.rs index 379f9070a6..f6d17f6bcf 100644 --- a/crates/codegen/runtime/generator/src/model.rs +++ b/crates/codegen/runtime/generator/src/model.rs @@ -10,7 +10,6 @@ use codegen_grammar::{ TriviaParserDefinitionRef, }; use codegen_language_definition::model::Language; -use indexmap::IndexMap; use quote::{format_ident, quote}; use semver::Version; use serde::Serialize; @@ -41,8 +40,6 @@ pub struct RuntimeModel { ast: AstModel, - queries: IndexMap>, - #[serde(skip)] top_level_scanner_names: BTreeSet<&'static str>, #[serde(skip)] @@ -75,15 +72,6 @@ impl RuntimeModel { // TODO(#638): Absorb the relevant fields into the model tree after migration is complete: model.all_versions = language.versions.iter().cloned().collect(); model.ast = AstModel::create(language); - model - .queries - .extend(language.queries.keys().enumerate().map(|(index, key)| { - // TODO(#554): parse the query and extract the real captures: - ( - key.to_string(), - ["foo", "bar", "baz"].into_iter().take(index + 1).collect(), - ) - })); model } diff --git a/crates/codegen/runtime/node_addon/Cargo.toml b/crates/codegen/runtime/node_addon/Cargo.toml index 16b5731253..d3a6a6a9a2 100644 --- a/crates/codegen/runtime/node_addon/Cargo.toml +++ b/crates/codegen/runtime/node_addon/Cargo.toml @@ -35,9 +35,9 @@ napi-build = { workspace = true } [dependencies] ariadne = { workspace = true } +metaslang_cst = { workspace = true } napi = { workspace = true } napi-derive = { workspace = true } -nom = { workspace = true } semver = { workspace = true } serde = { workspace = true } serde_json = { workspace = true, optional = true } diff --git a/crates/infra/cli/src/commands/publish/cargo/mod.rs b/crates/infra/cli/src/commands/publish/cargo/mod.rs index 53028bd33f..ff10d57c46 100644 --- a/crates/infra/cli/src/commands/publish/cargo/mod.rs +++ b/crates/infra/cli/src/commands/publish/cargo/mod.rs @@ -11,78 +11,111 @@ use itertools::Itertools; use crate::commands::publish::DryRun; -const USER_FACING_CRATE: &str = "slang_solidity"; +const USER_FACING_CRATES: &[&str] = &[ + // Sorted by dependency order (from dependencies to dependents): + "metaslang_cst", + "slang_solidity", +]; pub fn publish_cargo(dry_run: DryRun) -> Result<()> { - let local_version = CargoWorkspace::local_version()?; - println!("Local version: {local_version}"); - - let published_version = CargoWorkspace::published_version(USER_FACING_CRATE)?; - println!("Published version: {published_version}"); - - if local_version == published_version { - println!("Skipping crate, since the local version is already published."); - return Ok(()); - } - - let crate_dir = &CargoWorkspace::locate_source_crate(USER_FACING_CRATE)?; - let mut changeset = TemporaryChangeset::new( "infra/cargo-publish", "prepare Cargo packages for publishing", )?; - { - let cargo_toml = crate_dir.join("Cargo.toml"); - strip_publish_markers(&cargo_toml)?; - changeset.expect_change(&cargo_toml); - } + let mut changed_crates = vec![]; - { - let build_rs = crate_dir.join("build.rs"); - std::fs::remove_file(&build_rs)?; - changeset.expect_change(&build_rs); + for crate_name in USER_FACING_CRATES { + if prepare_for_publish(crate_name, &mut changeset)? { + changed_crates.push(crate_name); + } } - { - update_cargo_lock()?; - changeset.expect_change(Path::repo_path("Cargo.lock")); + if changed_crates.is_empty() { + println!("No crates to publish."); + return Ok(()); } + update_cargo_lock(&mut changeset)?; + changeset.commit_changes()?; - run_cargo_publish(dry_run)?; + for crate_name in &changed_crates { + run_cargo_publish(crate_name, dry_run)?; + } changeset.revert_changes()?; Ok(()) } -fn strip_publish_markers(cargo_toml: &Path) -> Result<()> { - let contents = std::fs::read_to_string(cargo_toml)?; +fn prepare_for_publish(crate_name: &str, changeset: &mut TemporaryChangeset) -> Result { + if let Ok(published_version) = CargoWorkspace::published_version(crate_name) { + println!("Published version of {crate_name}: {published_version}"); + + let local_version = CargoWorkspace::local_version()?; + println!("Local version: {local_version}"); - let contents = contents + if local_version == published_version { + println!("Skipping crate {crate_name}, since the local version is already published."); + return Ok(false); + } + } else { + println!("No published version found for crate {crate_name}."); + } + + let crate_dir = &CargoWorkspace::locate_source_crate(crate_name)?; + + let cargo_toml = crate_dir.join("Cargo.toml"); + if strip_publish_markers(&cargo_toml)? { + changeset.expect_change(&cargo_toml); + } + + let build_rs = crate_dir.join("build.rs"); + if build_rs.exists() { + std::fs::remove_file(&build_rs)?; + changeset.expect_change(&build_rs); + } + + Ok(true) +} + +fn strip_publish_markers(cargo_toml_path: &Path) -> Result { + let old_contents = std::fs::read_to_string(cargo_toml_path)?; + + let new_contents = old_contents .lines() .filter(|line| !line.contains("__REMOVE_THIS_LINE_DURING_CARGO_PUBLISH__")) .chain(once("")) .join("\n"); - std::fs::write(cargo_toml, contents)?; + let contents_changed = new_contents != old_contents; + if contents_changed { + std::fs::write(cargo_toml_path, new_contents)?; + } - Ok(()) + Ok(contents_changed) } -fn update_cargo_lock() -> Result<()> { - Command::new("cargo") - .arg("check") - .property("--package", USER_FACING_CRATE) - .run() +fn update_cargo_lock(changeset: &mut TemporaryChangeset) -> Result<()> { + let cargo_lock_path = Path::repo_path("Cargo.lock"); + let old_contents = std::fs::read_to_string(&cargo_lock_path)?; + + Command::new("cargo").arg("check").run()?; + + let new_contents = std::fs::read_to_string(&cargo_lock_path)?; + + if new_contents != old_contents { + changeset.expect_change(&cargo_lock_path); + } + + Ok(()) } -fn run_cargo_publish(dry_run: DryRun) -> Result<()> { +fn run_cargo_publish(crate_name: &str, dry_run: DryRun) -> Result<()> { let mut command = Command::new("cargo") .arg("publish") - .property("--package", USER_FACING_CRATE) + .property("--package", crate_name) .flag("--all-features"); if dry_run.is_yes() || !GitHub::is_running_in_ci() { diff --git a/crates/infra/utils/src/git/mod.rs b/crates/infra/utils/src/git/mod.rs index c2403a45c7..d628d1774b 100644 --- a/crates/infra/utils/src/git/mod.rs +++ b/crates/infra/utils/src/git/mod.rs @@ -44,6 +44,11 @@ impl TemporaryChangeset { self.expected_changes.iter().sorted().collect_vec(), ); + if self.expected_changes.is_empty() { + println!("No changes to commit, no need to create a branch."); + return Ok(()); + } + Command::new("git") .arg("checkout") .property("-b", &self.head_branch) @@ -67,6 +72,10 @@ impl TemporaryChangeset { } pub fn revert_changes(&self) -> Result<()> { + if self.expected_changes.is_empty() { + return Ok(()); + } + let current_branch = get_current_branch()?; assert_eq!(current_branch, self.head_branch); diff --git a/crates/metaslang/cst/CHANGELOG.md b/crates/metaslang/cst/CHANGELOG.md new file mode 100644 index 0000000000..e26ad44d42 --- /dev/null +++ b/crates/metaslang/cst/CHANGELOG.md @@ -0,0 +1,318 @@ +# changelog + +## 0.14.2 + +### Patch Changes + +- [#948](https://github.com/NomicFoundation/slang/pull/948) [`ce88cb7`](https://github.com/NomicFoundation/slang/commit/ce88cb7a6fd945b59ccc967cfd20f423dadc36fc) Thanks [@Xanewok](https://github.com/Xanewok)! - Restrict the grammar to correctly only allow an identifier in Yul variable declaration + +- [#945](https://github.com/NomicFoundation/slang/pull/945) [`e8f80d8`](https://github.com/NomicFoundation/slang/commit/e8f80d867b4b9d02413f42a8ece2630a43bc7494) Thanks [@Xanewok](https://github.com/Xanewok)! - Support `.address` built-in access in Yul paths + +## 0.14.1 + +### Patch Changes + +- [#943](https://github.com/NomicFoundation/slang/pull/943) [`a561fb1`](https://github.com/NomicFoundation/slang/commit/a561fb161eb7c18c838c85f71d132764d1d04050) Thanks [@Xanewok](https://github.com/Xanewok)! - Support Solidity 0.8.25 + +## 0.14.0 + +### Minor Changes + +- [#753](https://github.com/NomicFoundation/slang/pull/753) [`b35c763`](https://github.com/NomicFoundation/slang/commit/b35c7630ab7240304e67a43734700cf359acde0b) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Add tree query implementation as `Query::parse` and `Cursor::query` + +- [#755](https://github.com/NomicFoundation/slang/pull/755) [`8c260fc`](https://github.com/NomicFoundation/slang/commit/8c260fcb7e3111191cd33dd527817fb51119eac4) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - support parsing NatSpec comments + +- [#908](https://github.com/NomicFoundation/slang/pull/908) [`ab3688b`](https://github.com/NomicFoundation/slang/commit/ab3688bb99a60862c506566ac6122cd9c1155c57) Thanks [@Xanewok](https://github.com/Xanewok)! - Changed the cst.NodeType in TS to use more descriptive string values rather than 0/1 integers + +- [#886](https://github.com/NomicFoundation/slang/pull/886) [`0125717`](https://github.com/NomicFoundation/slang/commit/0125717fb0b48a5342a8452f18080db13e68fb6b) Thanks [@Xanewok](https://github.com/Xanewok)! - Add `TokenKind::is_trivia` + +- [#887](https://github.com/NomicFoundation/slang/pull/887) [`dff1201`](https://github.com/NomicFoundation/slang/commit/dff12011c549d68b20ecd54251af764643fb72db) Thanks [@Xanewok](https://github.com/Xanewok)! - Add support for constant function modifier removed in 0.5.0 + +- [#885](https://github.com/NomicFoundation/slang/pull/885) [`a9bd8da`](https://github.com/NomicFoundation/slang/commit/a9bd8da018469739832f71e38437caa83087baf0) Thanks [@Xanewok](https://github.com/Xanewok)! - Flatten the trivia syntax nodes into sibling tokens + +- [#908](https://github.com/NomicFoundation/slang/pull/908) [`ab3688b`](https://github.com/NomicFoundation/slang/commit/ab3688bb99a60862c506566ac6122cd9c1155c57) Thanks [@Xanewok](https://github.com/Xanewok)! - Add RuleNode/TokenNode::toJSON() in the TypeScript API + +### Patch Changes + +- [#801](https://github.com/NomicFoundation/slang/pull/801) [`ecbba49`](https://github.com/NomicFoundation/slang/commit/ecbba49c7ac25e37b8d317fb60fab7340c0628a5) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - unreserve pragma keywords in all versions + +- [#869](https://github.com/NomicFoundation/slang/pull/869) [`951b58d`](https://github.com/NomicFoundation/slang/commit/951b58ddb3eaea600ddf44427a82649761c6b651) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - support dots in yul identifiers from `0.5.8` till `0.7.0` + +- [#890](https://github.com/NomicFoundation/slang/pull/890) [`1ff8599`](https://github.com/NomicFoundation/slang/commit/1ff85993f25d92b38d0a500baa6ee48669a1b62a) Thanks [@Xanewok](https://github.com/Xanewok)! - Mark `override` as being a valid attribute only after 0.6.0 + +- [#800](https://github.com/NomicFoundation/slang/pull/800) [`d1827ff`](https://github.com/NomicFoundation/slang/commit/d1827ff7e1010493ff5487532a5ee0c77d355aa2) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - support unicode characters in string literals up to `0.7.0` + +- [#797](https://github.com/NomicFoundation/slang/pull/797) [`86f36d7`](https://github.com/NomicFoundation/slang/commit/86f36d71e60a44261ec114339e931dd3d24cd4a4) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - fix source locations for unicode characters in error reports + +- [#854](https://github.com/NomicFoundation/slang/pull/854) [`4b8970b`](https://github.com/NomicFoundation/slang/commit/4b8970b47ef7a2d1d51339cf5020a3e0f168b9aa) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - parse line breaks without newlines + +- [#844](https://github.com/NomicFoundation/slang/pull/844) [`f62de9e`](https://github.com/NomicFoundation/slang/commit/f62de9ea3fc2049ee11e5dbeff3dc51eb1ca984e) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - fix parsing empty `/**/` comments + +- [#799](https://github.com/NomicFoundation/slang/pull/799) [`303dda9`](https://github.com/NomicFoundation/slang/commit/303dda95c08b20450d03116765c210ece64a0864) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - prevent parsing multiple literals under `StringExpression` before `0.5.14` + +- [#847](https://github.com/NomicFoundation/slang/pull/847) [`6b6f260`](https://github.com/NomicFoundation/slang/commit/6b6f2603e3ba07c0a7dede0f96082369dc1df940) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - prioritize parsing `MultiLineComment` over `MultiLineNatSpecComment` + +- [#796](https://github.com/NomicFoundation/slang/pull/796) [`59e1e53`](https://github.com/NomicFoundation/slang/commit/59e1e53e7efa52355c273d7cef1a3974de13d88d) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - add `public` and `internal` to `UnnamedFunctionAttribute` till `0.5.0` + +- [#756](https://github.com/NomicFoundation/slang/pull/756) [`e839817`](https://github.com/NomicFoundation/slang/commit/e8398173f62d48596669628afc7c8b3572a15291) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - fix parsing `payable` primary expressions + +- [#851](https://github.com/NomicFoundation/slang/pull/851) [`67dfde8`](https://github.com/NomicFoundation/slang/commit/67dfde81a6d00101a9ed133104f15da5d46662b6) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - fix selection order of prefix/postfix AST fields + +- [#857](https://github.com/NomicFoundation/slang/pull/857) [`f677d5e`](https://github.com/NomicFoundation/slang/commit/f677d5eff40c4bfcf1db2fc4e63cdf37457fe467) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - rename `FieldName` to `NodeLabel` + +- [#852](https://github.com/NomicFoundation/slang/pull/852) [`ca79eca`](https://github.com/NomicFoundation/slang/commit/ca79ecaa522e531420b42ffba67da192c1e5fdb2) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - allow parsing `ColonEqual` as two separate tokens before `0.5.5` + +- [#889](https://github.com/NomicFoundation/slang/pull/889) [`ce5050f`](https://github.com/NomicFoundation/slang/commit/ce5050f95195fdd018a38a0351d8525f7d62073a) Thanks [@Xanewok](https://github.com/Xanewok)! - Support `delete` as an expression rather than a statement + +- [#923](https://github.com/NomicFoundation/slang/pull/923) [`bb30fc1`](https://github.com/NomicFoundation/slang/commit/bb30fc1e28a0fe806f8954a0d2779d903f3f4da7) Thanks [@Xanewok](https://github.com/Xanewok)! - Support arbitrary ASCII escape sequences in string literals until 0.4.25 + +- [#887](https://github.com/NomicFoundation/slang/pull/887) [`dff1201`](https://github.com/NomicFoundation/slang/commit/dff12011c549d68b20ecd54251af764643fb72db) Thanks [@Xanewok](https://github.com/Xanewok)! - Support view and pure function modifiers only from 0.4.16 + +- [#800](https://github.com/NomicFoundation/slang/pull/800) [`d1827ff`](https://github.com/NomicFoundation/slang/commit/d1827ff7e1010493ff5487532a5ee0c77d355aa2) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - rename `AsciiStringLiteral` to `StringLiteral` + +- [#838](https://github.com/NomicFoundation/slang/pull/838) [`ad98d1c`](https://github.com/NomicFoundation/slang/commit/ad98d1c7d9f9f7cb12b4b6184c04c9b680e6d70a) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - upgrade to rust `1.76.0` + +- [#849](https://github.com/NomicFoundation/slang/pull/849) [`5c42e0e`](https://github.com/NomicFoundation/slang/commit/5c42e0ef5f3afe0355614967cb6d2daa31518ccf) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - add `override` and `virtual` to `ConstructorAttribute` + +- [#862](https://github.com/NomicFoundation/slang/pull/862) [`5e37ea0`](https://github.com/NomicFoundation/slang/commit/5e37ea0c40e929e0888b6297fa6dd92952d9cd73) Thanks [@Xanewok](https://github.com/Xanewok)! - allow call options as a post-fix expression + +- [#786](https://github.com/NomicFoundation/slang/pull/786) [`0bfa6b7`](https://github.com/NomicFoundation/slang/commit/0bfa6b7397cd25aca713b30628c6d06e761b416a) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - support Yul label statements before `0.5.0` + +- [#839](https://github.com/NomicFoundation/slang/pull/839) [`2d698eb`](https://github.com/NomicFoundation/slang/commit/2d698ebe469110b85f539d6e0c75b503cd4ce57e) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - support string literals in version pragmas + +- [#891](https://github.com/NomicFoundation/slang/pull/891) [`70c9d7d`](https://github.com/NomicFoundation/slang/commit/70c9d7deebddb0f22114b7b05ddc85da6dcceaaf) Thanks [@Xanewok](https://github.com/Xanewok)! - Fix parsing `.member` member access expression + +- [#842](https://github.com/NomicFoundation/slang/pull/842) [`2069126`](https://github.com/NomicFoundation/slang/commit/20691263fb6967195bee30fba92abdfb06daa6fa) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - add `private` to `UnnamedFunctionAttribute` till `0.5.0` + +- [#840](https://github.com/NomicFoundation/slang/pull/840) [`7fb0d20`](https://github.com/NomicFoundation/slang/commit/7fb0d20655024daf71c872a6ef95aa30277a1366) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - allow `var` in `TupleDeconstructionStatement` before `0.5.0` + +## 0.13.1 + +### Patch Changes + +- [#748](https://github.com/NomicFoundation/slang/pull/748) [`c289cbf7`](https://github.com/NomicFoundation/slang/commit/c289cbf7e22118881818b82d0ffc5933a424a7aa) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - Properly parse EVM built-ins up till Paris/Solidity 0.8.18 + +## 0.13.0 + +### Minor Changes + +- [#710](https://github.com/NomicFoundation/slang/pull/710) [`2025b6cb`](https://github.com/NomicFoundation/slang/commit/2025b6cb23dc320b413b482ed1fe8455229b7d84) Thanks [@Xanewok](https://github.com/Xanewok)! - CST children nodes are now named + +- [#723](https://github.com/NomicFoundation/slang/pull/723) [`b3dc6bcd`](https://github.com/NomicFoundation/slang/commit/b3dc6bcdc1834d266a87d483927894617bf8e817) Thanks [@Xanewok](https://github.com/Xanewok)! - Properly parse unreserved keywords in an identifier position, i.e. `from`, `emit`, `global` etc. + +- [#728](https://github.com/NomicFoundation/slang/pull/728) [`662a672c`](https://github.com/NomicFoundation/slang/commit/662a672cd661b9f1bf4c18587acf68111fd1f2e8) Thanks [@Xanewok](https://github.com/Xanewok)! - Remove Language#scan API; use the parser API instead + +- [#719](https://github.com/NomicFoundation/slang/pull/719) [`1ad6bb37`](https://github.com/NomicFoundation/slang/commit/1ad6bb37337aa28d9344380c5c9eb1945e908271) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - introduce strong types for all Solidity non terminals in the TypeScript API. + +### Patch Changes + +- [#719](https://github.com/NomicFoundation/slang/pull/719) [`1ad6bb37`](https://github.com/NomicFoundation/slang/commit/1ad6bb37337aa28d9344380c5c9eb1945e908271) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - unify Rust/TypeScript node helpers: `*_with_kind()`, `*_with_kinds()`, `*_is_kind()`), ... + +- [#731](https://github.com/NomicFoundation/slang/pull/731) [`3deaea2e`](https://github.com/NomicFoundation/slang/commit/3deaea2eb82ce33dbccc54d1a79b9cf5657385ac) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - add `RuleNode.unparse()` to the TypeScript API + +## 0.12.0 + +### Minor Changes + +- [#699](https://github.com/NomicFoundation/slang/pull/699) [`ddfebfe9`](https://github.com/NomicFoundation/slang/commit/ddfebfe988345136007431f8ea2efac19fd7e887) Thanks [@Xanewok](https://github.com/Xanewok)! - Remove `ProductionKind` in favor of `RuleKind` + +- [#699](https://github.com/NomicFoundation/slang/pull/699) [`ddfebfe9`](https://github.com/NomicFoundation/slang/commit/ddfebfe988345136007431f8ea2efac19fd7e887) Thanks [@Xanewok](https://github.com/Xanewok)! - Allow parsing individual precedence expressions, like `ShiftExpression` + +- [#665](https://github.com/NomicFoundation/slang/pull/665) [`4b5f8b46`](https://github.com/NomicFoundation/slang/commit/4b5f8b467d4cbab72cf27a539bb5ca8c71090dd6) Thanks [@Xanewok](https://github.com/Xanewok)! - Remove the CST Visitor API in favor of the Cursor API + +- [#666](https://github.com/NomicFoundation/slang/pull/666) [`0434b68c`](https://github.com/NomicFoundation/slang/commit/0434b68c9ef9cd1d1dcc07d7ed50e6d63645319b) Thanks [@Xanewok](https://github.com/Xanewok)! - Add `Node::unparse()` that allows to reconstruct the source code from the CST node + +- [#675](https://github.com/NomicFoundation/slang/pull/675) [`daea4b7f`](https://github.com/NomicFoundation/slang/commit/daea4b7f954ff1e918b9191aff40ee95c10a4db2) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - rename `Cursor`'s `pathRuleNodes()` to `ancestors()` in the NodeJS API. + +- [#676](https://github.com/NomicFoundation/slang/pull/676) [`b496d361`](https://github.com/NomicFoundation/slang/commit/b496d36120700347bcbcc25b948eb46814fd5412) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - Fix NAPI `cursor` types and expose `cursor.depth`. + +### Patch Changes + +- [#685](https://github.com/NomicFoundation/slang/pull/685) [`b5fca94a`](https://github.com/NomicFoundation/slang/commit/b5fca94af917a2f0418c224b3101885c02e5cb9c) Thanks [@Xanewok](https://github.com/Xanewok)! - `bytes` is now properly recognized as a reserved word + +- [#660](https://github.com/NomicFoundation/slang/pull/660) [`97028991`](https://github.com/NomicFoundation/slang/commit/9702899164f0540a49f2e0f7f19d82fbd04b1d1b) Thanks [@Xanewok](https://github.com/Xanewok)! - Drop List suffix from collection grammar rule names + +## 0.11.0 + +### Minor Changes + +- [#625](https://github.com/NomicFoundation/slang/pull/625) [`7bb650b`](https://github.com/NomicFoundation/slang/commit/7bb650b12ae793a318dc5b7839fb93915c88828e) Thanks [@Xanewok](https://github.com/Xanewok)! - The CST Cursor now implements the Iterator trait as part of the Rust API + +- [#647](https://github.com/NomicFoundation/slang/pull/647) [`b1dced3`](https://github.com/NomicFoundation/slang/commit/b1dced355ce83f3bd858c02837d67665f7ef281d) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - Require specifying an initial offset when creating a CST cursor. + +### Patch Changes + +- [#648](https://github.com/NomicFoundation/slang/pull/648) [`2327bf5`](https://github.com/NomicFoundation/slang/commit/2327bf5d8c40d85edd0cc80fe9e36d367a1a3336) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - Support Solidity v0.8.22. + +- [#623](https://github.com/NomicFoundation/slang/pull/623) [`80114a8`](https://github.com/NomicFoundation/slang/commit/80114a833dc8249447c382bf457215b1a4d9e5ae) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Correct the types in the TS api by adding the correct namespaces to type references + +## 0.10.1 + +### Patch Changes + +- [#615](https://github.com/NomicFoundation/slang/pull/615) [`06cbbe8`](https://github.com/NomicFoundation/slang/commit/06cbbe88bc68928ad44046a96c31ad6e53fbf76c) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - `cursor` method is now exposed in Typescript API + +## 0.10.0 + +### Minor Changes + +- [#595](https://github.com/NomicFoundation/slang/pull/595) [`1a258c4`](https://github.com/NomicFoundation/slang/commit/1a258c49432eac06dac7055bc427e68af1fa3875) Thanks [@Xanewok](https://github.com/Xanewok)! - Attempt error recovery when parsing incomplete lists + +- [#564](https://github.com/NomicFoundation/slang/pull/564) [`e326a06`](https://github.com/NomicFoundation/slang/commit/e326a064da559c974fbb7a199090e9e5a313abb8) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Parsing operators with missing operands should no longer panic + +- [#564](https://github.com/NomicFoundation/slang/pull/564) [`e326a06`](https://github.com/NomicFoundation/slang/commit/e326a064da559c974fbb7a199090e9e5a313abb8) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Inline parse rules are no longer exposed to the API. + +- [#564](https://github.com/NomicFoundation/slang/pull/564) [`e326a06`](https://github.com/NomicFoundation/slang/commit/e326a064da559c974fbb7a199090e9e5a313abb8) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Scanners are no longer available as methods - use next_token instead + +- [#564](https://github.com/NomicFoundation/slang/pull/564) [`e326a06`](https://github.com/NomicFoundation/slang/commit/e326a064da559c974fbb7a199090e9e5a313abb8) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Scanners are now grouped into contexts to deal with contextual scanning + +### Patch Changes + +- [#601](https://github.com/NomicFoundation/slang/pull/601) [`cbd2a79`](https://github.com/NomicFoundation/slang/commit/cbd2a79658849c0029bb6a5ccc0b086564c28fe0) Thanks [@Xanewok](https://github.com/Xanewok)! - Attempt parser error recovery between bracket delimiters + +- [#599](https://github.com/NomicFoundation/slang/pull/599) [`4bbad48`](https://github.com/NomicFoundation/slang/commit/4bbad48d45ae7bde8a22198b33f790b7c792b319) Thanks [@Xanewok](https://github.com/Xanewok)! - Use correct versions for the `revert` and `global` keywords + +- [#561](https://github.com/NomicFoundation/slang/pull/561) [`cb6a138`](https://github.com/NomicFoundation/slang/commit/cb6a1384cb6f04926def3e4c1fe7a0b12a67143c) Thanks [@Xanewok](https://github.com/Xanewok)! - Add preliminary documentation for the `solidity_language` Rust package + +- [#603](https://github.com/NomicFoundation/slang/pull/603) [`be59a10`](https://github.com/NomicFoundation/slang/commit/be59a10c937542f0413a34fd84d84ec4d4400f6d) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - upgrade to rust 1.72.0 + +## 0.9.0 + +### Minor Changes + +- [#540](https://github.com/NomicFoundation/slang/pull/540) [`0d04f95`](https://github.com/NomicFoundation/slang/commit/0d04f959bf1f5831c912d5109de3d933cfaa6266) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Add a Rust Cursor API and refactor the Rust Visitor API to run on top of it. + +- [#540](https://github.com/NomicFoundation/slang/pull/540) [`0d04f95`](https://github.com/NomicFoundation/slang/commit/0d04f959bf1f5831c912d5109de3d933cfaa6266) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Move Visitor et al to node:: namespace, which is where Cursor is. + +- [#540](https://github.com/NomicFoundation/slang/pull/540) [`0d04f95`](https://github.com/NomicFoundation/slang/commit/0d04f959bf1f5831c912d5109de3d933cfaa6266) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Rename `range` functions that return a TextRange to `text_range` + +### Patch Changes + +- [#543](https://github.com/NomicFoundation/slang/pull/543) [`7a34599`](https://github.com/NomicFoundation/slang/commit/7a34599f6b237b03a0f8ba92755cae6107589e37) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - Move `syntax::parser::ProductionKind` to `syntax::nodes` namespace. + +- [#540](https://github.com/NomicFoundation/slang/pull/540) [`0d04f95`](https://github.com/NomicFoundation/slang/commit/0d04f959bf1f5831c912d5109de3d933cfaa6266) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Add TokenNode.text to the TS API. + +- [#540](https://github.com/NomicFoundation/slang/pull/540) [`0d04f95`](https://github.com/NomicFoundation/slang/commit/0d04f959bf1f5831c912d5109de3d933cfaa6266) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Add first pass of Typescript binding to the Cursor API, but no TS Visitor yet. + +- [#545](https://github.com/NomicFoundation/slang/pull/545) [`e73658a`](https://github.com/NomicFoundation/slang/commit/e73658ae4e777e78a01e213f213e2a5dc13e5cba) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - render EBNF grammar on top of each `ProductionKind`, `RuleKind`, and `TokenKind`. + +- [#558](https://github.com/NomicFoundation/slang/pull/558) [`95bbc50`](https://github.com/NomicFoundation/slang/commit/95bbc5025fbf63b8d4e07f7652a70a7f66363db6) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - Correct versioning for `SourceUnitMember` and `ContractMember` children. + +## 0.8.0 + +### Minor Changes + +- [#513](https://github.com/NomicFoundation/slang/pull/513) [`7e01250`](https://github.com/NomicFoundation/slang/commit/7e012501c04e639b54cd150e3736683ee2c2606f) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Typescript API now has TextIndex and TextRange types that are returned from the appropriate methods rather than tuples. + +### Patch Changes + +- [#527](https://github.com/NomicFoundation/slang/pull/527) [`7ccca87`](https://github.com/NomicFoundation/slang/commit/7ccca87beaa9cb96ad294d1af8a02f115481b71a) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Fix pratt parser behavior in the face of error correction +- [#531](https://github.com/NomicFoundation/slang/pull/531) [`e3450be4`](https://github.com/NomicFoundation/slang/commit/e3450be4722845bcfce7a9ec3b3046ba6eb6961d) Thanks [@alcuadrado](https://github.com/alcuadrado)! - Make ESM named imports work in Node.js. + +## 0.7.0 + +### Minor Changes + +- [#502](https://github.com/NomicFoundation/slang/pull/502) [`c383238`](https://github.com/NomicFoundation/slang/commit/c383238c1f51157b37ec63bc99e63fb85c1bc224) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Added error recovery i.e. a CST is _always_ produced, even if there are errors. The erroneous/skipped text is in the CST as a `TokenKind::SKIPPED` token. + +- [#501](https://github.com/NomicFoundation/slang/pull/501) [`cb221fe`](https://github.com/NomicFoundation/slang/commit/cb221fed784e8a2eb59f17907412149c7b415ed8) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - generate typescript string enums for CST kinds + +- [#517](https://github.com/NomicFoundation/slang/pull/517) [`8bd5446`](https://github.com/NomicFoundation/slang/commit/8bd544695a6dd4880a00d0cecf8d13ad79b238d3) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - extract inlined and sub-expressions in language grammar + +- [#518](https://github.com/NomicFoundation/slang/pull/518) [`b3b562b`](https://github.com/NomicFoundation/slang/commit/b3b562be6365fab25b97e54746a7500b9e7bd595) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - fill in missing CST node names + +- [#515](https://github.com/NomicFoundation/slang/pull/515) [`f24e873`](https://github.com/NomicFoundation/slang/commit/f24e873a93cbcef53aad1fa5eed1ea9ab1af1c04) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - switch over the NPM package to use CommonJS modules instead of ES modules. + +- [#498](https://github.com/NomicFoundation/slang/pull/498) [`44f1ff7`](https://github.com/NomicFoundation/slang/commit/44f1ff70100d6e2f8afe54c7ff87e24a8479e4b9) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - flatten unnamed CST nodes into parent nodes + +- [#502](https://github.com/NomicFoundation/slang/pull/502) [`c383238`](https://github.com/NomicFoundation/slang/commit/c383238c1f51157b37ec63bc99e63fb85c1bc224) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Use the Rowan model for the CST i.e. TokenNodes contain the string content, and RuleNodes contain only the combined _length_ of their children's text. + +- [#499](https://github.com/NomicFoundation/slang/pull/499) [`1582d60`](https://github.com/NomicFoundation/slang/commit/1582d60c7ef81a785db0b9e3cb4d074d9cb6d442) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - preserve correct ranges on empty rule nodes + +- [#500](https://github.com/NomicFoundation/slang/pull/500) [`73ddac9`](https://github.com/NomicFoundation/slang/commit/73ddac9ca972f80aa9a0321de7f94c47b505d7a6) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - inlining CST nodes that offer no additional syntactic information + +- [#512](https://github.com/NomicFoundation/slang/pull/512) [`72dc3d3`](https://github.com/NomicFoundation/slang/commit/72dc3d3d90bc6a02d36836cc1fed17f5be5de2fb) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Expression productions now correctly wrap the recursive 'calls' in a rule node + +## 0.6.0 + +### Minor Changes + +- [#490](https://github.com/NomicFoundation/slang/pull/490) [`ea8e7e7`](https://github.com/NomicFoundation/slang/commit/ea8e7e771fef7fd9195bcc3004b08fc132c8990d) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - provide API to list supported language versions + +- [#489](https://github.com/NomicFoundation/slang/pull/489) [`15c34a7`](https://github.com/NomicFoundation/slang/commit/15c34a7bb0268bf26eaa6535dd637f73349596c8) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - replace panics with JS exceptions in npm package + +### Patch Changes + +- [#488](https://github.com/NomicFoundation/slang/pull/488) [`d7f171c`](https://github.com/NomicFoundation/slang/commit/d7f171cf1e2da375a7ededd034a62fc6b723d44d) Thanks [@DaniPopes](https://github.com/DaniPopes)! - introduce a `cli` Cargo feature to compile the CLI binary + +## 0.5.0 + +### Minor Changes + +- [#475](https://github.com/NomicFoundation/slang/pull/475) [`0cdfe86`](https://github.com/NomicFoundation/slang/commit/0cdfe86037bfe2f1f8be66a69e8e7d7bdbf06364) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - match TypeScript and Rust API namespaces + +- [#477](https://github.com/NomicFoundation/slang/pull/477) [`13c85a2`](https://github.com/NomicFoundation/slang/commit/13c85a2a3e4e97894d9f24a3e2886a08ffe6e569) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - move expression operators into separate nodes + +- [#481](https://github.com/NomicFoundation/slang/pull/481) [`0269f2b`](https://github.com/NomicFoundation/slang/commit/0269f2b9de3f6711055119e1f40c3f036fe3a81f) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - fix grammar versions of individual keywords + +- [#473](https://github.com/NomicFoundation/slang/pull/473) [`11d8cb0`](https://github.com/NomicFoundation/slang/commit/11d8cb0658e01f16b7afd808f31d1da88e967679) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - upgrade to rust 1.69.0 + +## 0.4.0 + +### Minor Changes + +- [#458](https://github.com/NomicFoundation/slang/pull/458) [`c0fc7e9`](https://github.com/NomicFoundation/slang/commit/c0fc7e95b87eb1ddca4f9e0003136fcbe74f5798) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Record both character and byte offsets for input positions + +- [#463](https://github.com/NomicFoundation/slang/pull/463) [`0958d6b`](https://github.com/NomicFoundation/slang/commit/0958d6baadba3295df9307e421ddd0a41ef3aaa0) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - use `number` and getters in npm public API + +## 0.3.0 + +### Minor Changes + +- [#457](https://github.com/NomicFoundation/slang/pull/457) [`b7aae2a`](https://github.com/NomicFoundation/slang/commit/b7aae2ad891f940ee764365ac12c75fd1cb47687) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - minor grammar fixes + +- [#453](https://github.com/NomicFoundation/slang/pull/453) [`0f2f9ab`](https://github.com/NomicFoundation/slang/commit/0f2f9abec3f2525640d25bf1f288b769917fbc61) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - move Rust's `syntax::Parser::Language` API to root module + +- [#454](https://github.com/NomicFoundation/slang/pull/454) [`85dec01`](https://github.com/NomicFoundation/slang/commit/85dec0196eafa337065233de03c30d36211b03cf) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - moving to Rust version 1.65.0 + +- [#456](https://github.com/NomicFoundation/slang/pull/456) [`c6d1041`](https://github.com/NomicFoundation/slang/commit/c6d10417da440f15e1c074b7d8b5d13d38e95519) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - expose `ParseError` API + +- [#451](https://github.com/NomicFoundation/slang/pull/451) [`78f633c`](https://github.com/NomicFoundation/slang/commit/78f633cb5977d358b4bcc468151359a4301089b2) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - rename `VisitorExitResponse::StepIn` to `VisitorExitResponse::Continue` + +## 0.2.1 + +### Patch Changes + +- [#444](https://github.com/NomicFoundation/slang/pull/444) [`a858e2c`](https://github.com/NomicFoundation/slang/commit/a858e2c842db3b2183821fb92ed26af6b433e823) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - Fix HexLiteral cannot have NumberUnit + +## 0.2.0 + +### Minor Changes + +- [#435](https://github.com/NomicFoundation/slang/pull/435) [`2a5b193`](https://github.com/NomicFoundation/slang/commit/2a5b1930b20024359fbaf06b6e9748585d7423ff) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - support user defined operators + +### Patch Changes + +- [#416](https://github.com/NomicFoundation/slang/pull/416) [`fb977a5`](https://github.com/NomicFoundation/slang/commit/fb977a52b152a1ce8d8ce92db4bb00fcfb5881c1) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - fix primary expressions parser order + +- [#434](https://github.com/NomicFoundation/slang/pull/434) [`beb3708`](https://github.com/NomicFoundation/slang/commit/beb3708218ec797614ba283a13f1854d5f3c7239) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - fix UnicodeStringLiteral versioning + +- [#430](https://github.com/NomicFoundation/slang/pull/430) [`8b7492e`](https://github.com/NomicFoundation/slang/commit/8b7492e65ec7261176e444dca2563a82603b43b0) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - update READMEs with links to packages and user guides. + +- [#425](https://github.com/NomicFoundation/slang/pull/425) [`9b49b3d`](https://github.com/NomicFoundation/slang/commit/9b49b3d827536e707d78a6bc349fc82698237b75) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - add user guides to rust crate and npm packages. + +- [#432](https://github.com/NomicFoundation/slang/pull/432) [`1d1a8bb`](https://github.com/NomicFoundation/slang/commit/1d1a8bb5503c510a470bb99a18632c3e51a587ec) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - fix FunctionCallOptions versioning + +- [#427](https://github.com/NomicFoundation/slang/pull/427) [`1103916`](https://github.com/NomicFoundation/slang/commit/11039163ac3a3b66a74fa85683bde1c380a519f4) Thanks [@AntonyBlakey](https://github.com/AntonyBlakey)! - fix VariableDeclarationStatement versioning + +## 0.1.1 + +### Patch Changes + +- [#412](https://github.com/NomicFoundation/slang/pull/412) [`9cac1a04`](https://github.com/NomicFoundation/slang/commit/9cac1a04670fa870c15cee1bd20e0e78c1d213db) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - publish npm packages + +## 0.1.0 + +### Minor Changes + +- [#396](https://github.com/NomicFoundation/slang/pull/396) [`621b338`](https://github.com/NomicFoundation/slang/commit/621b33838c74415c46ab157205068008e05c5b9b) Thanks [@OmarTawfik](https://github.com/OmarTawfik)! - Initial release. diff --git a/crates/metaslang/cst/Cargo.toml b/crates/metaslang/cst/Cargo.toml new file mode 100644 index 0000000000..c20cc088a2 --- /dev/null +++ b/crates/metaslang/cst/Cargo.toml @@ -0,0 +1,26 @@ +[package] +version.workspace = true +rust-version.workspace = true +edition.workspace = true +publish = true + +name = "metaslang_cst" +description = "A generic library for building, navigating and querying concrete syntax trees." +homepage = "https://nomicfoundation.github.io/slang/" +repository = "https://github.com/NomicFoundation/slang/" +authors = [ + "Nomic Foundation ", + "Antony Blakey ", + "Igor Matuszewski ", + "Omar Tawfik ", +] + +readme = "README.md" +license = "MIT" +keywords = ["parser"] +categories = ["compilers", "parsing", "parser-implementations"] + +[dependencies] +nom = { workspace = true } +serde = { workspace = true } +strum = { workspace = true } diff --git a/crates/metaslang/cst/LICENSE b/crates/metaslang/cst/LICENSE new file mode 100644 index 0000000000..51d1dca789 --- /dev/null +++ b/crates/metaslang/cst/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Nomic Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/metaslang/cst/README.md b/crates/metaslang/cst/README.md new file mode 100644 index 0000000000..fc08b57da2 --- /dev/null +++ b/crates/metaslang/cst/README.md @@ -0,0 +1,15 @@ +# metaslang_cst + + + +[![release](https://img.shields.io/github/v/tag/NomicFoundation/slang?label=GitHub%20Release&logo=github&sort=semver&logoColor=white)](https://github.com/NomicFoundation/slang/releases) +[![crate](https://img.shields.io/crates/v/metaslang_cst?label=Rust%20Crate&logo=rust&logoColor=white)](https://crates.io/crates/metaslang_cst) + +## Solidity compiler tooling by [@NomicFoundation](https://github.com/NomicFoundation) + +This crate provides support for parse trees used in the Solidity compiler tooling. + +
+ +> ❗ This project is still in alpha, and is under active development. +> If you are planning on using it, please reach out to us on [Telegram](https://t.me/+pxApdT-Ssn5hMTFh) so we can help you get started. diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/cst.rs b/crates/metaslang/cst/src/cst.rs similarity index 62% rename from crates/testlang/outputs/cargo/slang_testlang/src/generated/cst.rs rename to crates/metaslang/cst/src/cst.rs index bdc126ca60..27f4b67b9d 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/cst.rs +++ b/crates/metaslang/cst/src/cst.rs @@ -1,67 +1,65 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - use std::rc::Rc; use serde::Serialize; use crate::cursor::Cursor; -use crate::kinds::{NodeLabel, RuleKind, TokenKind}; use crate::text_index::TextIndex; +use crate::{KindTypes, TerminalKind as _}; #[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub struct LabeledNode { - pub label: Option, - pub node: Node, -} - -impl LabeledNode { - /// Creates an anonymous node (without a label). - pub fn anonymous(node: Node) -> Self { - Self { label: None, node } - } -} - -impl std::ops::Deref for LabeledNode { - type Target = Node; - - fn deref(&self) -> &Self::Target { - &self.node - } +pub struct TerminalNode { + pub kind: T::TerminalKind, + pub text: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub struct RuleNode { - pub kind: RuleKind, +pub struct NonTerminalNode { + pub kind: T::NonTerminalKind, pub text_len: TextIndex, #[serde(skip_serializing_if = "Vec::is_empty")] - pub children: Vec, + pub children: Vec>, } #[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub struct TokenNode { - pub kind: TokenKind, - pub text: String, +pub enum Node { + Rule(Rc>), + Token(Rc>), } #[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub enum Node { - Rule(Rc), - Token(Rc), +pub struct LabeledNode { + pub label: Option, + pub node: Node, +} + +impl LabeledNode { + /// Creates an anonymous node (without a label). + pub fn anonymous(node: Node) -> Self { + Self { label: None, node } + } +} + +impl std::ops::Deref for LabeledNode { + type Target = Node; + + fn deref(&self) -> &Self::Target { + &self.node + } } -impl Node { - pub fn rule(kind: RuleKind, children: Vec) -> Self { +impl Node { + pub fn rule(kind: T::NonTerminalKind, children: Vec>) -> Self { let text_len = children.iter().map(|node| node.text_len()).sum(); - Self::Rule(Rc::new(RuleNode { + Self::Rule(Rc::new(NonTerminalNode { kind, text_len, children, })) } - pub fn token(kind: TokenKind, text: String) -> Self { - Self::Token(Rc::new(TokenNode { kind, text })) + pub fn token(kind: T::TerminalKind, text: String) -> Self { + Self::Token(Rc::new(TerminalNode { kind, text })) } pub fn text_len(&self) -> TextIndex { @@ -72,7 +70,7 @@ impl Node { } /// Returns a slice of the children (not all descendants) of this node. - pub fn children(&self) -> &[LabeledNode] { + pub fn children(&self) -> &[LabeledNode] { match self { Self::Rule(node) => &node.children, Self::Token(_) => &[], @@ -80,7 +78,7 @@ impl Node { } /// Creates a [`Cursor`] that starts at the current node as the root and a given initial `text_offset`. - pub fn cursor_with_offset(&self, text_offset: TextIndex) -> Cursor { + pub fn cursor_with_offset(&self, text_offset: TextIndex) -> Cursor { Cursor::new(self.clone(), text_offset) } @@ -92,7 +90,7 @@ impl Node { } } - pub fn into_rule(self) -> Option> { + pub fn into_rule(self) -> Option>> { match self { Self::Rule(rule) => Some(rule), Self::Token(..) => None, @@ -103,30 +101,33 @@ impl Node { self.as_rule().is_some() } - pub fn as_rule(&self) -> Option<&Rc> { + pub fn as_rule(&self) -> Option<&Rc>> { match self { Self::Rule(rule) => Some(rule), Self::Token(..) => None, } } - pub fn is_rule_with_kind(&self, kind: RuleKind) -> bool { + pub fn is_rule_with_kind(&self, kind: T::NonTerminalKind) -> bool { self.as_rule_with_kind(kind).is_some() } - pub fn as_rule_with_kind(&self, kind: RuleKind) -> Option<&Rc> { + pub fn as_rule_with_kind(&self, kind: T::NonTerminalKind) -> Option<&Rc>> { self.as_rule().filter(|rule| rule.kind == kind) } - pub fn is_rule_with_kinds(&self, kinds: &[RuleKind]) -> bool { + pub fn is_rule_with_kinds(&self, kinds: &[T::NonTerminalKind]) -> bool { self.as_rule_with_kinds(kinds).is_some() } - pub fn as_rule_with_kinds(&self, kinds: &[RuleKind]) -> Option<&Rc> { + pub fn as_rule_with_kinds( + &self, + kinds: &[T::NonTerminalKind], + ) -> Option<&Rc>> { self.as_rule().filter(|rule| kinds.contains(&rule.kind)) } - pub fn into_token(self) -> Option> { + pub fn into_token(self) -> Option>> { match self { Self::Token(token) => Some(token), Self::Rule(..) => None, @@ -137,26 +138,26 @@ impl Node { self.as_token().is_some() } - pub fn as_token(&self) -> Option<&Rc> { + pub fn as_token(&self) -> Option<&Rc>> { match self { Self::Token(token) => Some(token), Self::Rule(..) => None, } } - pub fn is_token_with_kind(&self, kind: TokenKind) -> bool { + pub fn is_token_with_kind(&self, kind: T::TerminalKind) -> bool { self.as_token_with_kind(kind).is_some() } - pub fn as_token_with_kind(&self, kind: TokenKind) -> Option<&Rc> { + pub fn as_token_with_kind(&self, kind: T::TerminalKind) -> Option<&Rc>> { self.as_token().filter(|token| token.kind == kind) } - pub fn is_token_with_kinds(&self, kinds: &[TokenKind]) -> bool { + pub fn is_token_with_kinds(&self, kinds: &[T::TerminalKind]) -> bool { self.as_token_with_kinds(kinds).is_some() } - pub fn as_token_with_kinds(&self, kinds: &[TokenKind]) -> Option<&Rc> { + pub fn as_token_with_kinds(&self, kinds: &[T::TerminalKind]) -> Option<&Rc>> { self.as_token().filter(|token| kinds.contains(&token.kind)) } @@ -168,21 +169,21 @@ impl Node { } } -impl From> for Node { - fn from(node: Rc) -> Self { +impl From>> for Node { + fn from(node: Rc>) -> Self { Self::Rule(node) } } -impl From> for Node { - fn from(node: Rc) -> Self { +impl From>> for Node { + fn from(node: Rc>) -> Self { Self::Token(node) } } -impl RuleNode { +impl NonTerminalNode { /// Creates a [`Cursor`] that starts at the current node as the root and a given initial `text_offset`. - pub fn cursor_with_offset(self: Rc, text_offset: TextIndex) -> Cursor { + pub fn cursor_with_offset(self: Rc, text_offset: TextIndex) -> Cursor { Cursor::new(Node::Rule(self), text_offset) } diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/cursor.rs b/crates/metaslang/cst/src/cursor.rs similarity index 84% rename from crates/testlang/outputs/cargo/slang_testlang/src/generated/cursor.rs rename to crates/metaslang/cst/src/cursor.rs index 72f14dd431..2aae0b30c0 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/cursor.rs +++ b/crates/metaslang/cst/src/cursor.rs @@ -1,18 +1,16 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - //! A cursor that can traverse a CST in a DFS pre-order fashion. use std::rc::Rc; -use crate::cst::{LabeledNode, Node, RuleNode}; -use crate::kinds::{NodeLabel, RuleKind, TokenKind}; +use crate::cst::{LabeledNode, Node, NonTerminalNode}; use crate::text_index::{TextIndex, TextRange}; +use crate::KindTypes; /// A node in the ancestor path of a [`Cursor`]. #[derive(Clone, Debug, PartialEq, Eq)] -struct PathAncestor { - parent: Option>, - rule_node: Rc, +struct PathAncestor { + parent: Option>>, + rule_node: Rc>, child_number: usize, text_offset: TextIndex, } @@ -21,11 +19,11 @@ struct PathAncestor { /// /// Nodes are visited in a DFS pre-order traversal. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Cursor { +pub struct Cursor { /// The parent path of this cursor - parent: Option>, + parent: Option>>, /// The node the cursor is currently pointing to. - node: Node, + node: Node, /// The index of the current child node in the parent's children. // Required to go to the next/previous sibling. child_number: usize, @@ -36,12 +34,12 @@ pub struct Cursor { is_completed: bool, } -impl Cursor { - fn as_ancestor_node(&self) -> Option> { - if let Node::Rule(rule_node) = &self.node { +impl Cursor { + fn as_ancestor_node(&self) -> Option>> { + if let Node::::Rule(rule_node) = &self.node { Some(Rc::new(PathAncestor { parent: self.parent.clone(), - rule_node: Rc::clone(rule_node), + rule_node: rule_node.clone(), child_number: self.child_number, text_offset: self.text_offset, })) @@ -50,16 +48,16 @@ impl Cursor { } } - fn set_from_ancestor_node(&mut self, ancestor: &Rc) { + fn set_from_ancestor_node(&mut self, ancestor: &Rc>) { self.parent = ancestor.parent.clone(); - self.node = Node::Rule(Rc::clone(&ancestor.rule_node)); + self.node = Node::::Rule(ancestor.rule_node.clone()); self.child_number = ancestor.child_number; self.text_offset = ancestor.text_offset; } } -impl Iterator for Cursor { - type Item = Node; +impl Iterator for Cursor { + type Item = Node; fn next(&mut self) -> Option { if self.is_completed { @@ -73,8 +71,8 @@ impl Iterator for Cursor { } } -impl Cursor { - pub(crate) fn new(node: Node, text_offset: TextIndex) -> Self { +impl Cursor { + pub(crate) fn new(node: Node, text_offset: TextIndex) -> Self { Self { parent: None, node, @@ -122,11 +120,11 @@ impl Cursor { } /// Returns the currently pointed to [`Node`]. - pub fn node(&self) -> Node { + pub fn node(&self) -> Node { self.node.clone() } - pub fn label(&self) -> Option { + pub fn label(&self) -> Option { self.parent.as_ref().and_then(|parent| { let this = &parent.rule_node.children[self.child_number]; @@ -162,17 +160,17 @@ impl Cursor { } /// Returns an iterator over the current node's ancestors, starting from the parent of the current node. - pub fn ancestors(&self) -> impl Iterator> { - struct Iter { - a: Option>, + pub fn ancestors(&self) -> impl Iterator>> { + struct Iter { + a: Option>>, } - impl Iterator for Iter { - type Item = Rc; + impl Iterator for Iter { + type Item = Rc>; fn next(&mut self) -> Option { if let Some(a) = self.a.take() { self.a = a.parent.clone(); - Some(Rc::clone(&a.rule_node)) + Some(a.rule_node.clone()) } else { None } @@ -372,14 +370,14 @@ impl Cursor { /// Attempts to go to the next token with the given kind, according to the DFS pre-order traversal. /// /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_token_with_kind(&mut self, kind: TokenKind) -> bool { + pub fn go_to_next_token_with_kind(&mut self, kind: T::TerminalKind) -> bool { self.go_to_next_matching(|node| node.is_token_with_kind(kind)) } /// Attempts to go to the next token with any of the given kinds, according to the DFS pre-order traversal. /// /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_token_with_kinds(&mut self, kinds: &[TokenKind]) -> bool { + pub fn go_to_next_token_with_kinds(&mut self, kinds: &[T::TerminalKind]) -> bool { self.go_to_next_matching(|node| node.is_token_with_kinds(kinds)) } @@ -393,18 +391,18 @@ impl Cursor { /// Attempts to go to the next rule with the given kind, according to the DFS pre-order traversal. /// /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_rule_with_kind(&mut self, kind: RuleKind) -> bool { + pub fn go_to_next_rule_with_kind(&mut self, kind: T::NonTerminalKind) -> bool { self.go_to_next_matching(|node| node.is_rule_with_kind(kind)) } /// Attempts to go to the next rule with any of the given kinds, according to the DFS pre-order traversal. /// /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_rule_with_kinds(&mut self, kinds: &[RuleKind]) -> bool { + pub fn go_to_next_rule_with_kinds(&mut self, kinds: &[T::NonTerminalKind]) -> bool { self.go_to_next_matching(|node| node.is_rule_with_kinds(kinds)) } - fn go_to_next_matching(&mut self, pred: impl Fn(&Node) -> bool) -> bool { + fn go_to_next_matching(&mut self, pred: impl Fn(&Node) -> bool) -> bool { while self.go_to_next() { if pred(&self.node) { return true; @@ -416,49 +414,49 @@ impl Cursor { } /// A [`Cursor`] that also keeps track of the labels of the nodes it visits. -pub struct CursorWithLabels { - cursor: Cursor, +pub struct CursorWithLabels { + cursor: Cursor, } -impl CursorWithLabels { - pub fn without_labels(self) -> Cursor { +impl CursorWithLabels { + pub fn without_labels(self) -> Cursor { self.cursor } } -impl Iterator for CursorWithLabels { - type Item = LabeledNode; +impl Iterator for CursorWithLabels { + type Item = LabeledNode; fn next(&mut self) -> Option { let label = self.cursor.label(); - self.cursor.next().map(|node| LabeledNode { label, node }) + self.cursor.next().map(|node| Self::Item { label, node }) } } -impl std::ops::Deref for CursorWithLabels { - type Target = Cursor; +impl std::ops::Deref for CursorWithLabels { + type Target = Cursor; fn deref(&self) -> &Self::Target { &self.cursor } } -impl std::ops::DerefMut for CursorWithLabels { +impl std::ops::DerefMut for CursorWithLabels { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.cursor } } -impl Cursor { +impl Cursor { /// Returns a [`CursorWithLabels`] that wraps this cursor. - pub fn with_labels(self) -> CursorWithLabels { - CursorWithLabels::from(self) + pub fn with_labels(self) -> CursorWithLabels { + CursorWithLabels::::from(self) } } -impl From for CursorWithLabels { - fn from(cursor: Cursor) -> Self { - CursorWithLabels { cursor } +impl From> for CursorWithLabels { + fn from(cursor: Cursor) -> Self { + CursorWithLabels:: { cursor } } } diff --git a/crates/metaslang/cst/src/lib.rs b/crates/metaslang/cst/src/lib.rs new file mode 100644 index 0000000000..b38d987908 --- /dev/null +++ b/crates/metaslang/cst/src/lib.rs @@ -0,0 +1,44 @@ +pub mod cst; +pub mod cursor; +pub mod query; +pub mod text_index; + +pub trait Kind: + Sized + + Copy + + Clone + + PartialEq + + Eq + + std::fmt::Display + + std::fmt::Debug + + serde::Serialize + + for<'a> std::convert::TryFrom<&'a str, Error = strum::ParseError> +{ +} + +impl Kind for T where + T: Sized + + Copy + + Clone + + PartialEq + + Eq + + std::fmt::Display + + std::fmt::Debug + + serde::Serialize + + for<'a> std::convert::TryFrom<&'a str, Error = strum::ParseError> +{ +} + +pub trait TerminalKind: Kind { + fn is_trivia(&self) -> bool; +} + +pub trait NonTerminalKind: Kind {} + +pub trait EdgeKind: Kind {} + +pub trait KindTypes: Clone + PartialEq { + type NonTerminalKind: NonTerminalKind; + type TerminalKind: TerminalKind; + type EdgeKind: EdgeKind; +} diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/engine.rs b/crates/metaslang/cst/src/query/engine.rs similarity index 71% rename from crates/solidity/outputs/cargo/slang_solidity/src/generated/query/engine.rs rename to crates/metaslang/cst/src/query/engine.rs index 2f794db725..a19ea30aed 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/engine.rs +++ b/crates/metaslang/cst/src/query/engine.rs @@ -1,18 +1,16 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - use std::collections::HashMap; use std::rc::Rc; -// This crate is copied to another crate, so all imports should be relative -use super::super::cst; -use super::super::cursor::Cursor; -use super::model::{ +use crate::cst::Node; +use crate::cursor::Cursor; +use crate::query::model::{ AlternativesMatcher, BindingMatcher, Kind, Matcher, NodeMatcher, NodeSelector, OneOrMoreMatcher, OptionalMatcher, Query, SequenceMatcher, }; +use crate::KindTypes; -impl Cursor { - pub fn query(self, queries: Vec) -> QueryResultIterator { +impl Cursor { + pub fn query(self, queries: Vec>) -> QueryResultIterator { QueryResultIterator::new(self, queries) } @@ -27,9 +25,9 @@ impl Cursor { } } - fn matches_node_selector(&self, node_selector: &NodeSelector) -> bool { + fn matches_node_selector(&self, node_selector: &NodeSelector) -> bool { match self.node() { - cst::Node::Rule(rule) => match node_selector { + Node::::Rule(rule) => match node_selector { NodeSelector::Anonymous => true, NodeSelector::Kind { kind } => Kind::Rule(rule.kind) == *kind, NodeSelector::Text { .. } => false, @@ -40,7 +38,7 @@ impl Cursor { NodeSelector::LabelAndText { .. } => false, }, - cst::Node::Token(token) => match node_selector { + Node::::Token(token) => match node_selector { NodeSelector::Anonymous => true, NodeSelector::Kind { kind } => Kind::Token(token.kind) == *kind, NodeSelector::Text { text } => token.text == *text, @@ -56,9 +54,9 @@ impl Cursor { } } -impl Matcher { +impl Matcher { // This allows for queries to pre-flight against a cursor without allocating - fn can_match(&self, cursor: &Cursor) -> bool { + fn can_match(&self, cursor: &Cursor) -> bool { match self { Self::Binding(matcher) => matcher.child.can_match(cursor), Self::Node(matcher) => cursor.matches_node_selector(&matcher.node_selector), @@ -70,41 +68,43 @@ impl Matcher { } } - fn create_combinator(&self, cursor: Cursor) -> CombinatorRef { + fn create_combinator(&self, cursor: Cursor) -> CombinatorRef { match self { - Self::Binding(matcher) => Box::new(BindingCombinator::new(Rc::clone(matcher), cursor)), - Self::Node(matcher) => Box::new(NodeCombinator::new(Rc::clone(matcher), cursor)), + Self::Binding(matcher) => { + Box::new(BindingCombinator::::new(matcher.clone(), cursor)) + } + Self::Node(matcher) => Box::new(NodeCombinator::::new(matcher.clone(), cursor)), Self::Sequence(matcher) => { - Box::new(SequenceCombinator::new(Rc::clone(matcher), cursor)) + Box::new(SequenceCombinator::::new(matcher.clone(), cursor)) } Self::Alternatives(matcher) => { - Box::new(AlternativesCombinator::new(Rc::clone(matcher), cursor)) + Box::new(AlternativesCombinator::::new(matcher.clone(), cursor)) } Self::Optional(matcher) => { - Box::new(OptionalCombinator::new(Rc::clone(matcher), cursor)) + Box::new(OptionalCombinator::::new(matcher.clone(), cursor)) } Self::OneOrMore(matcher) => { - Box::new(OneOrMoreCombinator::new(Rc::clone(matcher), cursor)) + Box::new(OneOrMoreCombinator::::new(matcher.clone(), cursor)) } - Self::Ellipsis => Box::new(EllipsisCombinator::new(cursor)), + Self::Ellipsis => Box::new(EllipsisCombinator::::new(cursor)), } } } -pub struct QueryResult { +pub struct QueryResult { pub query_number: usize, - pub bindings: HashMap>, + pub bindings: HashMap>>, } -pub struct QueryResultIterator { - cursor: Cursor, - queries: Vec, +pub struct QueryResultIterator { + cursor: Cursor, + queries: Vec>, query_number: usize, - combinator: Option, + combinator: Option>, } -impl QueryResultIterator { - fn new(cursor: Cursor, queries: Vec) -> Self { +impl QueryResultIterator { + fn new(cursor: Cursor, queries: Vec>) -> Self { Self { cursor, queries, @@ -129,8 +129,8 @@ impl QueryResultIterator { } } -impl Iterator for QueryResultIterator { - type Item = QueryResult; +impl Iterator for QueryResultIterator { + type Item = QueryResult; fn next(&mut self) -> Option { while !self.cursor.is_completed() { @@ -153,23 +153,23 @@ impl Iterator for QueryResultIterator { } } -trait Combinator { +trait Combinator { // None -> failed to match, you must backtrack. DO NOT call again // Some(cursor) if cursor.is_complete -> matched, end of input // Some(cursor) if !cursor.is_complete -> matched, more input to go - fn next(&mut self) -> Option; - fn accumulate_bindings(&self, bindings: &mut HashMap>); + fn next(&mut self) -> Option>; + fn accumulate_bindings(&self, bindings: &mut HashMap>>); } -type CombinatorRef = Box; +type CombinatorRef = Box>; -struct BindingCombinator { - matcher: Rc, - cursor: Cursor, - child: CombinatorRef, +struct BindingCombinator { + matcher: Rc>, + cursor: Cursor, + child: CombinatorRef, } -impl BindingCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { +impl BindingCombinator { + fn new(matcher: Rc>, cursor: Cursor) -> Self { let child = matcher.child.create_combinator(cursor.clone()); Self { matcher, @@ -179,12 +179,12 @@ impl BindingCombinator { } } -impl Combinator for BindingCombinator { - fn next(&mut self) -> Option { +impl Combinator for BindingCombinator { + fn next(&mut self) -> Option> { self.child.next() } - fn accumulate_bindings(&self, bindings: &mut HashMap>) { + fn accumulate_bindings(&self, bindings: &mut HashMap>>) { bindings .entry(self.matcher.name.clone()) .or_default() @@ -193,15 +193,15 @@ impl Combinator for BindingCombinator { } } -struct NodeCombinator { - matcher: Rc, - child: Option, - cursor: Cursor, +struct NodeCombinator { + matcher: Rc>, + child: Option>, + cursor: Cursor, is_initialised: bool, } -impl NodeCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { +impl NodeCombinator { + fn new(matcher: Rc>, cursor: Cursor) -> Self { Self { matcher, child: None, @@ -211,8 +211,8 @@ impl NodeCombinator { } } -impl Combinator for NodeCombinator { - fn next(&mut self) -> Option { +impl Combinator for NodeCombinator { + fn next(&mut self) -> Option> { if self.cursor.is_completed() { return None; } @@ -255,22 +255,22 @@ impl Combinator for NodeCombinator { None } - fn accumulate_bindings(&self, bindings: &mut HashMap>) { + fn accumulate_bindings(&self, bindings: &mut HashMap>>) { if let Some(child) = self.child.as_ref() { child.accumulate_bindings(bindings); } } } -struct SequenceCombinator { - matcher: Rc, - children: Vec, - cursor: Cursor, +struct SequenceCombinator { + matcher: Rc>, + children: Vec>, + cursor: Cursor, is_initialised: bool, } -impl SequenceCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { +impl SequenceCombinator { + fn new(matcher: Rc>, cursor: Cursor) -> Self { Self { matcher, children: vec![], @@ -280,8 +280,8 @@ impl SequenceCombinator { } } -impl Combinator for SequenceCombinator { - fn next(&mut self) -> Option { +impl Combinator for SequenceCombinator { + fn next(&mut self) -> Option> { if !self.is_initialised { self.is_initialised = true; @@ -307,22 +307,22 @@ impl Combinator for SequenceCombinator { None } - fn accumulate_bindings(&self, bindings: &mut HashMap>) { + fn accumulate_bindings(&self, bindings: &mut HashMap>>) { for child in &self.children { child.accumulate_bindings(bindings); } } } -struct AlternativesCombinator { - matcher: Rc, +struct AlternativesCombinator { + matcher: Rc>, next_child_number: usize, - child: Option, - cursor: Cursor, + child: Option>, + cursor: Cursor, } -impl AlternativesCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { +impl AlternativesCombinator { + fn new(matcher: Rc>, cursor: Cursor) -> Self { Self { matcher, next_child_number: 0, @@ -332,8 +332,8 @@ impl AlternativesCombinator { } } -impl Combinator for AlternativesCombinator { - fn next(&mut self) -> Option { +impl Combinator for AlternativesCombinator { + fn next(&mut self) -> Option> { loop { if self.child.is_none() { match self.matcher.children.get(self.next_child_number) { @@ -353,20 +353,20 @@ impl Combinator for AlternativesCombinator { } } - fn accumulate_bindings(&self, bindings: &mut HashMap>) { + fn accumulate_bindings(&self, bindings: &mut HashMap>>) { self.child.as_ref().unwrap().accumulate_bindings(bindings); } } -struct OptionalCombinator { - matcher: Rc, - child: Option, - cursor: Cursor, +struct OptionalCombinator { + matcher: Rc>, + child: Option>, + cursor: Cursor, have_nonempty_match: bool, } -impl OptionalCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { +impl OptionalCombinator { + fn new(matcher: Rc>, cursor: Cursor) -> Self { Self { matcher, child: None, @@ -376,8 +376,8 @@ impl OptionalCombinator { } } -impl Combinator for OptionalCombinator { - fn next(&mut self) -> Option { +impl Combinator for OptionalCombinator { + fn next(&mut self) -> Option> { if let Some(child) = self.child.as_mut() { match child.next() { result @ Some(_) => { @@ -397,7 +397,7 @@ impl Combinator for OptionalCombinator { } } - fn accumulate_bindings(&self, bindings: &mut HashMap>) { + fn accumulate_bindings(&self, bindings: &mut HashMap>>) { if self.have_nonempty_match { if let Some(child) = self.child.as_ref() { child.accumulate_bindings(bindings); @@ -406,14 +406,14 @@ impl Combinator for OptionalCombinator { } } -struct OneOrMoreCombinator { - matcher: Rc, - children: Vec, - cursor_for_next_repetition: Option, +struct OneOrMoreCombinator { + matcher: Rc>, + children: Vec>, + cursor_for_next_repetition: Option>, } -impl OneOrMoreCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { +impl OneOrMoreCombinator { + fn new(matcher: Rc>, cursor: Cursor) -> Self { let cursor_for_next_repetition = Some(cursor); Self { matcher, @@ -423,8 +423,8 @@ impl OneOrMoreCombinator { } } -impl Combinator for OneOrMoreCombinator { - fn next(&mut self) -> Option { +impl Combinator for OneOrMoreCombinator { + fn next(&mut self) -> Option> { loop { if let Some(cursor_for_next_repetition) = self.cursor_for_next_repetition.take() { let next_child = self @@ -448,20 +448,20 @@ impl Combinator for OneOrMoreCombinator { } } - fn accumulate_bindings(&self, bindings: &mut HashMap>) { + fn accumulate_bindings(&self, bindings: &mut HashMap>>) { for child in &self.children { child.accumulate_bindings(bindings); } } } -struct EllipsisCombinator { - cursor: Cursor, +struct EllipsisCombinator { + cursor: Cursor, has_returned_initial_empty_value: bool, } -impl EllipsisCombinator { - fn new(cursor: Cursor) -> Self { +impl EllipsisCombinator { + fn new(cursor: Cursor) -> Self { Self { cursor, has_returned_initial_empty_value: false, @@ -469,8 +469,8 @@ impl EllipsisCombinator { } } -impl Combinator for EllipsisCombinator { - fn next(&mut self) -> Option { +impl Combinator for EllipsisCombinator { + fn next(&mut self) -> Option> { if !self.has_returned_initial_empty_value { self.has_returned_initial_empty_value = true; return Some(self.cursor.clone()); @@ -483,5 +483,5 @@ impl Combinator for EllipsisCombinator { None } - fn accumulate_bindings(&self, _bindings: &mut HashMap>) {} + fn accumulate_bindings(&self, _bindings: &mut HashMap>>) {} } diff --git a/crates/metaslang/cst/src/query/mod.rs b/crates/metaslang/cst/src/query/mod.rs new file mode 100644 index 0000000000..8d954ed676 --- /dev/null +++ b/crates/metaslang/cst/src/query/mod.rs @@ -0,0 +1,6 @@ +mod engine; +mod model; +mod parser; + +pub use engine::{QueryResult, QueryResultIterator}; +pub use model::Query; diff --git a/crates/codegen/runtime/cargo/src/runtime/query/model.rs b/crates/metaslang/cst/src/query/model.rs similarity index 69% rename from crates/codegen/runtime/cargo/src/runtime/query/model.rs rename to crates/metaslang/cst/src/query/model.rs index 4153cd58ab..5b7a77c665 100644 --- a/crates/codegen/runtime/cargo/src/runtime/query/model.rs +++ b/crates/metaslang/cst/src/query/model.rs @@ -1,42 +1,41 @@ use std::fmt; use std::rc::Rc; -// This crate is copied to another crate, so all imports should be relative -use super::super::kinds::{NodeLabel, RuleKind, TokenKind}; +use crate::KindTypes; #[derive(Clone)] -pub struct Query(pub(super) Matcher); +pub struct Query(pub(super) Matcher); -impl Query { +impl Query { pub fn parse(text: &str) -> Result { Matcher::parse(text).map(Self) } } -impl fmt::Display for Query { +impl fmt::Display for Query { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } #[derive(Clone)] -pub(super) enum Matcher { - Binding(Rc), - Node(Rc), - Optional(Rc), - Alternatives(Rc), - Sequence(Rc), - OneOrMore(Rc), +pub(super) enum Matcher { + Binding(Rc>), + Node(Rc>), + Optional(Rc>), + Alternatives(Rc>), + Sequence(Rc>), + OneOrMore(Rc>), Ellipsis, } -impl Matcher { +impl Matcher { fn parse(text: &str) -> Result { super::parser::parse_query(text) } } -impl fmt::Display for Matcher { +impl fmt::Display for Matcher { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Binding(binding) => { @@ -87,31 +86,31 @@ impl fmt::Display for Matcher { } #[derive(Copy, Clone, PartialEq, Eq)] -pub(super) enum Kind { - Rule(RuleKind), - Token(TokenKind), +pub(super) enum Kind { + Rule(T::NonTerminalKind), + Token(T::TerminalKind), } -impl fmt::Display for Kind { +impl fmt::Display for Kind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Kind::Rule(rule) => write!(f, "{rule}"), - Kind::Token(token) => write!(f, "{token}"), + Self::Rule(rule) => write!(f, "{rule}"), + Self::Token(token) => write!(f, "{token}"), } } } #[derive(Clone)] -pub(super) enum NodeSelector { +pub(super) enum NodeSelector { Anonymous, - Kind { kind: Kind }, + Kind { kind: Kind }, Text { text: String }, - Label { label: NodeLabel }, - LabelAndKind { label: NodeLabel, kind: Kind }, - LabelAndText { label: NodeLabel, text: String }, + Label { label: T::EdgeKind }, + LabelAndKind { label: T::EdgeKind, kind: Kind }, + LabelAndText { label: T::EdgeKind, text: String }, } -impl fmt::Display for NodeSelector { +impl fmt::Display for NodeSelector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn escape_string(string: &str) -> String { string @@ -145,28 +144,28 @@ impl fmt::Display for NodeSelector { } } -pub(super) struct BindingMatcher { +pub(super) struct BindingMatcher { pub name: String, - pub child: Matcher, + pub child: Matcher, } -pub(super) struct NodeMatcher { - pub node_selector: NodeSelector, - pub child: Option, +pub(super) struct NodeMatcher { + pub node_selector: NodeSelector, + pub child: Option>, } -pub(super) struct SequenceMatcher { - pub children: Vec, +pub(super) struct SequenceMatcher { + pub children: Vec>, } -pub(super) struct AlternativesMatcher { - pub children: Vec, +pub(super) struct AlternativesMatcher { + pub children: Vec>, } -pub(super) struct OptionalMatcher { - pub child: Matcher, +pub(super) struct OptionalMatcher { + pub child: Matcher, } -pub(super) struct OneOrMoreMatcher { - pub child: Matcher, +pub(super) struct OneOrMoreMatcher { + pub child: Matcher, } diff --git a/crates/codegen/runtime/cargo/src/runtime/query/parser.rs b/crates/metaslang/cst/src/query/parser.rs similarity index 89% rename from crates/codegen/runtime/cargo/src/runtime/query/parser.rs rename to crates/metaslang/cst/src/query/parser.rs index 2deae26067..7ed8e4ff4c 100644 --- a/crates/codegen/runtime/cargo/src/runtime/query/parser.rs +++ b/crates/metaslang/cst/src/query/parser.rs @@ -9,14 +9,13 @@ use nom::multi::{fold_many0, many0, many1}; use nom::sequence::{delimited, pair, preceded, terminated}; use nom::{Finish, IResult, Parser}; -// This crate is copied to another crate, so all imports should be relative -use super::super::kinds::{NodeLabel, RuleKind, TokenKind}; -use super::model::{ +use crate::query::model::{ AlternativesMatcher, BindingMatcher, Kind, Matcher, NodeMatcher, NodeSelector, OneOrMoreMatcher, OptionalMatcher, SequenceMatcher, }; +use crate::KindTypes; -pub(super) fn parse_query(input: &str) -> Result { +pub(super) fn parse_query(input: &str) -> Result, String> { all_consuming(preceded( multispace0, opt(binding_name_token) @@ -47,7 +46,7 @@ pub(super) fn parse_query(input: &str) -> Result { .map_err(|e| e.to_string()) } -fn parse_node(i: &str) -> IResult<&str, Matcher, VerboseError<&str>> { +fn parse_node(i: &str) -> IResult<&str, Matcher, VerboseError<&str>> { delimited( token('['), parse_node_selector.and(many0(parse_match)), @@ -69,14 +68,16 @@ fn parse_node(i: &str) -> IResult<&str, Matcher, VerboseError<&str>> { .parse(i) } -fn parse_node_selector(input: &str) -> IResult<&str, NodeSelector, VerboseError<&str>> { - enum Tail { +fn parse_node_selector( + input: &str, +) -> IResult<&str, NodeSelector, VerboseError<&str>> { + enum Tail { Anonymous, - Kind(Kind), + Kind(Kind), Text(String), } - opt(label_token) + opt(label_token::) .and(alt(( token('_').map(|_| Tail::Anonymous), kind_token.map(Tail::Kind), @@ -100,7 +101,7 @@ enum Quantifier { OneOrMore, } -fn parse_match(input: &str) -> IResult<&str, Matcher, VerboseError<&str>> { +fn parse_match(input: &str) -> IResult<&str, Matcher, VerboseError<&str>> { opt(binding_name_token) .and(alt(( parse_node, @@ -172,22 +173,20 @@ fn binding_name_token(i: &str) -> IResult<&str, String, VerboseError<&str>> { terminated(preceded(char('@'), raw_identifier), multispace0).parse(i) } -fn kind_token(i: &str) -> IResult<&str, Kind, VerboseError<&str>> { +fn kind_token(i: &str) -> IResult<&str, Kind, VerboseError<&str>> { terminated(raw_identifier, multispace0) .map(|id| { - TokenKind::try_from(id.as_str()) + T::TerminalKind::try_from(id.as_str()) .map(Kind::Token) - .or_else(|_| RuleKind::try_from(id.as_str()).map(Kind::Rule)) - .unwrap( - // TODO(#554): report these errors to users - ) + .or_else(|_| T::NonTerminalKind::try_from(id.as_str()).map(Kind::Rule)) + .unwrap() // TODO }) .parse(i) } -fn label_token(i: &str) -> IResult<&str, NodeLabel, VerboseError<&str>> { +fn label_token(i: &str) -> IResult<&str, T::EdgeKind, VerboseError<&str>> { terminated(raw_identifier, token(':')) - .map(|id| NodeLabel::try_from(id.as_str()).unwrap()) + .map(|id| T::EdgeKind::try_from(id.as_str()).unwrap()) .parse(i) } diff --git a/crates/codegen/runtime/cargo/src/runtime/text_index.rs b/crates/metaslang/cst/src/text_index.rs similarity index 100% rename from crates/codegen/runtime/cargo/src/runtime/text_index.rs rename to crates/metaslang/cst/src/text_index.rs diff --git a/crates/solidity/inputs/language/src/definition.rs b/crates/solidity/inputs/language/src/definition.rs index 8586078397..f3a536ae1d 100644 --- a/crates/solidity/inputs/language/src/definition.rs +++ b/crates/solidity/inputs/language/src/definition.rs @@ -6582,11 +6582,5 @@ codegen_language_macros::compile!(Language( ) ] ) - ], - queries = ( - // TODO(#554): replace with real queries: - query_one = "query 1 code", - query_two = "query 2 code", - query_three = "query 3 code" - ) + ] )); diff --git a/crates/solidity/outputs/cargo/slang_solidity/Cargo.toml b/crates/solidity/outputs/cargo/slang_solidity/Cargo.toml index b9a76ef719..a83f06eee5 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/Cargo.toml +++ b/crates/solidity/outputs/cargo/slang_solidity/Cargo.toml @@ -45,7 +45,7 @@ solidity_language = { workspace = true } # __REMOVE_THIS_LINE_DURING_CAR anyhow = { workspace = true, optional = true } ariadne = { workspace = true } clap = { workspace = true, optional = true } -nom = { workspace = true } +metaslang_cst = { workspace = true } semver = { workspace = true } serde = { workspace = true } serde_json = { workspace = true, optional = true } diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/cst.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/cst.rs deleted file mode 100644 index bdc126ca60..0000000000 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/cst.rs +++ /dev/null @@ -1,200 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -use std::rc::Rc; - -use serde::Serialize; - -use crate::cursor::Cursor; -use crate::kinds::{NodeLabel, RuleKind, TokenKind}; -use crate::text_index::TextIndex; - -#[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub struct LabeledNode { - pub label: Option, - pub node: Node, -} - -impl LabeledNode { - /// Creates an anonymous node (without a label). - pub fn anonymous(node: Node) -> Self { - Self { label: None, node } - } -} - -impl std::ops::Deref for LabeledNode { - type Target = Node; - - fn deref(&self) -> &Self::Target { - &self.node - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub struct RuleNode { - pub kind: RuleKind, - pub text_len: TextIndex, - #[serde(skip_serializing_if = "Vec::is_empty")] - pub children: Vec, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub struct TokenNode { - pub kind: TokenKind, - pub text: String, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize)] -pub enum Node { - Rule(Rc), - Token(Rc), -} - -impl Node { - pub fn rule(kind: RuleKind, children: Vec) -> Self { - let text_len = children.iter().map(|node| node.text_len()).sum(); - - Self::Rule(Rc::new(RuleNode { - kind, - text_len, - children, - })) - } - - pub fn token(kind: TokenKind, text: String) -> Self { - Self::Token(Rc::new(TokenNode { kind, text })) - } - - pub fn text_len(&self) -> TextIndex { - match self { - Self::Rule(node) => node.text_len, - Self::Token(node) => (&node.text).into(), - } - } - - /// Returns a slice of the children (not all descendants) of this node. - pub fn children(&self) -> &[LabeledNode] { - match self { - Self::Rule(node) => &node.children, - Self::Token(_) => &[], - } - } - - /// Creates a [`Cursor`] that starts at the current node as the root and a given initial `text_offset`. - pub fn cursor_with_offset(&self, text_offset: TextIndex) -> Cursor { - Cursor::new(self.clone(), text_offset) - } - - /// Reconstructs the original source code from the parse tree. - pub fn unparse(self) -> String { - match self { - Self::Rule(rule) => rule.unparse(), - Self::Token(token) => token.text.clone(), - } - } - - pub fn into_rule(self) -> Option> { - match self { - Self::Rule(rule) => Some(rule), - Self::Token(..) => None, - } - } - - pub fn is_rule(&self) -> bool { - self.as_rule().is_some() - } - - pub fn as_rule(&self) -> Option<&Rc> { - match self { - Self::Rule(rule) => Some(rule), - Self::Token(..) => None, - } - } - - pub fn is_rule_with_kind(&self, kind: RuleKind) -> bool { - self.as_rule_with_kind(kind).is_some() - } - - pub fn as_rule_with_kind(&self, kind: RuleKind) -> Option<&Rc> { - self.as_rule().filter(|rule| rule.kind == kind) - } - - pub fn is_rule_with_kinds(&self, kinds: &[RuleKind]) -> bool { - self.as_rule_with_kinds(kinds).is_some() - } - - pub fn as_rule_with_kinds(&self, kinds: &[RuleKind]) -> Option<&Rc> { - self.as_rule().filter(|rule| kinds.contains(&rule.kind)) - } - - pub fn into_token(self) -> Option> { - match self { - Self::Token(token) => Some(token), - Self::Rule(..) => None, - } - } - - pub fn is_token(&self) -> bool { - self.as_token().is_some() - } - - pub fn as_token(&self) -> Option<&Rc> { - match self { - Self::Token(token) => Some(token), - Self::Rule(..) => None, - } - } - - pub fn is_token_with_kind(&self, kind: TokenKind) -> bool { - self.as_token_with_kind(kind).is_some() - } - - pub fn as_token_with_kind(&self, kind: TokenKind) -> Option<&Rc> { - self.as_token().filter(|token| token.kind == kind) - } - - pub fn is_token_with_kinds(&self, kinds: &[TokenKind]) -> bool { - self.as_token_with_kinds(kinds).is_some() - } - - pub fn as_token_with_kinds(&self, kinds: &[TokenKind]) -> Option<&Rc> { - self.as_token().filter(|token| kinds.contains(&token.kind)) - } - - pub fn is_trivia(&self) -> bool { - match self { - Self::Rule(_) => false, - Self::Token(token) => token.kind.is_trivia(), - } - } -} - -impl From> for Node { - fn from(node: Rc) -> Self { - Self::Rule(node) - } -} - -impl From> for Node { - fn from(node: Rc) -> Self { - Self::Token(node) - } -} - -impl RuleNode { - /// Creates a [`Cursor`] that starts at the current node as the root and a given initial `text_offset`. - pub fn cursor_with_offset(self: Rc, text_offset: TextIndex) -> Cursor { - Cursor::new(Node::Rule(self), text_offset) - } - - /// Reconstructs the original source code from the parse tree. - pub fn unparse(self: Rc) -> String { - let acc = String::with_capacity(self.text_len.utf8); - - self.cursor_with_offset(TextIndex::ZERO) - .filter_map(|node| node.into_token()) - .fold(acc, |mut acc, token| { - acc.push_str(&token.text); - acc - }) - } -} diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/cursor.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/cursor.rs deleted file mode 100644 index 72f14dd431..0000000000 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/cursor.rs +++ /dev/null @@ -1,464 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -//! A cursor that can traverse a CST in a DFS pre-order fashion. - -use std::rc::Rc; - -use crate::cst::{LabeledNode, Node, RuleNode}; -use crate::kinds::{NodeLabel, RuleKind, TokenKind}; -use crate::text_index::{TextIndex, TextRange}; - -/// A node in the ancestor path of a [`Cursor`]. -#[derive(Clone, Debug, PartialEq, Eq)] -struct PathAncestor { - parent: Option>, - rule_node: Rc, - child_number: usize, - text_offset: TextIndex, -} - -/// A cursor that can traverse a CST. -/// -/// Nodes are visited in a DFS pre-order traversal. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Cursor { - /// The parent path of this cursor - parent: Option>, - /// The node the cursor is currently pointing to. - node: Node, - /// The index of the current child node in the parent's children. - // Required to go to the next/previous sibling. - child_number: usize, - /// Text offset that corresponds to the beginning of the currently pointed to node. - text_offset: TextIndex, - /// Whether the cursor is completed, i.e. at the root node as a result of traversal (or when `complete`d). - /// If `true`, the cursor cannot be moved. - is_completed: bool, -} - -impl Cursor { - fn as_ancestor_node(&self) -> Option> { - if let Node::Rule(rule_node) = &self.node { - Some(Rc::new(PathAncestor { - parent: self.parent.clone(), - rule_node: Rc::clone(rule_node), - child_number: self.child_number, - text_offset: self.text_offset, - })) - } else { - None - } - } - - fn set_from_ancestor_node(&mut self, ancestor: &Rc) { - self.parent = ancestor.parent.clone(); - self.node = Node::Rule(Rc::clone(&ancestor.rule_node)); - self.child_number = ancestor.child_number; - self.text_offset = ancestor.text_offset; - } -} - -impl Iterator for Cursor { - type Item = Node; - - fn next(&mut self) -> Option { - if self.is_completed { - None - } else { - let cur = self.node(); - self.go_to_next(); - - Some(cur) - } - } -} - -impl Cursor { - pub(crate) fn new(node: Node, text_offset: TextIndex) -> Self { - Self { - parent: None, - node, - child_number: 0, - text_offset, - is_completed: false, - } - } - - /// Resets the cursor to the root node. - pub fn reset(&mut self) { - self.complete(); - self.is_completed = false; - } - - /// Completes the cursor, setting it to the root node. - pub fn complete(&mut self) { - if let Some(parent) = &self.parent.clone() { - let mut parent = parent; - while let Some(grandparent) = &parent.parent { - parent = grandparent; - } - self.set_from_ancestor_node(parent); - } - self.is_completed = true; - } - - /// Unlike `clone`, this re-roots at the current node. - /// It does preserve the correct text offset however, - /// even though the path is reset. - #[must_use] - pub fn spawn(&self) -> Self { - Self { - is_completed: false, - parent: None, - node: self.node.clone(), - child_number: 0, - text_offset: self.text_offset, - } - } - - /// Whether the cursor can be moved. - pub fn is_completed(&self) -> bool { - self.is_completed - } - - /// Returns the currently pointed to [`Node`]. - pub fn node(&self) -> Node { - self.node.clone() - } - - pub fn label(&self) -> Option { - self.parent.as_ref().and_then(|parent| { - let this = &parent.rule_node.children[self.child_number]; - - this.label - }) - } - - /// Returns the text offset that corresponds to the beginning of the currently pointed to node. - pub fn text_offset(&self) -> TextIndex { - self.text_offset - } - - /// Returns the text range that corresponds to the currently pointed to node. - pub fn text_range(&self) -> TextRange { - let start = self.text_offset; - let end = start + self.node.text_len(); - start..end - } - - /// Returns the depth of the current node in the CST, i.e. the number of ancestors. - pub fn depth(&self) -> usize { - let mut depth = 0; - if let Some(parent) = &self.parent { - let mut parent = parent; - depth += 1; - while let Some(grandparent) = &parent.parent { - depth += 1; - parent = grandparent; - } - } - - depth - } - - /// Returns an iterator over the current node's ancestors, starting from the parent of the current node. - pub fn ancestors(&self) -> impl Iterator> { - struct Iter { - a: Option>, - } - impl Iterator for Iter { - type Item = Rc; - - fn next(&mut self) -> Option { - if let Some(a) = self.a.take() { - self.a = a.parent.clone(); - Some(Rc::clone(&a.rule_node)) - } else { - None - } - } - } - Iter { - a: self.parent.clone(), - } - } - - /// Attempts to go to current node's next one, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next(&mut self) -> bool { - if self.is_completed { - return false; - } - - self.go_to_first_child() || self.go_to_next_non_descendent() - } - - /// Attempts to go to current node's next non-descendent. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_non_descendent(&mut self) -> bool { - if self.is_completed { - return false; - } - - while !self.go_to_next_sibling() { - if !self.go_to_parent() { - return false; - } - } - - true - } - - /// Attempts to go to current node's previous one, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_previous(&mut self) -> bool { - if self.is_completed { - return false; - } - - while !self.go_to_previous_sibling() { - if !self.go_to_parent() { - return false; - } - } - - while self.go_to_last_child() {} - - true - } - - /// Attempts to go to current node's parent. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_parent(&mut self) -> bool { - if let Some(parent) = &self.parent.clone() { - self.set_from_ancestor_node(parent); - - true - } else { - self.is_completed = true; - - false - } - } - - /// Attempts to go to current node's first child. - /// - /// Returns `false` if the cursor is finished or there's no child to go to. - pub fn go_to_first_child(&mut self) -> bool { - if self.is_completed { - return false; - } - - // If the current cursor is a node and it has children, go to first children - if let Some(new_parent) = self.as_ancestor_node() { - if let Some(new_child) = new_parent.rule_node.children.first().cloned() { - self.parent = Some(new_parent); - self.node = new_child.node; - self.child_number = 0; - - return true; - } - } - - false - } - - /// Attempts to go to current node's last child. - /// - /// Returns `false` if the cursor is finished or there's no child to go to. - pub fn go_to_last_child(&mut self) -> bool { - if self.is_completed { - return false; - } - - if let Some(new_parent) = self.as_ancestor_node() { - if let Some(new_child) = new_parent.rule_node.children.last().cloned() { - self.child_number = new_parent.rule_node.children.len() - 1; - // This is cheaper than summing up the length of the children - self.text_offset += new_parent.rule_node.text_len - new_child.text_len(); - self.node = new_child.node; - self.parent = Some(new_parent); - - return true; - } - } - - false - } - - /// Attempts to go to current node's nth child. - /// - /// Returns `false` if the cursor is finished or there's no child to go to. - pub fn go_to_nth_child(&mut self, child_number: usize) -> bool { - if self.is_completed { - return false; - } - - if let Some(new_parent) = self.as_ancestor_node() { - if let Some(new_child) = new_parent.rule_node.children.get(child_number).cloned() { - self.node = new_child.node; - self.child_number = child_number; - // Sum up the length of the children before this child - // TODO(#871): it might sometimes be quicker to start from the end (like `go_to_last_child`) - self.text_offset += new_parent.rule_node.children[..child_number] - .iter() - .map(|node| node.text_len()) - .sum(); - self.parent = Some(new_parent); - - return true; - } - } - - false - } - - /// Attempts to go to current node's next sibling. - /// - /// Returns `false` if the cursor is finished or there's no sibling to go to. - pub fn go_to_next_sibling(&mut self) -> bool { - if self.is_completed { - return false; - } - - if let Some(parent) = &self.parent { - let new_child_number = self.child_number + 1; - if let Some(new_child) = parent.rule_node.children.get(new_child_number) { - self.text_offset += self.node.text_len(); - self.node = new_child.node.clone(); - self.child_number = new_child_number; - - return true; - } - } - - false - } - - /// Attempts to go to current node's previous sibling. - /// - /// Returns `false` if the cursor is finished or there's no sibling to go to. - pub fn go_to_previous_sibling(&mut self) -> bool { - if self.is_completed { - return false; - } - - if let Some(parent) = &self.parent { - if self.child_number > 0 { - let new_child_number = self.child_number - 1; - let new_child = &parent.rule_node.children[new_child_number]; - self.text_offset -= new_child.node.text_len(); - self.node = new_child.node.clone(); - self.child_number = new_child_number; - - return true; - } - } - - false - } - - /// Attempts to go to the next token, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_token(&mut self) -> bool { - self.go_to_next_matching(|node| node.is_token()) - } - - /// Attempts to go to the next token with the given kind, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_token_with_kind(&mut self, kind: TokenKind) -> bool { - self.go_to_next_matching(|node| node.is_token_with_kind(kind)) - } - - /// Attempts to go to the next token with any of the given kinds, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_token_with_kinds(&mut self, kinds: &[TokenKind]) -> bool { - self.go_to_next_matching(|node| node.is_token_with_kinds(kinds)) - } - - /// Attempts to go to the next rule, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_rule(&mut self) -> bool { - self.go_to_next_matching(|node| node.is_rule()) - } - - /// Attempts to go to the next rule with the given kind, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_rule_with_kind(&mut self, kind: RuleKind) -> bool { - self.go_to_next_matching(|node| node.is_rule_with_kind(kind)) - } - - /// Attempts to go to the next rule with any of the given kinds, according to the DFS pre-order traversal. - /// - /// Returns `false` if the cursor is finished and at the root. - pub fn go_to_next_rule_with_kinds(&mut self, kinds: &[RuleKind]) -> bool { - self.go_to_next_matching(|node| node.is_rule_with_kinds(kinds)) - } - - fn go_to_next_matching(&mut self, pred: impl Fn(&Node) -> bool) -> bool { - while self.go_to_next() { - if pred(&self.node) { - return true; - } - } - - false - } -} - -/// A [`Cursor`] that also keeps track of the labels of the nodes it visits. -pub struct CursorWithLabels { - cursor: Cursor, -} - -impl CursorWithLabels { - pub fn without_labels(self) -> Cursor { - self.cursor - } -} - -impl Iterator for CursorWithLabels { - type Item = LabeledNode; - - fn next(&mut self) -> Option { - let label = self.cursor.label(); - - self.cursor.next().map(|node| LabeledNode { label, node }) - } -} - -impl std::ops::Deref for CursorWithLabels { - type Target = Cursor; - - fn deref(&self) -> &Self::Target { - &self.cursor - } -} - -impl std::ops::DerefMut for CursorWithLabels { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cursor - } -} - -impl Cursor { - /// Returns a [`CursorWithLabels`] that wraps this cursor. - pub fn with_labels(self) -> CursorWithLabels { - CursorWithLabels::from(self) - } -} - -impl From for CursorWithLabels { - fn from(cursor: Cursor) -> Self { - CursorWithLabels { cursor } - } -} diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/generated/kinds.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/generated/kinds.rs index e4d07f2549..37968d6126 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/generated/kinds.rs +++ b/crates/solidity/outputs/cargo/slang_solidity/src/generated/generated/kinds.rs @@ -234,6 +234,8 @@ pub enum RuleKind { YulVariableDeclarationValue, } +impl metaslang_cst::NonTerminalKind for RuleKind {} + #[derive( Debug, Eq, @@ -386,6 +388,8 @@ pub enum NodeLabel { WhileKeyword, } +impl metaslang_cst::EdgeKind for NodeLabel {} + #[derive( Debug, Eq, @@ -774,8 +778,8 @@ pub enum TokenKind { YulYearsKeyword, } -impl TokenKind { - pub fn is_trivia(&self) -> bool { +impl metaslang_cst::TerminalKind for TokenKind { + fn is_trivia(&self) -> bool { matches!(self, |Self::EndOfLine| Self::MultiLineComment | Self::MultiLineNatSpecComment | Self::SingleLineComment diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/mod.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/mod.rs index 6c56b9c553..93a1bebed4 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/mod.rs +++ b/crates/solidity/outputs/cargo/slang_solidity/src/generated/mod.rs @@ -4,17 +4,69 @@ pub(crate) mod parser_support; pub(crate) mod lexer; -pub mod cst; -pub mod cursor; -pub mod parse_error; -pub mod parse_output; -pub mod query; -pub mod text_index; - #[path = "generated/kinds.rs"] pub mod kinds; #[path = "generated/language.rs"] pub mod language; +pub mod parse_error; +pub mod parse_output; #[cfg(feature = "slang_napi_interfaces")] pub mod napi_interface; + +mod metaslang_cst { + #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] + // These derives are because default #[derive(...)] on a generic type implements only the trait + // with default bounds also implied for the generic types as well, i.e. + // + // #[derive(Clone)] // expands to `impl Clone for MyOption { ... }` (notice the `T: Clone`) + // struct MyOption(Option); + // + // This assumes that the underlying data type uses this internally, however it's only used as a + // type container/marker. + // + // A slightly more "correct" approach would be to implement the traits while skipping the bounds for + // the type marker, however this can be more noisy + pub enum KindTypes {} + + impl metaslang_cst::KindTypes for KindTypes { + type NonTerminalKind = crate::kinds::RuleKind; + type TerminalKind = crate::kinds::TokenKind; + type EdgeKind = crate::kinds::NodeLabel; + } +} + +pub mod cst { + use metaslang_cst::cst; + + use super::metaslang_cst::KindTypes; + + pub type Node = cst::Node; + pub type RuleNode = cst::NonTerminalNode; + pub type TokenNode = cst::TerminalNode; + pub type LabeledNode = cst::LabeledNode; +} + +pub mod cursor { + use metaslang_cst::cursor; + + use super::metaslang_cst::KindTypes; + + pub type Cursor = cursor::Cursor; + pub type CursorWithLabels = cursor::CursorWithLabels; +} + +pub mod query { + use metaslang_cst::query; + + use super::metaslang_cst::KindTypes; + + pub type Query = query::Query; + pub type QueryResult = query::QueryResult; + pub type QueryResultIterator = query::QueryResultIterator; +} + +pub mod text_index { + use metaslang_cst::text_index; + pub use text_index::{TextIndex, TextRange, TextRangeExtensions}; +} diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/cst.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/cst.rs index 56a62adcca..eff2d3bfe7 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/cst.rs +++ b/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/cst.rs @@ -17,9 +17,13 @@ pub enum NodeType { Token, } -impl RustNode { +pub trait NAPINodeExtensions { + fn into_js_either_node(self) -> Either; +} + +impl NAPINodeExtensions for RustNode { /// Converts the node into `napi` wrapper for `RuleNode | TokenNode` JS object. - pub fn into_js_either_node(self) -> Either { + fn into_js_either_node(self) -> Either { match self { RustNode::Rule(rule) => Either::A(RuleNode(rule)), RustNode::Token(token) => Either::B(TokenNode(token)), @@ -27,12 +31,6 @@ impl RustNode { } } -impl From for Either { - fn from(value: RustNode) -> Self { - value.into_js_either_node() - } -} - #[derive(Debug)] #[napi(namespace = "cst")] pub struct RuleNode(pub(crate) Rc); @@ -73,7 +71,7 @@ impl RuleNode { self.0 .children .iter() - .map(|child| child.node.clone().into()) + .map(|child| child.node.clone().into_js_either_node()) .collect() } diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/cursor.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/cursor.rs index 623cdfb7c6..3fd9f64c17 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/cursor.rs +++ b/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/cursor.rs @@ -9,7 +9,7 @@ use napi::Either; use napi_derive::napi; use text_index::{TextIndex, TextRange}; -use crate::napi_interface::cst::{self, RuleNode, TokenNode}; +use crate::napi_interface::cst::{self, NAPINodeExtensions, RuleNode, TokenNode}; use crate::napi_interface::{text_index, NodeLabel, RuleKind, RustCursor, TokenKind}; #[napi(namespace = "cursor")] @@ -55,7 +55,7 @@ impl Cursor { #[napi(ts_return_type = "cst.Node", catch_unwind)] pub fn node(&self) -> Either { - self.0.node().into() + self.0.node().into_js_either_node() } #[napi(getter, ts_return_type = "kinds.NodeLabel", catch_unwind)] diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/generated/ast_selectors.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/generated/ast_selectors.rs index f544b2be8a..e49bb5d10f 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/generated/ast_selectors.rs +++ b/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/generated/ast_selectors.rs @@ -7,7 +7,7 @@ use std::rc::Rc; use napi::Either; use napi_derive::napi; -use crate::napi_interface::cst::{RuleNode, TokenNode}; +use crate::napi_interface::cst::{NAPINodeExtensions, RuleNode, TokenNode}; use crate::napi_interface::{RuleKind, RustLabeledNode, RustNode, RustRuleNode, TokenKind}; // diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/parse_output.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/parse_output.rs index 994cf7de26..dd162e213b 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/parse_output.rs +++ b/crates/solidity/outputs/cargo/slang_solidity/src/generated/napi_interface/parse_output.rs @@ -3,7 +3,7 @@ use napi::Either; use napi_derive::napi; -use crate::napi_interface::cst::{RuleNode, TokenNode}; +use crate::napi_interface::cst::{NAPINodeExtensions, RuleNode, TokenNode}; use crate::napi_interface::{cursor, parse_error, RustParseOutput}; #[napi(namespace = "parse_output")] @@ -19,7 +19,7 @@ impl From for ParseOutput { impl ParseOutput { #[napi(ts_return_type = "cst.Node", catch_unwind)] pub fn tree(&self) -> Either { - self.0.tree().into() + self.0.tree().into_js_either_node() } #[napi(ts_return_type = "Array", catch_unwind)] diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/parser_support/parser_result.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/parser_support/parser_result.rs index 16e59411fa..f86d384932 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/parser_support/parser_result.rs +++ b/crates/solidity/outputs/cargo/slang_solidity/src/generated/parser_support/parser_result.rs @@ -2,6 +2,8 @@ use std::ops::ControlFlow; +use metaslang_cst::TerminalKind; + use crate::cst::{self, LabeledNode, Node}; use crate::kinds::{NodeLabel, RuleKind, TokenKind}; use crate::text_index::TextIndex; diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/parser_support/sequence_helper.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/parser_support/sequence_helper.rs index 38acbad7b5..cecb10b5de 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/parser_support/sequence_helper.rs +++ b/crates/solidity/outputs/cargo/slang_solidity/src/generated/parser_support/sequence_helper.rs @@ -2,6 +2,8 @@ use std::ops::ControlFlow; +use metaslang_cst::TerminalKind; + use crate::cst::{self, LabeledNode}; use crate::kinds::{NodeLabel, TokenKind}; use crate::parser_support::parser_result::{Match, ParserResult, PrattElement, SkippedUntil}; diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/generated/user_defined_queries.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/generated/user_defined_queries.rs deleted file mode 100644 index cd1e97bd4d..0000000000 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/generated/user_defined_queries.rs +++ /dev/null @@ -1,13 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -pub(crate) trait UserDefinedQueries { - fn query_one(foo: &crate::cursor::Cursor); - - fn query_two(foo: &crate::cursor::Cursor, bar: &crate::cursor::Cursor); - - fn query_three( - foo: &crate::cursor::Cursor, - bar: &crate::cursor::Cursor, - baz: &crate::cursor::Cursor, - ); -} diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/mod.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/mod.rs deleted file mode 100644 index 2b38245760..0000000000 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -mod engine; -mod model; -mod parser; - -#[path = "generated/user_defined_queries.rs"] -mod user_defined_queries; - -pub use engine::{QueryResult, QueryResultIterator}; -pub use model::Query; -pub(crate) use user_defined_queries::UserDefinedQueries; diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/model.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/model.rs deleted file mode 100644 index 50f270ab71..0000000000 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/model.rs +++ /dev/null @@ -1,174 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -use std::fmt; -use std::rc::Rc; - -// This crate is copied to another crate, so all imports should be relative -use super::super::kinds::{NodeLabel, RuleKind, TokenKind}; - -#[derive(Clone)] -pub struct Query(pub(super) Matcher); - -impl Query { - pub fn parse(text: &str) -> Result { - Matcher::parse(text).map(Self) - } -} - -impl fmt::Display for Query { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -#[derive(Clone)] -pub(super) enum Matcher { - Binding(Rc), - Node(Rc), - Optional(Rc), - Alternatives(Rc), - Sequence(Rc), - OneOrMore(Rc), - Ellipsis, -} - -impl Matcher { - fn parse(text: &str) -> Result { - super::parser::parse_query(text) - } -} - -impl fmt::Display for Matcher { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Binding(binding) => { - write!(f, "@{} {}", binding.name, binding.child) - } - Self::Node(node) => { - if let Some(child) = &node.child { - write!(f, "[{} {}]", node.node_selector, child) - } else { - write!(f, "[{}]", node.node_selector) - } - } - Self::Optional(optional) => { - write!(f, "({})?", optional.child) - } - Self::Alternatives(alternatives) => { - let mut done_first = false; - write!(f, "(")?; - for a in &alternatives.children { - if done_first { - write!(f, " | ")?; - } else { - done_first = true; - }; - a.fmt(f)?; - } - write!(f, ")")?; - Ok(()) - } - Self::Sequence(sequence) => { - let mut done_first = false; - for a in &sequence.children { - if done_first { - write!(f, " ")?; - } else { - done_first = true; - }; - a.fmt(f)?; - } - Ok(()) - } - Self::OneOrMore(one_or_more) => { - write!(f, "({})+", one_or_more.child) - } - Self::Ellipsis => write!(f, "..."), - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub(super) enum Kind { - Rule(RuleKind), - Token(TokenKind), -} - -impl fmt::Display for Kind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Kind::Rule(rule) => write!(f, "{rule}"), - Kind::Token(token) => write!(f, "{token}"), - } - } -} - -#[derive(Clone)] -pub(super) enum NodeSelector { - Anonymous, - Kind { kind: Kind }, - Text { text: String }, - Label { label: NodeLabel }, - LabelAndKind { label: NodeLabel, kind: Kind }, - LabelAndText { label: NodeLabel, text: String }, -} - -impl fmt::Display for NodeSelector { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fn escape_string(string: &str) -> String { - string - .chars() - .map(|c| match c { - '"' => "\\\"".to_string(), - '\\' => "\\\\".to_string(), - '\n' => "\\n".to_string(), - '\r' => "\\r".to_string(), - '\t' => "\\t".to_string(), - '\u{08}' => "\\b".to_string(), - '\u{0c}' => "\\f".to_string(), - _ if c.is_ascii_graphic() => c.to_string(), - _ => format!("\\u{{{:x}}}", c as u32), - }) - .collect::() - } - - match self { - Self::Anonymous => write!(f, "_"), - Self::Kind { kind } => kind.fmt(f), - Self::Text { text } => write!(f, "\"{}\"", escape_string(text)), - Self::Label { label } => label.fmt(f), - Self::LabelAndKind { label, kind } => { - write!(f, "{label}; {kind}") - } - Self::LabelAndText { label, text } => { - write!(f, "{label}: \"{}\"", escape_string(text)) - } - } - } -} - -pub(super) struct BindingMatcher { - pub name: String, - pub child: Matcher, -} - -pub(super) struct NodeMatcher { - pub node_selector: NodeSelector, - pub child: Option, -} - -pub(super) struct SequenceMatcher { - pub children: Vec, -} - -pub(super) struct AlternativesMatcher { - pub children: Vec, -} - -pub(super) struct OptionalMatcher { - pub child: Matcher, -} - -pub(super) struct OneOrMoreMatcher { - pub child: Matcher, -} diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/parser.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/parser.rs deleted file mode 100644 index dc46cff704..0000000000 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/query/parser.rs +++ /dev/null @@ -1,267 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -use std::rc::Rc; - -use nom::branch::alt; -use nom::bytes::complete::{is_not, tag, take_while, take_while1, take_while_m_n}; -use nom::character::complete::{char, multispace0, multispace1, satisfy}; -use nom::combinator::{all_consuming, map_opt, map_res, opt, recognize, value, verify}; -use nom::error::VerboseError; -use nom::multi::{fold_many0, many0, many1}; -use nom::sequence::{delimited, pair, preceded, terminated}; -use nom::{Finish, IResult, Parser}; - -// This crate is copied to another crate, so all imports should be relative -use super::super::kinds::{NodeLabel, RuleKind, TokenKind}; -use super::model::{ - AlternativesMatcher, BindingMatcher, Kind, Matcher, NodeMatcher, NodeSelector, - OneOrMoreMatcher, OptionalMatcher, SequenceMatcher, -}; - -pub(super) fn parse_query(input: &str) -> Result { - all_consuming(preceded( - multispace0, - opt(binding_name_token) - .and(alt(( - parse_node, - delimited( - token('('), - pair(parse_node, many1(preceded(token('|'), parse_node))), - token(')'), - ) - .map(|(first, rest)| { - let mut children = vec![first]; - children.extend(rest); - Matcher::Alternatives(Rc::new(AlternativesMatcher { children })) - }), - ))) - .map(|(binding_name, child)| { - if let Some(name) = binding_name { - Matcher::Binding(Rc::new(BindingMatcher { name, child })) - } else { - child - } - }), - )) - .parse(input) - .finish() - .map(|(_, query)| query) - .map_err(|e| e.to_string()) -} - -fn parse_node(i: &str) -> IResult<&str, Matcher, VerboseError<&str>> { - delimited( - token('['), - parse_node_selector.and(many0(parse_match)), - token(']'), - ) - .map(|(id, mut children)| { - let child = if children.is_empty() { - None - } else if children.len() == 1 { - Some(children.pop().unwrap()) - } else { - Some(Matcher::Sequence(Rc::new(SequenceMatcher { children }))) - }; - Matcher::Node(Rc::new(NodeMatcher { - node_selector: id, - child, - })) - }) - .parse(i) -} - -fn parse_node_selector(input: &str) -> IResult<&str, NodeSelector, VerboseError<&str>> { - enum Tail { - Anonymous, - Kind(Kind), - Text(String), - } - - opt(label_token) - .and(alt(( - token('_').map(|_| Tail::Anonymous), - kind_token.map(Tail::Kind), - text_token.map(Tail::Text), - ))) - .map(|(label, tail)| match (label, tail) { - (None, Tail::Anonymous) => NodeSelector::Anonymous, - (None, Tail::Kind(kind)) => NodeSelector::Kind { kind }, - (None, Tail::Text(string)) => NodeSelector::Text { text: string }, - (Some(label), Tail::Anonymous) => NodeSelector::Label { label }, - (Some(label), Tail::Kind(kind)) => NodeSelector::LabelAndKind { label, kind }, - (Some(label), Tail::Text(text)) => NodeSelector::LabelAndText { label, text }, - }) - .parse(input) -} - -#[derive(Clone)] -enum Quantifier { - ZeroOrOne, - ZeroOrMore, - OneOrMore, -} - -fn parse_match(input: &str) -> IResult<&str, Matcher, VerboseError<&str>> { - opt(binding_name_token) - .and(alt(( - parse_node, - pair( - delimited(token('('), many1(parse_match), token(')')), - parse_trailing_quantifier, - ) - .map(|(mut children, quantifier)| { - let child = if children.len() == 1 { - children.pop().unwrap() - } else { - Matcher::Sequence(Rc::new(SequenceMatcher { children })) - }; - match quantifier { - Quantifier::ZeroOrOne => Matcher::Optional(Rc::new(OptionalMatcher { child })), - Quantifier::ZeroOrMore => Matcher::Optional(Rc::new(OptionalMatcher { - child: Matcher::OneOrMore(Rc::new(OneOrMoreMatcher { child })), - })), - Quantifier::OneOrMore => { - Matcher::OneOrMore(Rc::new(OneOrMoreMatcher { child })) - } - } - }), - delimited( - token('('), - pair(parse_match, many1(preceded(token('|'), parse_match))), - token(')'), - ) - .map(|(first, rest)| { - let mut children = vec![first]; - children.extend(rest); - Matcher::Alternatives(Rc::new(AlternativesMatcher { children })) - }), - ellipsis_token.map(|_| Matcher::Ellipsis), - ))) - .map(|(binding, child)| { - if let Some(name) = binding { - Matcher::Binding(Rc::new(BindingMatcher { name, child })) - } else { - child - } - }) - .parse(input) -} - -fn parse_trailing_quantifier(i: &str) -> IResult<&str, Quantifier, VerboseError<&str>> { - alt(( - value(Quantifier::ZeroOrOne, token('?')), - value(Quantifier::ZeroOrMore, token('*')), - value(Quantifier::OneOrMore, token('+')), - )) - .parse(i) -} - -fn raw_identifier(i: &str) -> IResult<&str, String, VerboseError<&str>> { - let identifier_head = satisfy(|c| c.is_alphabetic()); - let is_identifier_tail = |c: char| c == '_' || c.is_alphanumeric(); - recognize(alt(( - // single underscore is the anonymous syntax item, - // so we don't allow it as an identifier - char('_').and(take_while1(is_identifier_tail)), - identifier_head.and(take_while(is_identifier_tail)), - ))) - .map(|s: &str| s.to_string()) - .parse(i) -} - -fn binding_name_token(i: &str) -> IResult<&str, String, VerboseError<&str>> { - terminated(preceded(char('@'), raw_identifier), multispace0).parse(i) -} - -fn kind_token(i: &str) -> IResult<&str, Kind, VerboseError<&str>> { - terminated(raw_identifier, multispace0) - .map(|id| { - TokenKind::try_from(id.as_str()) - .map(Kind::Token) - .or_else(|_| RuleKind::try_from(id.as_str()).map(Kind::Rule)) - .unwrap( - // TODO(#554): report these errors to users - ) - }) - .parse(i) -} - -fn label_token(i: &str) -> IResult<&str, NodeLabel, VerboseError<&str>> { - terminated(raw_identifier, token(':')) - .map(|id| NodeLabel::try_from(id.as_str()).unwrap()) - .parse(i) -} - -fn text_token(i: &str) -> IResult<&str, String, VerboseError<&str>> { - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - enum Fragment<'a> { - EscapedChar(char), - SwallowedWhitespace, - UnescapedSequence(&'a str), - } - - let escaped_char = preceded( - char('\\'), - alt(( - map_opt( - map_res( - preceded( - char('u'), - delimited( - char('{'), - // 1 to 6 hex digits - take_while_m_n(1, 6, |c: char| c.is_ascii_hexdigit()), - char('}'), - ), - ), - // converted from hex - move |hex| u32::from_str_radix(hex, 16), - ), - // converted to a char - std::char::from_u32, - ), - value('\n', char('n')), - value('\r', char('r')), - value('\t', char('t')), - value('\u{08}', char('b')), - value('\u{0C}', char('f')), - value('\\', char('\\')), - value('"', char('"')), - )), - ) - .map(Fragment::EscapedChar); - - // any amount of whitespace, collapsed to nothing - let swallowed_whitespace = value( - Fragment::SwallowedWhitespace, - preceded(char('\\'), multispace1), - ); - - let unescaped_sequence = - verify(is_not("\"\\"), |s: &str| !s.is_empty()).map(Fragment::UnescapedSequence); - - let fragment = alt((unescaped_sequence, escaped_char, swallowed_whitespace)); - - delimited( - char('"'), - fold_many0(fragment, String::new, |mut string, fragment| { - match fragment { - Fragment::EscapedChar(c) => string.push(c), - Fragment::SwallowedWhitespace => {} - Fragment::UnescapedSequence(s) => string.push_str(s), - } - string - }), - char('"'), - ) - .parse(i) -} - -fn ellipsis_token(i: &str) -> IResult<&str, &str, VerboseError<&str>> { - terminated(tag("..."), multispace0).parse(i) -} - -fn token<'input>(c: char) -> impl Parser<&'input str, char, VerboseError<&'input str>> { - terminated(char(c), multispace0) -} diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/generated/text_index.rs b/crates/solidity/outputs/cargo/slang_solidity/src/generated/text_index.rs deleted file mode 100644 index 4304823e44..0000000000 --- a/crates/solidity/outputs/cargo/slang_solidity/src/generated/text_index.rs +++ /dev/null @@ -1,136 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -use std::fmt::Display; -use std::ops::{Add, AddAssign, Range, Sub, SubAssign}; - -use serde::Serialize; - -#[derive(Default, Copy, Clone, PartialEq, Eq, Debug, Serialize)] -pub struct TextIndex { - pub utf8: usize, - pub utf16: usize, - pub char: usize, -} - -impl TextIndex { - /// Shorthand for `TextIndex { utf8: 0, utf16: 0, char: 0 }`. - pub const ZERO: TextIndex = TextIndex { - utf8: 0, - utf16: 0, - char: 0, - }; -} - -impl PartialOrd for TextIndex { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TextIndex { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.utf8.cmp(&other.utf8) - } -} - -impl Display for TextIndex { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.utf8.fmt(f) - } -} - -impl From<&str> for TextIndex { - fn from(s: &str) -> Self { - let mut utf8 = 0; - let mut utf16 = 0; - let mut char = 0; - for c in s.chars() { - utf8 += c.len_utf8(); - utf16 += c.len_utf16(); - char += 1; - } - Self { utf8, utf16, char } - } -} - -impl From<&String> for TextIndex { - fn from(s: &String) -> Self { - let mut utf8 = 0; - let mut utf16 = 0; - let mut char = 0; - for c in s.chars() { - utf8 += c.len_utf8(); - utf16 += c.len_utf16(); - char += 1; - } - Self { utf8, utf16, char } - } -} - -impl Add for TextIndex { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - Self { - utf8: self.utf8 + rhs.utf8, - utf16: self.utf16 + rhs.utf16, - char: self.char + rhs.char, - } - } -} - -impl std::iter::Sum for TextIndex { - fn sum>(iter: I) -> Self { - iter.fold(Self::default(), Add::add) - } -} - -impl Sub for TextIndex { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - Self { - utf8: self.utf8 - rhs.utf8, - utf16: self.utf16 - rhs.utf16, - char: self.char - rhs.char, - } - } -} - -impl AddAssign for TextIndex { - fn add_assign(&mut self, rhs: Self) { - self.utf8 += rhs.utf8; - self.utf16 += rhs.utf16; - self.char += rhs.char; - } -} - -impl SubAssign for TextIndex { - fn sub_assign(&mut self, rhs: Self) { - self.utf8 -= rhs.utf8; - self.utf16 -= rhs.utf16; - self.char -= rhs.char; - } -} - -pub type TextRange = Range; - -pub trait TextRangeExtensions { - fn utf8(&self) -> Range; - fn utf16(&self) -> Range; - fn char(&self) -> Range; -} - -impl TextRangeExtensions for TextRange { - fn utf8(&self) -> Range { - self.start.utf8..self.end.utf8 - } - - fn utf16(&self) -> Range { - self.start.utf16..self.end.utf16 - } - - fn char(&self) -> Range { - self.start.char..self.end.char - } -} diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/lib.rs b/crates/solidity/outputs/cargo/slang_solidity/src/lib.rs index cbf7722d01..53f0e1fcc2 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/lib.rs +++ b/crates/solidity/outputs/cargo/slang_solidity/src/lib.rs @@ -1,5 +1,4 @@ mod generated; -mod user_defined; pub use generated::*; diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/main.rs b/crates/solidity/outputs/cargo/slang_solidity/src/main.rs index 54dfb99241..22d295cc03 100644 --- a/crates/solidity/outputs/cargo/slang_solidity/src/main.rs +++ b/crates/solidity/outputs/cargo/slang_solidity/src/main.rs @@ -13,7 +13,9 @@ use slang_solidity::language::Language; // This is a known issue, and we should remove this hack once there is a better solution from Cargo. // https://github.com/rust-lang/cargo/issues/1982 mod supress_api_dependencies { - use {ariadne as _, nom as _, serde as _, strum as _, strum_macros as _, thiserror as _}; + use { + ariadne as _, metaslang_cst as _, serde as _, strum as _, strum_macros as _, thiserror as _, + }; } #[derive(ClapParser, Debug)] diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/user_defined/mod.rs b/crates/solidity/outputs/cargo/slang_solidity/src/user_defined/mod.rs deleted file mode 100644 index 67350db2a2..0000000000 --- a/crates/solidity/outputs/cargo/slang_solidity/src/user_defined/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod query; diff --git a/crates/solidity/outputs/cargo/slang_solidity/src/user_defined/query.rs b/crates/solidity/outputs/cargo/slang_solidity/src/user_defined/query.rs deleted file mode 100644 index a685238ee3..0000000000 --- a/crates/solidity/outputs/cargo/slang_solidity/src/user_defined/query.rs +++ /dev/null @@ -1,15 +0,0 @@ -pub struct UserDefinedQueriesImpl; - -// TODO(#554): invoke these from the code that will build the scope graph: -impl crate::query::UserDefinedQueries for UserDefinedQueriesImpl { - fn query_one(_foo: &crate::cursor::Cursor) {} - - fn query_two(_foo: &crate::cursor::Cursor, _bar: &crate::cursor::Cursor) {} - - fn query_three( - _foo: &crate::cursor::Cursor, - _bar: &crate::cursor::Cursor, - _baz: &crate::cursor::Cursor, - ) { - } -} diff --git a/crates/solidity/outputs/cargo/slang_solidity_node_addon/Cargo.toml b/crates/solidity/outputs/cargo/slang_solidity_node_addon/Cargo.toml index b596cfbc36..b7e242b68b 100644 --- a/crates/solidity/outputs/cargo/slang_solidity_node_addon/Cargo.toml +++ b/crates/solidity/outputs/cargo/slang_solidity_node_addon/Cargo.toml @@ -37,9 +37,9 @@ napi-build = { workspace = true } [dependencies] ariadne = { workspace = true } +metaslang_cst = { workspace = true } napi = { workspace = true } napi-derive = { workspace = true } -nom = { workspace = true } semver = { workspace = true } serde = { workspace = true } serde_json = { workspace = true, optional = true } diff --git a/crates/testlang/inputs/language/src/definition.rs b/crates/testlang/inputs/language/src/definition.rs index f4aba43584..b43826dafb 100644 --- a/crates/testlang/inputs/language/src/definition.rs +++ b/crates/testlang/inputs/language/src/definition.rs @@ -304,11 +304,5 @@ codegen_language_macros::compile!(Language( ] ) ] - )], - queries = ( - // TODO(#554): replace with real queries: - query_one = "query 1 code", - query_two = "query 2 code", - query_three = "query 3 code" - ) + )] )); diff --git a/crates/testlang/outputs/cargo/slang_testlang/Cargo.toml b/crates/testlang/outputs/cargo/slang_testlang/Cargo.toml index c3ccc09864..d455f141e7 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/Cargo.toml +++ b/crates/testlang/outputs/cargo/slang_testlang/Cargo.toml @@ -14,7 +14,7 @@ testlang_language = { workspace = true } [dependencies] ariadne = { workspace = true } -nom = { workspace = true } +metaslang_cst = { workspace = true } semver = { workspace = true } serde = { workspace = true } strum = { workspace = true } diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/generated/kinds.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/generated/kinds.rs index c612090762..166db5216d 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/generated/kinds.rs +++ b/crates/testlang/outputs/cargo/slang_testlang/src/generated/generated/kinds.rs @@ -33,6 +33,8 @@ pub enum RuleKind { TreeNodeChildren, } +impl metaslang_cst::NonTerminalKind for RuleKind {} + #[derive( Debug, Eq, @@ -72,6 +74,8 @@ pub enum NodeLabel { Semicolon, } +impl metaslang_cst::EdgeKind for NodeLabel {} + #[derive( Debug, Eq, @@ -107,8 +111,8 @@ pub enum TokenKind { Whitespace, } -impl TokenKind { - pub fn is_trivia(&self) -> bool { +impl metaslang_cst::TerminalKind for TokenKind { + fn is_trivia(&self) -> bool { matches!(self, |Self::EndOfLine| Self::MultiLineComment | Self::SingleLineComment | Self::Whitespace) diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/mod.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/mod.rs index 6c56b9c553..93a1bebed4 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/mod.rs +++ b/crates/testlang/outputs/cargo/slang_testlang/src/generated/mod.rs @@ -4,17 +4,69 @@ pub(crate) mod parser_support; pub(crate) mod lexer; -pub mod cst; -pub mod cursor; -pub mod parse_error; -pub mod parse_output; -pub mod query; -pub mod text_index; - #[path = "generated/kinds.rs"] pub mod kinds; #[path = "generated/language.rs"] pub mod language; +pub mod parse_error; +pub mod parse_output; #[cfg(feature = "slang_napi_interfaces")] pub mod napi_interface; + +mod metaslang_cst { + #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] + // These derives are because default #[derive(...)] on a generic type implements only the trait + // with default bounds also implied for the generic types as well, i.e. + // + // #[derive(Clone)] // expands to `impl Clone for MyOption { ... }` (notice the `T: Clone`) + // struct MyOption(Option); + // + // This assumes that the underlying data type uses this internally, however it's only used as a + // type container/marker. + // + // A slightly more "correct" approach would be to implement the traits while skipping the bounds for + // the type marker, however this can be more noisy + pub enum KindTypes {} + + impl metaslang_cst::KindTypes for KindTypes { + type NonTerminalKind = crate::kinds::RuleKind; + type TerminalKind = crate::kinds::TokenKind; + type EdgeKind = crate::kinds::NodeLabel; + } +} + +pub mod cst { + use metaslang_cst::cst; + + use super::metaslang_cst::KindTypes; + + pub type Node = cst::Node; + pub type RuleNode = cst::NonTerminalNode; + pub type TokenNode = cst::TerminalNode; + pub type LabeledNode = cst::LabeledNode; +} + +pub mod cursor { + use metaslang_cst::cursor; + + use super::metaslang_cst::KindTypes; + + pub type Cursor = cursor::Cursor; + pub type CursorWithLabels = cursor::CursorWithLabels; +} + +pub mod query { + use metaslang_cst::query; + + use super::metaslang_cst::KindTypes; + + pub type Query = query::Query; + pub type QueryResult = query::QueryResult; + pub type QueryResultIterator = query::QueryResultIterator; +} + +pub mod text_index { + use metaslang_cst::text_index; + pub use text_index::{TextIndex, TextRange, TextRangeExtensions}; +} diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/cst.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/cst.rs index 56a62adcca..eff2d3bfe7 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/cst.rs +++ b/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/cst.rs @@ -17,9 +17,13 @@ pub enum NodeType { Token, } -impl RustNode { +pub trait NAPINodeExtensions { + fn into_js_either_node(self) -> Either; +} + +impl NAPINodeExtensions for RustNode { /// Converts the node into `napi` wrapper for `RuleNode | TokenNode` JS object. - pub fn into_js_either_node(self) -> Either { + fn into_js_either_node(self) -> Either { match self { RustNode::Rule(rule) => Either::A(RuleNode(rule)), RustNode::Token(token) => Either::B(TokenNode(token)), @@ -27,12 +31,6 @@ impl RustNode { } } -impl From for Either { - fn from(value: RustNode) -> Self { - value.into_js_either_node() - } -} - #[derive(Debug)] #[napi(namespace = "cst")] pub struct RuleNode(pub(crate) Rc); @@ -73,7 +71,7 @@ impl RuleNode { self.0 .children .iter() - .map(|child| child.node.clone().into()) + .map(|child| child.node.clone().into_js_either_node()) .collect() } diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/cursor.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/cursor.rs index 623cdfb7c6..3fd9f64c17 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/cursor.rs +++ b/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/cursor.rs @@ -9,7 +9,7 @@ use napi::Either; use napi_derive::napi; use text_index::{TextIndex, TextRange}; -use crate::napi_interface::cst::{self, RuleNode, TokenNode}; +use crate::napi_interface::cst::{self, NAPINodeExtensions, RuleNode, TokenNode}; use crate::napi_interface::{text_index, NodeLabel, RuleKind, RustCursor, TokenKind}; #[napi(namespace = "cursor")] @@ -55,7 +55,7 @@ impl Cursor { #[napi(ts_return_type = "cst.Node", catch_unwind)] pub fn node(&self) -> Either { - self.0.node().into() + self.0.node().into_js_either_node() } #[napi(getter, ts_return_type = "kinds.NodeLabel", catch_unwind)] diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/generated/ast_selectors.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/generated/ast_selectors.rs index 624533001f..3e0afe63b6 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/generated/ast_selectors.rs +++ b/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/generated/ast_selectors.rs @@ -7,7 +7,7 @@ use std::rc::Rc; use napi::Either; use napi_derive::napi; -use crate::napi_interface::cst::{RuleNode, TokenNode}; +use crate::napi_interface::cst::{NAPINodeExtensions, RuleNode, TokenNode}; use crate::napi_interface::{RuleKind, RustLabeledNode, RustNode, RustRuleNode, TokenKind}; // diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/parse_output.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/parse_output.rs index 994cf7de26..dd162e213b 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/parse_output.rs +++ b/crates/testlang/outputs/cargo/slang_testlang/src/generated/napi_interface/parse_output.rs @@ -3,7 +3,7 @@ use napi::Either; use napi_derive::napi; -use crate::napi_interface::cst::{RuleNode, TokenNode}; +use crate::napi_interface::cst::{NAPINodeExtensions, RuleNode, TokenNode}; use crate::napi_interface::{cursor, parse_error, RustParseOutput}; #[napi(namespace = "parse_output")] @@ -19,7 +19,7 @@ impl From for ParseOutput { impl ParseOutput { #[napi(ts_return_type = "cst.Node", catch_unwind)] pub fn tree(&self) -> Either { - self.0.tree().into() + self.0.tree().into_js_either_node() } #[napi(ts_return_type = "Array", catch_unwind)] diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/parser_support/parser_result.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/parser_support/parser_result.rs index 16e59411fa..f86d384932 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/parser_support/parser_result.rs +++ b/crates/testlang/outputs/cargo/slang_testlang/src/generated/parser_support/parser_result.rs @@ -2,6 +2,8 @@ use std::ops::ControlFlow; +use metaslang_cst::TerminalKind; + use crate::cst::{self, LabeledNode, Node}; use crate::kinds::{NodeLabel, RuleKind, TokenKind}; use crate::text_index::TextIndex; diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/parser_support/sequence_helper.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/parser_support/sequence_helper.rs index 38acbad7b5..cecb10b5de 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/parser_support/sequence_helper.rs +++ b/crates/testlang/outputs/cargo/slang_testlang/src/generated/parser_support/sequence_helper.rs @@ -2,6 +2,8 @@ use std::ops::ControlFlow; +use metaslang_cst::TerminalKind; + use crate::cst::{self, LabeledNode}; use crate::kinds::{NodeLabel, TokenKind}; use crate::parser_support::parser_result::{Match, ParserResult, PrattElement, SkippedUntil}; diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/engine.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/engine.rs deleted file mode 100644 index 2f794db725..0000000000 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/engine.rs +++ /dev/null @@ -1,487 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -use std::collections::HashMap; -use std::rc::Rc; - -// This crate is copied to another crate, so all imports should be relative -use super::super::cst; -use super::super::cursor::Cursor; -use super::model::{ - AlternativesMatcher, BindingMatcher, Kind, Matcher, NodeMatcher, NodeSelector, - OneOrMoreMatcher, OptionalMatcher, Query, SequenceMatcher, -}; - -impl Cursor { - pub fn query(self, queries: Vec) -> QueryResultIterator { - QueryResultIterator::new(self, queries) - } - - fn irrevocably_go_to_next_sibling(&mut self) -> bool { - if self.is_completed() { - false - } else { - if !self.go_to_next_sibling() { - self.complete(); - } - true - } - } - - fn matches_node_selector(&self, node_selector: &NodeSelector) -> bool { - match self.node() { - cst::Node::Rule(rule) => match node_selector { - NodeSelector::Anonymous => true, - NodeSelector::Kind { kind } => Kind::Rule(rule.kind) == *kind, - NodeSelector::Text { .. } => false, - NodeSelector::Label { label } => Some(*label) == self.label(), - NodeSelector::LabelAndKind { label, kind } => { - Some(*label) == self.label() && Kind::Rule(rule.kind) == *kind - } - NodeSelector::LabelAndText { .. } => false, - }, - - cst::Node::Token(token) => match node_selector { - NodeSelector::Anonymous => true, - NodeSelector::Kind { kind } => Kind::Token(token.kind) == *kind, - NodeSelector::Text { text } => token.text == *text, - NodeSelector::Label { label } => Some(*label) == self.label(), - NodeSelector::LabelAndKind { label, kind } => { - Some(*label) == self.label() && Kind::Token(token.kind) == *kind - } - NodeSelector::LabelAndText { label, text } => { - Some(*label) == self.label() && token.text == *text - } - }, - } - } -} - -impl Matcher { - // This allows for queries to pre-flight against a cursor without allocating - fn can_match(&self, cursor: &Cursor) -> bool { - match self { - Self::Binding(matcher) => matcher.child.can_match(cursor), - Self::Node(matcher) => cursor.matches_node_selector(&matcher.node_selector), - Self::Alternatives(matcher) => matcher.children.iter().any(|c| c.can_match(cursor)), - Self::Sequence(matcher) => matcher.children[0].can_match(cursor), - Self::OneOrMore(matcher) => matcher.child.can_match(cursor), - Self::Optional(_) => true, - Self::Ellipsis => true, - } - } - - fn create_combinator(&self, cursor: Cursor) -> CombinatorRef { - match self { - Self::Binding(matcher) => Box::new(BindingCombinator::new(Rc::clone(matcher), cursor)), - Self::Node(matcher) => Box::new(NodeCombinator::new(Rc::clone(matcher), cursor)), - Self::Sequence(matcher) => { - Box::new(SequenceCombinator::new(Rc::clone(matcher), cursor)) - } - Self::Alternatives(matcher) => { - Box::new(AlternativesCombinator::new(Rc::clone(matcher), cursor)) - } - Self::Optional(matcher) => { - Box::new(OptionalCombinator::new(Rc::clone(matcher), cursor)) - } - Self::OneOrMore(matcher) => { - Box::new(OneOrMoreCombinator::new(Rc::clone(matcher), cursor)) - } - Self::Ellipsis => Box::new(EllipsisCombinator::new(cursor)), - } - } -} - -pub struct QueryResult { - pub query_number: usize, - pub bindings: HashMap>, -} - -pub struct QueryResultIterator { - cursor: Cursor, - queries: Vec, - query_number: usize, - combinator: Option, -} - -impl QueryResultIterator { - fn new(cursor: Cursor, queries: Vec) -> Self { - Self { - cursor, - queries, - query_number: 0, - combinator: None, - } - } - - fn advance_to_next_possible_matching_query(&mut self) { - while !self.cursor.is_completed() { - while self.query_number < self.queries.len() { - let matcher = &self.queries[self.query_number].0; - if matcher.can_match(&self.cursor) { - self.combinator = Some(matcher.create_combinator(self.cursor.clone())); - return; - }; - self.query_number += 1; - } - self.cursor.go_to_next(); - self.query_number = 0; - } - } -} - -impl Iterator for QueryResultIterator { - type Item = QueryResult; - - fn next(&mut self) -> Option { - while !self.cursor.is_completed() { - if let Some(combinator) = self.combinator.as_mut() { - if combinator.next().is_some() { - let mut bindings = HashMap::new(); - combinator.accumulate_bindings(&mut bindings); - return Some(QueryResult { - query_number: self.query_number, - bindings, - }); - } - self.query_number += 1; - } - - self.advance_to_next_possible_matching_query(); - } - - None - } -} - -trait Combinator { - // None -> failed to match, you must backtrack. DO NOT call again - // Some(cursor) if cursor.is_complete -> matched, end of input - // Some(cursor) if !cursor.is_complete -> matched, more input to go - fn next(&mut self) -> Option; - fn accumulate_bindings(&self, bindings: &mut HashMap>); -} -type CombinatorRef = Box; - -struct BindingCombinator { - matcher: Rc, - cursor: Cursor, - child: CombinatorRef, -} - -impl BindingCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - let child = matcher.child.create_combinator(cursor.clone()); - Self { - matcher, - cursor, - child, - } - } -} - -impl Combinator for BindingCombinator { - fn next(&mut self) -> Option { - self.child.next() - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - bindings - .entry(self.matcher.name.clone()) - .or_default() - .push(self.cursor.clone()); - self.child.accumulate_bindings(bindings); - } -} - -struct NodeCombinator { - matcher: Rc, - child: Option, - cursor: Cursor, - is_initialised: bool, -} - -impl NodeCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - Self { - matcher, - child: None, - cursor, - is_initialised: false, - } - } -} - -impl Combinator for NodeCombinator { - fn next(&mut self) -> Option { - if self.cursor.is_completed() { - return None; - } - - if !self.is_initialised { - self.is_initialised = true; - - if !self - .cursor - .matches_node_selector(&self.matcher.node_selector) - { - return None; - } - - if let Some(child) = self.matcher.child.as_ref() { - let mut child_cursor = self.cursor.clone(); - if !child_cursor.go_to_first_child() { - return None; - } - - self.child = Some(child.create_combinator(child_cursor)); - } else { - let mut return_cursor = self.cursor.clone(); - return_cursor.irrevocably_go_to_next_sibling(); - return Some(return_cursor); - } - } - - if let Some(child) = self.child.as_mut() { - while let Some(cursor) = child.as_mut().next() { - if cursor.is_completed() { - let mut return_cursor = self.cursor.clone(); - return_cursor.irrevocably_go_to_next_sibling(); - return Some(return_cursor); - } - } - self.child = None; - } - - None - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - if let Some(child) = self.child.as_ref() { - child.accumulate_bindings(bindings); - } - } -} - -struct SequenceCombinator { - matcher: Rc, - children: Vec, - cursor: Cursor, - is_initialised: bool, -} - -impl SequenceCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - Self { - matcher, - children: vec![], - cursor, - is_initialised: false, - } - } -} - -impl Combinator for SequenceCombinator { - fn next(&mut self) -> Option { - if !self.is_initialised { - self.is_initialised = true; - - let child_cursor = self.cursor.clone(); - let child = self.matcher.children[0].create_combinator(child_cursor); - self.children.push(child); - } - - while !self.children.is_empty() { - if let Some(child_cursor) = self.children.last_mut().unwrap().next() { - if self.children.len() == self.matcher.children.len() { - return Some(child_cursor); - } - - let child = - self.matcher.children[self.children.len()].create_combinator(child_cursor); - self.children.push(child); - } else { - self.children.pop(); - } - } - - None - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - for child in &self.children { - child.accumulate_bindings(bindings); - } - } -} - -struct AlternativesCombinator { - matcher: Rc, - next_child_number: usize, - child: Option, - cursor: Cursor, -} - -impl AlternativesCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - Self { - matcher, - next_child_number: 0, - child: None, - cursor, - } - } -} - -impl Combinator for AlternativesCombinator { - fn next(&mut self) -> Option { - loop { - if self.child.is_none() { - match self.matcher.children.get(self.next_child_number) { - Some(child) => { - let child = child.create_combinator(self.cursor.clone()); - self.child = Some(child); - self.next_child_number += 1; - } - None => return None, - } - } - - match self.child.as_mut().unwrap().next() { - Some(cursor) => return Some(cursor), - None => self.child = None, - } - } - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - self.child.as_ref().unwrap().accumulate_bindings(bindings); - } -} - -struct OptionalCombinator { - matcher: Rc, - child: Option, - cursor: Cursor, - have_nonempty_match: bool, -} - -impl OptionalCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - Self { - matcher, - child: None, - cursor, - have_nonempty_match: false, - } - } -} - -impl Combinator for OptionalCombinator { - fn next(&mut self) -> Option { - if let Some(child) = self.child.as_mut() { - match child.next() { - result @ Some(_) => { - self.have_nonempty_match = true; - result - } - None => { - self.child = None; - None - } - } - } else { - let child_cursor = self.cursor.clone(); - let child = self.matcher.child.create_combinator(child_cursor); - self.child = Some(child); - Some(self.cursor.clone()) - } - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - if self.have_nonempty_match { - if let Some(child) = self.child.as_ref() { - child.accumulate_bindings(bindings); - } - } - } -} - -struct OneOrMoreCombinator { - matcher: Rc, - children: Vec, - cursor_for_next_repetition: Option, -} - -impl OneOrMoreCombinator { - fn new(matcher: Rc, cursor: Cursor) -> Self { - let cursor_for_next_repetition = Some(cursor); - Self { - matcher, - children: vec![], - cursor_for_next_repetition, - } - } -} - -impl Combinator for OneOrMoreCombinator { - fn next(&mut self) -> Option { - loop { - if let Some(cursor_for_next_repetition) = self.cursor_for_next_repetition.take() { - let next_child = self - .matcher - .child - .create_combinator(cursor_for_next_repetition); - self.children.push(next_child); - } else { - let tail = self.children.last_mut().unwrap(); - if let Some(cursor) = tail.next() { - if !cursor.is_completed() { - self.cursor_for_next_repetition = Some(cursor.clone()); - } - return Some(cursor); - } - self.children.pop(); - if self.children.is_empty() { - return None; - } - } - } - } - - fn accumulate_bindings(&self, bindings: &mut HashMap>) { - for child in &self.children { - child.accumulate_bindings(bindings); - } - } -} - -struct EllipsisCombinator { - cursor: Cursor, - has_returned_initial_empty_value: bool, -} - -impl EllipsisCombinator { - fn new(cursor: Cursor) -> Self { - Self { - cursor, - has_returned_initial_empty_value: false, - } - } -} - -impl Combinator for EllipsisCombinator { - fn next(&mut self) -> Option { - if !self.has_returned_initial_empty_value { - self.has_returned_initial_empty_value = true; - return Some(self.cursor.clone()); - } - - if self.cursor.irrevocably_go_to_next_sibling() { - return Some(self.cursor.clone()); - } - - None - } - - fn accumulate_bindings(&self, _bindings: &mut HashMap>) {} -} diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/generated/user_defined_queries.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/generated/user_defined_queries.rs deleted file mode 100644 index cd1e97bd4d..0000000000 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/generated/user_defined_queries.rs +++ /dev/null @@ -1,13 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -pub(crate) trait UserDefinedQueries { - fn query_one(foo: &crate::cursor::Cursor); - - fn query_two(foo: &crate::cursor::Cursor, bar: &crate::cursor::Cursor); - - fn query_three( - foo: &crate::cursor::Cursor, - bar: &crate::cursor::Cursor, - baz: &crate::cursor::Cursor, - ); -} diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/mod.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/mod.rs deleted file mode 100644 index 2b38245760..0000000000 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -mod engine; -mod model; -mod parser; - -#[path = "generated/user_defined_queries.rs"] -mod user_defined_queries; - -pub use engine::{QueryResult, QueryResultIterator}; -pub use model::Query; -pub(crate) use user_defined_queries::UserDefinedQueries; diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/model.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/model.rs deleted file mode 100644 index 50f270ab71..0000000000 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/model.rs +++ /dev/null @@ -1,174 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -use std::fmt; -use std::rc::Rc; - -// This crate is copied to another crate, so all imports should be relative -use super::super::kinds::{NodeLabel, RuleKind, TokenKind}; - -#[derive(Clone)] -pub struct Query(pub(super) Matcher); - -impl Query { - pub fn parse(text: &str) -> Result { - Matcher::parse(text).map(Self) - } -} - -impl fmt::Display for Query { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -#[derive(Clone)] -pub(super) enum Matcher { - Binding(Rc), - Node(Rc), - Optional(Rc), - Alternatives(Rc), - Sequence(Rc), - OneOrMore(Rc), - Ellipsis, -} - -impl Matcher { - fn parse(text: &str) -> Result { - super::parser::parse_query(text) - } -} - -impl fmt::Display for Matcher { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Binding(binding) => { - write!(f, "@{} {}", binding.name, binding.child) - } - Self::Node(node) => { - if let Some(child) = &node.child { - write!(f, "[{} {}]", node.node_selector, child) - } else { - write!(f, "[{}]", node.node_selector) - } - } - Self::Optional(optional) => { - write!(f, "({})?", optional.child) - } - Self::Alternatives(alternatives) => { - let mut done_first = false; - write!(f, "(")?; - for a in &alternatives.children { - if done_first { - write!(f, " | ")?; - } else { - done_first = true; - }; - a.fmt(f)?; - } - write!(f, ")")?; - Ok(()) - } - Self::Sequence(sequence) => { - let mut done_first = false; - for a in &sequence.children { - if done_first { - write!(f, " ")?; - } else { - done_first = true; - }; - a.fmt(f)?; - } - Ok(()) - } - Self::OneOrMore(one_or_more) => { - write!(f, "({})+", one_or_more.child) - } - Self::Ellipsis => write!(f, "..."), - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub(super) enum Kind { - Rule(RuleKind), - Token(TokenKind), -} - -impl fmt::Display for Kind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Kind::Rule(rule) => write!(f, "{rule}"), - Kind::Token(token) => write!(f, "{token}"), - } - } -} - -#[derive(Clone)] -pub(super) enum NodeSelector { - Anonymous, - Kind { kind: Kind }, - Text { text: String }, - Label { label: NodeLabel }, - LabelAndKind { label: NodeLabel, kind: Kind }, - LabelAndText { label: NodeLabel, text: String }, -} - -impl fmt::Display for NodeSelector { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fn escape_string(string: &str) -> String { - string - .chars() - .map(|c| match c { - '"' => "\\\"".to_string(), - '\\' => "\\\\".to_string(), - '\n' => "\\n".to_string(), - '\r' => "\\r".to_string(), - '\t' => "\\t".to_string(), - '\u{08}' => "\\b".to_string(), - '\u{0c}' => "\\f".to_string(), - _ if c.is_ascii_graphic() => c.to_string(), - _ => format!("\\u{{{:x}}}", c as u32), - }) - .collect::() - } - - match self { - Self::Anonymous => write!(f, "_"), - Self::Kind { kind } => kind.fmt(f), - Self::Text { text } => write!(f, "\"{}\"", escape_string(text)), - Self::Label { label } => label.fmt(f), - Self::LabelAndKind { label, kind } => { - write!(f, "{label}; {kind}") - } - Self::LabelAndText { label, text } => { - write!(f, "{label}: \"{}\"", escape_string(text)) - } - } - } -} - -pub(super) struct BindingMatcher { - pub name: String, - pub child: Matcher, -} - -pub(super) struct NodeMatcher { - pub node_selector: NodeSelector, - pub child: Option, -} - -pub(super) struct SequenceMatcher { - pub children: Vec, -} - -pub(super) struct AlternativesMatcher { - pub children: Vec, -} - -pub(super) struct OptionalMatcher { - pub child: Matcher, -} - -pub(super) struct OneOrMoreMatcher { - pub child: Matcher, -} diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/parser.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/parser.rs deleted file mode 100644 index dc46cff704..0000000000 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/query/parser.rs +++ /dev/null @@ -1,267 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -use std::rc::Rc; - -use nom::branch::alt; -use nom::bytes::complete::{is_not, tag, take_while, take_while1, take_while_m_n}; -use nom::character::complete::{char, multispace0, multispace1, satisfy}; -use nom::combinator::{all_consuming, map_opt, map_res, opt, recognize, value, verify}; -use nom::error::VerboseError; -use nom::multi::{fold_many0, many0, many1}; -use nom::sequence::{delimited, pair, preceded, terminated}; -use nom::{Finish, IResult, Parser}; - -// This crate is copied to another crate, so all imports should be relative -use super::super::kinds::{NodeLabel, RuleKind, TokenKind}; -use super::model::{ - AlternativesMatcher, BindingMatcher, Kind, Matcher, NodeMatcher, NodeSelector, - OneOrMoreMatcher, OptionalMatcher, SequenceMatcher, -}; - -pub(super) fn parse_query(input: &str) -> Result { - all_consuming(preceded( - multispace0, - opt(binding_name_token) - .and(alt(( - parse_node, - delimited( - token('('), - pair(parse_node, many1(preceded(token('|'), parse_node))), - token(')'), - ) - .map(|(first, rest)| { - let mut children = vec![first]; - children.extend(rest); - Matcher::Alternatives(Rc::new(AlternativesMatcher { children })) - }), - ))) - .map(|(binding_name, child)| { - if let Some(name) = binding_name { - Matcher::Binding(Rc::new(BindingMatcher { name, child })) - } else { - child - } - }), - )) - .parse(input) - .finish() - .map(|(_, query)| query) - .map_err(|e| e.to_string()) -} - -fn parse_node(i: &str) -> IResult<&str, Matcher, VerboseError<&str>> { - delimited( - token('['), - parse_node_selector.and(many0(parse_match)), - token(']'), - ) - .map(|(id, mut children)| { - let child = if children.is_empty() { - None - } else if children.len() == 1 { - Some(children.pop().unwrap()) - } else { - Some(Matcher::Sequence(Rc::new(SequenceMatcher { children }))) - }; - Matcher::Node(Rc::new(NodeMatcher { - node_selector: id, - child, - })) - }) - .parse(i) -} - -fn parse_node_selector(input: &str) -> IResult<&str, NodeSelector, VerboseError<&str>> { - enum Tail { - Anonymous, - Kind(Kind), - Text(String), - } - - opt(label_token) - .and(alt(( - token('_').map(|_| Tail::Anonymous), - kind_token.map(Tail::Kind), - text_token.map(Tail::Text), - ))) - .map(|(label, tail)| match (label, tail) { - (None, Tail::Anonymous) => NodeSelector::Anonymous, - (None, Tail::Kind(kind)) => NodeSelector::Kind { kind }, - (None, Tail::Text(string)) => NodeSelector::Text { text: string }, - (Some(label), Tail::Anonymous) => NodeSelector::Label { label }, - (Some(label), Tail::Kind(kind)) => NodeSelector::LabelAndKind { label, kind }, - (Some(label), Tail::Text(text)) => NodeSelector::LabelAndText { label, text }, - }) - .parse(input) -} - -#[derive(Clone)] -enum Quantifier { - ZeroOrOne, - ZeroOrMore, - OneOrMore, -} - -fn parse_match(input: &str) -> IResult<&str, Matcher, VerboseError<&str>> { - opt(binding_name_token) - .and(alt(( - parse_node, - pair( - delimited(token('('), many1(parse_match), token(')')), - parse_trailing_quantifier, - ) - .map(|(mut children, quantifier)| { - let child = if children.len() == 1 { - children.pop().unwrap() - } else { - Matcher::Sequence(Rc::new(SequenceMatcher { children })) - }; - match quantifier { - Quantifier::ZeroOrOne => Matcher::Optional(Rc::new(OptionalMatcher { child })), - Quantifier::ZeroOrMore => Matcher::Optional(Rc::new(OptionalMatcher { - child: Matcher::OneOrMore(Rc::new(OneOrMoreMatcher { child })), - })), - Quantifier::OneOrMore => { - Matcher::OneOrMore(Rc::new(OneOrMoreMatcher { child })) - } - } - }), - delimited( - token('('), - pair(parse_match, many1(preceded(token('|'), parse_match))), - token(')'), - ) - .map(|(first, rest)| { - let mut children = vec![first]; - children.extend(rest); - Matcher::Alternatives(Rc::new(AlternativesMatcher { children })) - }), - ellipsis_token.map(|_| Matcher::Ellipsis), - ))) - .map(|(binding, child)| { - if let Some(name) = binding { - Matcher::Binding(Rc::new(BindingMatcher { name, child })) - } else { - child - } - }) - .parse(input) -} - -fn parse_trailing_quantifier(i: &str) -> IResult<&str, Quantifier, VerboseError<&str>> { - alt(( - value(Quantifier::ZeroOrOne, token('?')), - value(Quantifier::ZeroOrMore, token('*')), - value(Quantifier::OneOrMore, token('+')), - )) - .parse(i) -} - -fn raw_identifier(i: &str) -> IResult<&str, String, VerboseError<&str>> { - let identifier_head = satisfy(|c| c.is_alphabetic()); - let is_identifier_tail = |c: char| c == '_' || c.is_alphanumeric(); - recognize(alt(( - // single underscore is the anonymous syntax item, - // so we don't allow it as an identifier - char('_').and(take_while1(is_identifier_tail)), - identifier_head.and(take_while(is_identifier_tail)), - ))) - .map(|s: &str| s.to_string()) - .parse(i) -} - -fn binding_name_token(i: &str) -> IResult<&str, String, VerboseError<&str>> { - terminated(preceded(char('@'), raw_identifier), multispace0).parse(i) -} - -fn kind_token(i: &str) -> IResult<&str, Kind, VerboseError<&str>> { - terminated(raw_identifier, multispace0) - .map(|id| { - TokenKind::try_from(id.as_str()) - .map(Kind::Token) - .or_else(|_| RuleKind::try_from(id.as_str()).map(Kind::Rule)) - .unwrap( - // TODO(#554): report these errors to users - ) - }) - .parse(i) -} - -fn label_token(i: &str) -> IResult<&str, NodeLabel, VerboseError<&str>> { - terminated(raw_identifier, token(':')) - .map(|id| NodeLabel::try_from(id.as_str()).unwrap()) - .parse(i) -} - -fn text_token(i: &str) -> IResult<&str, String, VerboseError<&str>> { - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - enum Fragment<'a> { - EscapedChar(char), - SwallowedWhitespace, - UnescapedSequence(&'a str), - } - - let escaped_char = preceded( - char('\\'), - alt(( - map_opt( - map_res( - preceded( - char('u'), - delimited( - char('{'), - // 1 to 6 hex digits - take_while_m_n(1, 6, |c: char| c.is_ascii_hexdigit()), - char('}'), - ), - ), - // converted from hex - move |hex| u32::from_str_radix(hex, 16), - ), - // converted to a char - std::char::from_u32, - ), - value('\n', char('n')), - value('\r', char('r')), - value('\t', char('t')), - value('\u{08}', char('b')), - value('\u{0C}', char('f')), - value('\\', char('\\')), - value('"', char('"')), - )), - ) - .map(Fragment::EscapedChar); - - // any amount of whitespace, collapsed to nothing - let swallowed_whitespace = value( - Fragment::SwallowedWhitespace, - preceded(char('\\'), multispace1), - ); - - let unescaped_sequence = - verify(is_not("\"\\"), |s: &str| !s.is_empty()).map(Fragment::UnescapedSequence); - - let fragment = alt((unescaped_sequence, escaped_char, swallowed_whitespace)); - - delimited( - char('"'), - fold_many0(fragment, String::new, |mut string, fragment| { - match fragment { - Fragment::EscapedChar(c) => string.push(c), - Fragment::SwallowedWhitespace => {} - Fragment::UnescapedSequence(s) => string.push_str(s), - } - string - }), - char('"'), - ) - .parse(i) -} - -fn ellipsis_token(i: &str) -> IResult<&str, &str, VerboseError<&str>> { - terminated(tag("..."), multispace0).parse(i) -} - -fn token<'input>(c: char) -> impl Parser<&'input str, char, VerboseError<&'input str>> { - terminated(char(c), multispace0) -} diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/generated/text_index.rs b/crates/testlang/outputs/cargo/slang_testlang/src/generated/text_index.rs deleted file mode 100644 index 4304823e44..0000000000 --- a/crates/testlang/outputs/cargo/slang_testlang/src/generated/text_index.rs +++ /dev/null @@ -1,136 +0,0 @@ -// This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -use std::fmt::Display; -use std::ops::{Add, AddAssign, Range, Sub, SubAssign}; - -use serde::Serialize; - -#[derive(Default, Copy, Clone, PartialEq, Eq, Debug, Serialize)] -pub struct TextIndex { - pub utf8: usize, - pub utf16: usize, - pub char: usize, -} - -impl TextIndex { - /// Shorthand for `TextIndex { utf8: 0, utf16: 0, char: 0 }`. - pub const ZERO: TextIndex = TextIndex { - utf8: 0, - utf16: 0, - char: 0, - }; -} - -impl PartialOrd for TextIndex { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TextIndex { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.utf8.cmp(&other.utf8) - } -} - -impl Display for TextIndex { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.utf8.fmt(f) - } -} - -impl From<&str> for TextIndex { - fn from(s: &str) -> Self { - let mut utf8 = 0; - let mut utf16 = 0; - let mut char = 0; - for c in s.chars() { - utf8 += c.len_utf8(); - utf16 += c.len_utf16(); - char += 1; - } - Self { utf8, utf16, char } - } -} - -impl From<&String> for TextIndex { - fn from(s: &String) -> Self { - let mut utf8 = 0; - let mut utf16 = 0; - let mut char = 0; - for c in s.chars() { - utf8 += c.len_utf8(); - utf16 += c.len_utf16(); - char += 1; - } - Self { utf8, utf16, char } - } -} - -impl Add for TextIndex { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - Self { - utf8: self.utf8 + rhs.utf8, - utf16: self.utf16 + rhs.utf16, - char: self.char + rhs.char, - } - } -} - -impl std::iter::Sum for TextIndex { - fn sum>(iter: I) -> Self { - iter.fold(Self::default(), Add::add) - } -} - -impl Sub for TextIndex { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - Self { - utf8: self.utf8 - rhs.utf8, - utf16: self.utf16 - rhs.utf16, - char: self.char - rhs.char, - } - } -} - -impl AddAssign for TextIndex { - fn add_assign(&mut self, rhs: Self) { - self.utf8 += rhs.utf8; - self.utf16 += rhs.utf16; - self.char += rhs.char; - } -} - -impl SubAssign for TextIndex { - fn sub_assign(&mut self, rhs: Self) { - self.utf8 -= rhs.utf8; - self.utf16 -= rhs.utf16; - self.char -= rhs.char; - } -} - -pub type TextRange = Range; - -pub trait TextRangeExtensions { - fn utf8(&self) -> Range; - fn utf16(&self) -> Range; - fn char(&self) -> Range; -} - -impl TextRangeExtensions for TextRange { - fn utf8(&self) -> Range { - self.start.utf8..self.end.utf8 - } - - fn utf16(&self) -> Range { - self.start.utf16..self.end.utf16 - } - - fn char(&self) -> Range { - self.start.char..self.end.char - } -} diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/lib.rs b/crates/testlang/outputs/cargo/slang_testlang/src/lib.rs index 14828276ef..086cd6d549 100644 --- a/crates/testlang/outputs/cargo/slang_testlang/src/lib.rs +++ b/crates/testlang/outputs/cargo/slang_testlang/src/lib.rs @@ -1,4 +1,3 @@ mod generated; -mod user_defined; pub use generated::*; diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/user_defined/mod.rs b/crates/testlang/outputs/cargo/slang_testlang/src/user_defined/mod.rs deleted file mode 100644 index 67350db2a2..0000000000 --- a/crates/testlang/outputs/cargo/slang_testlang/src/user_defined/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod query; diff --git a/crates/testlang/outputs/cargo/slang_testlang/src/user_defined/query.rs b/crates/testlang/outputs/cargo/slang_testlang/src/user_defined/query.rs deleted file mode 100644 index a685238ee3..0000000000 --- a/crates/testlang/outputs/cargo/slang_testlang/src/user_defined/query.rs +++ /dev/null @@ -1,15 +0,0 @@ -pub struct UserDefinedQueriesImpl; - -// TODO(#554): invoke these from the code that will build the scope graph: -impl crate::query::UserDefinedQueries for UserDefinedQueriesImpl { - fn query_one(_foo: &crate::cursor::Cursor) {} - - fn query_two(_foo: &crate::cursor::Cursor, _bar: &crate::cursor::Cursor) {} - - fn query_three( - _foo: &crate::cursor::Cursor, - _bar: &crate::cursor::Cursor, - _baz: &crate::cursor::Cursor, - ) { - } -} diff --git a/crates/testlang/outputs/cargo/slang_testlang_node_addon/Cargo.toml b/crates/testlang/outputs/cargo/slang_testlang_node_addon/Cargo.toml index df3867e1ed..1891e19329 100644 --- a/crates/testlang/outputs/cargo/slang_testlang_node_addon/Cargo.toml +++ b/crates/testlang/outputs/cargo/slang_testlang_node_addon/Cargo.toml @@ -37,9 +37,9 @@ napi-build = { workspace = true } [dependencies] ariadne = { workspace = true } +metaslang_cst = { workspace = true } napi = { workspace = true } napi-derive = { workspace = true } -nom = { workspace = true } semver = { workspace = true } serde = { workspace = true } serde_json = { workspace = true, optional = true } diff --git a/crates/testlang/outputs/cargo/tests/src/query/engine_tests.rs b/crates/testlang/outputs/cargo/tests/src/query/engine_tests.rs index e102eedef7..7ef5fad059 100644 --- a/crates/testlang/outputs/cargo/tests/src/query/engine_tests.rs +++ b/crates/testlang/outputs/cargo/tests/src/query/engine_tests.rs @@ -1,6 +1,5 @@ use std::collections::{BTreeMap, HashMap}; -// This crate is copied to another crate, so all imports should be relative use slang_testlang::cst::{LabeledNode, Node}; use slang_testlang::cursor::Cursor; use slang_testlang::kinds::{NodeLabel, RuleKind, TokenKind};