Skip to content

Commit

Permalink
chore(grit): implement Grit snippet bindings (#3162)
Browse files Browse the repository at this point in the history
  • Loading branch information
arendjr authored Jun 11, 2024
1 parent d1551b3 commit c77e7e9
Show file tree
Hide file tree
Showing 16 changed files with 743 additions and 123 deletions.
20 changes: 20 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 @@ -24,6 +24,8 @@ biome_rowan = { workspace = true }
grit-pattern-matcher = { version = "0.3" }
grit-util = { version = "0.3" }
im = { version = "15.1.0" }
path-absolutize = { version = "3.1.1", optional = false, features = ["use_unix_paths_on_wasm"] }
regex = { workspace = true }
rustc-hash = { workspace = true }
serde = { workspace = true, features = ["derive"] }

Expand Down
7 changes: 7 additions & 0 deletions crates/biome_grit_patterns/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use biome_diagnostics::serde::Diagnostic as SerializableDiagnostic;
use biome_diagnostics::Diagnostic;
use biome_rowan::SyntaxError;
use grit_util::ByteRange;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Diagnostic, Serialize)]
Expand Down Expand Up @@ -37,6 +38,12 @@ pub enum CompileError {
/// If a function or bubble pattern has multiple parameters with the same name.
DuplicateParameters,

/// A metavariable was expected at the given range.
InvalidMetavariableRange(ByteRange),

/// Incorrect reference to a metavariable.
MetavariableNotFound(String),

/// Tried to declare or assign a Grit reserved metavariable.
ReservedMetavariable(String),

Expand Down
74 changes: 61 additions & 13 deletions crates/biome_grit_patterns/src/grit_binding.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
use crate::{
grit_context::GritQueryContext, grit_target_language::GritTargetLanguage,
grit_target_node::GritTargetNode,
grit_target_node::GritTargetNode, grit_tree::GritTree, source_location_ext::SourceFileExt,
};
use biome_diagnostics::{display::SourceFile, SourceCode};
use grit_pattern_matcher::{binding::Binding, constant::Constant};
use grit_util::{ByteRange, CodeRange, Range};
use grit_util::{Ast, AstNode, ByteRange, CodeRange, Range};
use std::path::Path;

#[derive(Clone, Debug, PartialEq)]
pub(crate) struct GritBinding;
pub(crate) enum GritBinding<'a> {
Tree(&'a GritTree),
Node(GritTargetNode),
Constant(&'a Constant),
}

impl<'a> Binding<'a, GritQueryContext> for GritBinding {
fn from_constant(_constant: &'a Constant) -> Self {
todo!()
impl<'a> GritBinding<'a> {
pub fn from_tree(tree: &'a GritTree) -> Self {
Self::Tree(tree)
}
}

impl<'a> Binding<'a, GritQueryContext> for GritBinding<'a> {
fn from_constant(constant: &'a Constant) -> Self {
Self::Constant(constant)
}

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

fn from_path(_path: &'a Path) -> Self {
Expand All @@ -26,24 +37,61 @@ impl<'a> Binding<'a, GritQueryContext> for GritBinding {
todo!()
}

/// Returns the only node bound by this binding.
///
/// This includes list bindings that only match a single child.
///
/// Returns `None` if the binding has no associated node, or if there is
/// more than one associated node.
fn singleton(&self) -> Option<GritTargetNode> {
todo!()
match self {
Self::Node(node) => Some(node.clone()),
Self::Tree(..) | Self::Constant(..) => None,
}
}

fn get_sexp(&self) -> Option<String> {
todo!()
None
}

fn position(&self, _language: &GritTargetLanguage) -> Option<Range> {
todo!()
match self {
GritBinding::Tree(tree) => {
let source = tree.source();
let source = SourceFile::new(SourceCode {
text: &source,
line_starts: None,
});
source.to_grit_range(tree.root_node().text_range())
}
GritBinding::Node(node) => {
// TODO: This is probably very inefficient.
let root = node.ancestors().last()?;
let source = root.text().to_string();
let source = SourceFile::new(SourceCode {
text: &source,
line_starts: None,
});
source.to_grit_range(root.text_trimmed_range())
}
GritBinding::Constant(_) => None,
}
}

fn range(&self, _language: &GritTargetLanguage) -> Option<ByteRange> {
todo!()
match self {
GritBinding::Tree(tree) => Some(tree.root_node().byte_range()),
GritBinding::Node(node) => Some(node.byte_range()),
GritBinding::Constant(_) => None,
}
}

fn code_range(&self, _language: &GritTargetLanguage) -> Option<CodeRange> {
todo!()
match self {
GritBinding::Tree(tree) => Some(tree.root_node().code_range()),
GritBinding::Node(node) => Some(node.code_range()),
GritBinding::Constant(_) => None,
}
}

fn is_equivalent_to(&self, _other: &Self, _language: &GritTargetLanguage) -> bool {
Expand Down
30 changes: 23 additions & 7 deletions crates/biome_grit_patterns/src/grit_code_snippet.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use crate::grit_context::{GritExecContext, GritQueryContext};
use crate::resolved_pattern::GritResolvedPattern;
use crate::grit_resolved_pattern::GritResolvedPattern;
use crate::grit_target_node::GritTargetSyntaxKind;
use anyhow::Result;
use grit_pattern_matcher::binding::Binding;
use grit_pattern_matcher::context::ExecContext;
use grit_pattern_matcher::pattern::{
CodeSnippet, DynamicPattern, Matcher, Pattern, PatternName, State,
CodeSnippet, DynamicPattern, Matcher, Pattern, PatternName, ResolvedPattern, State,
};
use grit_util::AnalysisLogs;

#[derive(Clone, Debug)]
pub(crate) struct GritCodeSnippet {
pub(crate) patterns: Vec<(GritTargetSyntaxKind, Pattern<GritQueryContext>)>,
pub(crate) source: String,
pub(crate) dynamic_snippet: Option<DynamicPattern<GritQueryContext>>,
}
Expand All @@ -25,12 +29,24 @@ impl CodeSnippet<GritQueryContext> for GritCodeSnippet {
impl Matcher<GritQueryContext> for GritCodeSnippet {
fn execute<'a>(
&'a self,
_binding: &GritResolvedPattern,
_state: &mut State<'a, GritQueryContext>,
_context: &'a GritExecContext,
_logs: &mut AnalysisLogs,
resolved: &GritResolvedPattern<'a>,
state: &mut State<'a, GritQueryContext>,
context: &'a GritExecContext,
logs: &mut AnalysisLogs,
) -> Result<bool> {
todo!()
let Some(binding) = resolved.get_last_binding() else {
return Ok(resolved.text(&state.files, context.language())?.trim() == self.source);
};

let Some(node) = binding.singleton() else {
return Ok(false);
};

if let Some((_, pattern)) = self.patterns.iter().find(|(kind, _)| *kind == node.kind()) {
pattern.execute(resolved, state, context, logs)
} else {
Ok(false)
}
}
}

Expand Down
24 changes: 16 additions & 8 deletions crates/biome_grit_patterns/src/grit_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use crate::grit_binding::GritBinding;
use crate::grit_code_snippet::GritCodeSnippet;
use crate::grit_file::GritFile;
use crate::grit_node_patterns::{GritLeafNodePattern, GritNodePattern};
use crate::grit_resolved_pattern::GritResolvedPattern;
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;
use grit_pattern_matcher::context::{ExecContext, QueryContext};
use grit_pattern_matcher::file_owners::FileOwners;
Expand All @@ -14,24 +14,32 @@ use grit_pattern_matcher::pattern::{
};
use grit_util::AnalysisLogs;

#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct GritQueryContext;

impl QueryContext for GritQueryContext {
type Node<'a> = GritTargetNode;
type NodePattern = GritNodePattern;
type LeafNodePattern = GritLeafNodePattern;
type ExecContext<'a> = GritExecContext;
type Binding<'a> = GritBinding;
type Binding<'a> = GritBinding<'a>;
type CodeSnippet = GritCodeSnippet;
type ResolvedPattern<'a> = GritResolvedPattern;
type ResolvedPattern<'a> = GritResolvedPattern<'a>;
type Language<'a> = GritTargetLanguage;
type File<'a> = GritFile;
type File<'a> = GritFile<'a>;
type Tree<'a> = GritTree;
}

#[derive(Debug)]
pub(crate) struct GritExecContext;
pub(crate) struct GritExecContext {
lang: GritTargetLanguage,
}

impl GritExecContext {
pub fn new(lang: GritTargetLanguage) -> Self {
Self { lang }
}
}

impl<'a> ExecContext<'a, GritQueryContext> for GritExecContext {
fn pattern_definitions(&self) -> &[PatternDefinition<GritQueryContext>] {
Expand All @@ -56,7 +64,7 @@ impl<'a> ExecContext<'a, GritQueryContext> for GritExecContext {
_context: &'a Self,
_state: &mut State<'a, GritQueryContext>,
_logs: &mut AnalysisLogs,
) -> Result<GritResolvedPattern> {
) -> Result<GritResolvedPattern<'a>> {
todo!()
}

Expand All @@ -65,7 +73,7 @@ impl<'a> ExecContext<'a, GritQueryContext> for GritExecContext {
}

fn language(&self) -> &GritTargetLanguage {
todo!()
&self.lang
}

fn exec_step(
Expand Down
65 changes: 51 additions & 14 deletions crates/biome_grit_patterns/src/grit_file.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,65 @@
use std::path::Path;

use crate::grit_context::GritQueryContext;
use crate::grit_resolved_pattern::GritResolvedPattern;
use crate::grit_target_language::GritTargetLanguage;
use crate::resolved_pattern::GritResolvedPattern;
use grit_pattern_matcher::pattern::{File, FileRegistry};
use grit_pattern_matcher::{
constant::Constant,
pattern::{File, FilePtr, FileRegistry, ResolvedFile, ResolvedPattern},
};
use grit_util::Ast;
use path_absolutize::Absolutize;

pub(crate) struct GritFile;
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum GritFile<'a> {
Resolved(Box<ResolvedFile<'a, GritQueryContext>>),
Ptr(FilePtr),
}

impl<'a> File<'a, GritQueryContext> for GritFile {
fn name(&self, _files: &FileRegistry<'a, GritQueryContext>) -> GritResolvedPattern {
todo!()
impl<'a> File<'a, GritQueryContext> for GritFile<'a> {
fn name(&self, files: &FileRegistry<'a, GritQueryContext>) -> GritResolvedPattern<'a> {
match self {
Self::Resolved(resolved) => resolved.name.clone(),
Self::Ptr(ptr) => GritResolvedPattern::from_path_binding(files.get_file_name(*ptr)),
}
}

fn absolute_path(
&self,
_files: &FileRegistry<'a, GritQueryContext>,
_language: &GritTargetLanguage,
) -> anyhow::Result<GritResolvedPattern> {
todo!()
files: &FileRegistry<'a, GritQueryContext>,
language: &GritTargetLanguage,
) -> anyhow::Result<GritResolvedPattern<'a>> {
match self {
Self::Resolved(resolved) => {
let name = resolved.name.text(files, language)?;
let absolute_path = Path::new(name.as_ref()).absolutize()?;
Ok(ResolvedPattern::from_constant(Constant::String(
absolute_path.to_string_lossy().to_string(),
)))
}
Self::Ptr(ptr) => Ok(ResolvedPattern::from_path_binding(
files.get_absolute_path(*ptr)?,
)),
}
}

fn body(&self, _files: &FileRegistry<'a, GritQueryContext>) -> GritResolvedPattern {
todo!()
fn body(&self, files: &FileRegistry<'a, GritQueryContext>) -> GritResolvedPattern<'a> {
match self {
Self::Resolved(resolved) => resolved.body.clone(),
Self::Ptr(ptr) => {
let file = &files.get_file_owner(*ptr);
GritResolvedPattern::from_tree(&file.tree)
}
}
}

fn binding(&self, _files: &FileRegistry<'a, GritQueryContext>) -> GritResolvedPattern {
todo!()
fn binding(&self, files: &FileRegistry<'a, GritQueryContext>) -> GritResolvedPattern<'a> {
match self {
Self::Resolved(resolved) => resolved.body.clone(),
Self::Ptr(ptr) => {
let file = &files.get_file_owner(*ptr);
ResolvedPattern::from_node_binding(file.tree.root_node())
}
}
}
}
Loading

0 comments on commit c77e7e9

Please sign in to comment.