Skip to content

Commit

Permalink
chore: add Grit target node bindings (#2746)
Browse files Browse the repository at this point in the history
  • Loading branch information
arendjr committed May 6, 2024
1 parent 62beb76 commit b9482ec
Show file tree
Hide file tree
Showing 14 changed files with 496 additions and 34 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/biome_grit_patterns/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ biome_console = { workspace = true }
biome_diagnostics = { workspace = true }
biome_grit_parser = { workspace = true }
biome_grit_syntax = { workspace = true }
biome_js_syntax = { workspace = true }
biome_parser = { workspace = true }
biome_rowan = { workspace = true }
grit-pattern-matcher = { version = "0.2" }
grit-util = { version = "0.2" }
Expand Down
15 changes: 8 additions & 7 deletions crates/biome_grit_patterns/src/grit_binding.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
grit_context::GritQueryContext, grit_node::GritNode, grit_target_language::GritTargetLanguage,
grit_context::GritQueryContext, grit_target_language::GritTargetLanguage,
grit_target_node::GritTargetNode,
};
use grit_pattern_matcher::{binding::Binding, constant::Constant};
use grit_util::{ByteRange, CodeRange, Range};
Expand All @@ -13,7 +14,7 @@ impl<'a> Binding<'a, GritQueryContext> for GritBinding {
todo!()
}

fn from_node(_node: GritNode) -> Self {
fn from_node(_node: GritTargetNode) -> Self {
todo!()
}

Expand All @@ -25,7 +26,7 @@ impl<'a> Binding<'a, GritQueryContext> for GritBinding {
todo!()
}

fn singleton(&self) -> Option<GritNode> {
fn singleton(&self) -> Option<GritTargetNode> {
todo!()
}

Expand Down Expand Up @@ -90,19 +91,19 @@ impl<'a> Binding<'a, GritQueryContext> for GritBinding {
todo!()
}

fn as_node(&self) -> Option<GritNode> {
fn as_node(&self) -> Option<GritTargetNode> {
todo!()
}

fn is_list(&self) -> bool {
todo!()
}

fn list_items(&self) -> Option<impl Iterator<Item = GritNode> + Clone> {
fn list_items(&self) -> Option<impl Iterator<Item = GritTargetNode> + Clone> {
None::<TodoIterator>
}

fn parent_node(&self) -> Option<GritNode> {
fn parent_node(&self) -> Option<GritTargetNode> {
todo!()
}

Expand All @@ -123,7 +124,7 @@ impl<'a> Binding<'a, GritQueryContext> for GritBinding {
struct TodoIterator;

impl Iterator for TodoIterator {
type Item = GritNode;
type Item = GritTargetNode;

fn next(&mut self) -> Option<Self::Item> {
todo!()
Expand Down
4 changes: 2 additions & 2 deletions crates/biome_grit_patterns/src/grit_context.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::grit_binding::GritBinding;
use crate::grit_code_snippet::GritCodeSnippet;
use crate::grit_file::GritFile;
use crate::grit_node::GritNode;
use crate::grit_node_patterns::{GritLeafNodePattern, GritNodePattern};
use crate::grit_target_language::GritTargetLanguage;
use crate::grit_target_node::GritTargetNode;
use crate::grit_tree::GritTree;
use crate::resolved_pattern::GritResolvedPattern;
use anyhow::Result;
Expand All @@ -18,7 +18,7 @@ use grit_util::AnalysisLogs;
pub(crate) struct GritQueryContext;

impl QueryContext for GritQueryContext {
type Node<'a> = GritNode;
type Node<'a> = GritTargetNode;
type NodePattern = GritNodePattern;
type LeafNodePattern = GritLeafNodePattern;
type ExecContext<'a> = GritExecContext;
Expand Down
4 changes: 4 additions & 0 deletions crates/biome_grit_patterns/src/grit_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ use biome_grit_syntax::GritSyntaxNode;
use grit_util::{AstCursor, AstNode as GritAstNode, ByteRange, CodeRange};
use std::{borrow::Cow, ops::Deref, str::Utf8Error};

/// Wrapper around `GritSyntaxNode` as produced by our internal Grit parser.
///
/// This enables us to implement the [`GritAstNode`] trait on Grit nodes, which
/// offers a bunch of utilities used by our node compilers.
#[derive(Clone, Debug)]
pub struct GritNode(GritSyntaxNode);

Expand Down
4 changes: 2 additions & 2 deletions crates/biome_grit_patterns/src/grit_node_patterns.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::grit_context::{GritExecContext, GritQueryContext};
use crate::grit_node::GritNode;
use crate::grit_target_node::GritTargetNode;
use crate::resolved_pattern::GritResolvedPattern;
use anyhow::Result;
use grit_pattern_matcher::pattern::{
Expand All @@ -15,7 +15,7 @@ impl AstNodePattern<GritQueryContext> for GritNodePattern {
todo!()
}

fn matches_kind_of(&self, _node: &GritNode) -> bool {
fn matches_kind_of(&self, _node: &GritTargetNode) -> bool {
todo!()
}
}
Expand Down
123 changes: 107 additions & 16 deletions crates/biome_grit_patterns/src/grit_target_language.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,116 @@
use crate::grit_node::GritNode;
mod js_target_language;

pub use js_target_language::JsTargetLanguage;

use crate::grit_target_node::{GritTargetNode, GritTargetSyntaxKind};
use biome_rowan::SyntaxKind;
use grit_util::Language;

#[derive(Clone, Copy)]
pub struct GritTargetLanguage;
/// Generates the `GritTargetLanguage` enum.
///
/// This enum contains a variant for every language that we support running Grit
/// queries on. We implement Grit's [`Language`] trait on this enum, and
/// implement the slightly more convenient [`GritTargetLanguageImpl`] for
/// creating language-specific implementations.
macro_rules! generate_target_language {
($($language:ident),+) => {
#[derive(Clone, Debug)]
pub enum GritTargetLanguage {
$($language($language)),+
}

impl Language for GritTargetLanguage {
type Node<'a> = GritNode;
$(impl From<$language> for GritTargetLanguage {
fn from(value: $language) -> Self {
Self::$language(value)
}
})+

fn language_name(&self) -> &'static str {
todo!()
}
impl GritTargetLanguage {
fn metavariable_kind(&self) -> GritTargetSyntaxKind {
match self {
$(Self::$language(_) => $language::metavariable_kind().into()),+
}
}

fn snippet_context_strings(&self) -> &[(&'static str, &'static str)] {
todo!()
}
fn is_alternative_metavariable_kind(&self, kind: GritTargetSyntaxKind) -> bool {
match self {
$(Self::$language(_) => $language::is_alternative_metavariable_kind(kind)),+
}
}
}

fn is_comment(&self, _node: &Self::Node<'_>) -> bool {
todo!()
}
impl Language for GritTargetLanguage {
type Node<'a> = GritTargetNode;

fn language_name(&self) -> &'static str {
match self {
$(Self::$language(language) => language.language_name()),+
}
}

fn snippet_context_strings(&self) -> &[(&'static str, &'static str)] {
match self {
$(Self::$language(language) => language.snippet_context_strings()),+
}
}

fn is_comment(&self, node: &GritTargetNode) -> bool {
match self {
$(Self::$language(language) => language.is_comment(node)),+
}
}

fn is_metavariable(&self, node: &GritTargetNode) -> bool {
node.kind() == self.metavariable_kind()
|| (self.is_alternative_metavariable_kind(node.kind())
&& self.exact_replaced_variable_regex().is_match(&node.text_trimmed().to_string()))
}
}
};
}

generate_target_language! {
JsTargetLanguage
}

/// Trait to be implemented by the language-specific implementations.
///
/// This is used to make language implementations a little easier, by not
/// forcing them to reimplement methods that are common across implementations.
trait GritTargetLanguageImpl {
type Kind: SyntaxKind;

fn language_name(&self) -> &'static str;

/// Strings that provide context for parsing snippets.
///
/// Snippet contexts help when a snippet is a valid AST subtree, but needs
/// to be in a larger tree to parse. For example, matching on a table name
/// like ` $schema.$table` in SQL is not valid SQL by itself, only when
/// surrounded by something like `SELECT x from $schema.$table` is the
/// snippet valid.
///
/// This method returns a list of strings that are used to match the snippet
/// in the larger tree. For example, the SQL implementation returns
/// `["SELECT 1 from ", ";"]` to match a table name in a SQL query.
fn snippet_context_strings(&self) -> &[(&'static str, &'static str)];

/// Determines whether the given target node is a comment.
fn is_comment(&self, node: &GritTargetNode) -> bool;

/// Returns the syntax kind for metavariables.
fn metavariable_kind() -> Self::Kind;

fn is_metavariable(&self, _node: &Self::Node<'_>) -> bool {
todo!()
/// Returns whether the given syntax kind is an "alternative" kind for
/// metavariables.
///
/// For example, in JavaScript, the content of a template string may also
/// contain metavariables.
///
/// Note that any node kind for which this returns `true` should have a
/// (trimmed) text representation which corresponds exactly to the
/// metavariable representation.
fn is_alternative_metavariable_kind(_kind: GritTargetSyntaxKind) -> bool {
false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use super::GritTargetLanguageImpl;
use crate::grit_target_node::{GritTargetNode, GritTargetSyntaxKind};
use biome_js_syntax::JsSyntaxKind;
use biome_parser::{token_set, TokenSet};

const COMMENT_KINDS: TokenSet<JsSyntaxKind> =
token_set![JsSyntaxKind::COMMENT, JsSyntaxKind::MULTILINE_COMMENT];

#[derive(Clone, Debug)]
pub struct JsTargetLanguage;

impl GritTargetLanguageImpl for JsTargetLanguage {
type Kind = JsSyntaxKind;

fn language_name(&self) -> &'static str {
"JavaScript"
}

fn snippet_context_strings(&self) -> &[(&'static str, &'static str)] {
&[
("", ""),
("import ", " from 'GRIT_PACKAGE';"),
("GRIT_VALUE ", " GRIT_VALUE"),
("class GRIT_CLASS ", " {}"),
("class GRIT_CLASS { ", " GRIT_PROP = 'GRIT_VALUE'; }"),
("", " function GRIT_FUNCTION() {}"),
("GRIT_OBJ = { ", " }"),
("class GRIT_CLASS { ", " }"),
("GRIT_VAR = ", ""),
("<f>", "</f>"),
("<f ", " />"),
("function GRIT_FN(", ") {}"),
("var ", ";"),
("", " class GRIT_CLASS {}"),
("function GRIT_FN(GRIT_ARG:", ") { }"),
("import { ", " } from 'GRIT_PACKAGE'"),
("function GRIT_FN(GRIT_ARG", ") { }"),
("GRIT_FN<{ ", " }>();"),
]
}

fn is_comment(&self, node: &GritTargetNode) -> bool {
node.kind()
.as_js_kind()
.map_or(false, |kind| COMMENT_KINDS.contains(kind))
}

fn metavariable_kind() -> Self::Kind {
JsSyntaxKind::JS_GRIT_METAVARIABLE
}

fn is_alternative_metavariable_kind(kind: GritTargetSyntaxKind) -> bool {
kind.as_js_kind().map_or(false, |kind| {
kind == JsSyntaxKind::JS_TEMPLATE_ELEMENT_LIST
|| kind == JsSyntaxKind::TS_TEMPLATE_ELEMENT_LIST
})
}
}
Loading

0 comments on commit b9482ec

Please sign in to comment.