Skip to content

Commit

Permalink
Refactor: move bindings builder into its own module along with resolv…
Browse files Browse the repository at this point in the history
…er and loader
  • Loading branch information
ggiraldez committed Dec 23, 2024
1 parent 70e9607 commit edf12f2
Show file tree
Hide file tree
Showing 10 changed files with 433 additions and 405 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ mod resolver {
use metaslang_graph_builder::graph::{Graph, Value};
use metaslang_graph_builder::ExecutionError;

use crate::{FileDescriptor, PathResolver};
use crate::builder::FileDescriptor;
use crate::PathResolver;

pub fn add_functions<KT: KindTypes + 'static>(
functions: &mut Functions<KT>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ use stack_graphs::arena::Handle;
use stack_graphs::graph::{File, Node, NodeID, StackGraph};
use thiserror::Error;

use crate::{DefinitionBindingInfo, ReferenceBindingInfo};
use crate::builder::{DefinitionBindingInfo, ReferenceBindingInfo};

// Node type values
static DROP_SCOPES_TYPE: &str = "drop_scopes";
Expand Down Expand Up @@ -392,7 +392,7 @@ pub(crate) struct Loader<'a, KT: KindTypes + 'static> {
extension_hooks: HashSet<Handle<Node>>,
}

pub(crate) struct BuildResult<KT: KindTypes + 'static> {
pub(crate) struct LoadResult<KT: KindTypes + 'static> {
#[cfg(feature = "__private_testing_utils")]
pub graph: Graph<KT>,
pub cursors: HashMap<Handle<Node>, Cursor<KT>>,
Expand Down Expand Up @@ -476,7 +476,7 @@ impl<'a, KT: KindTypes + 'static> Loader<'a, KT> {
pub fn execute(
mut self,
cancellation_flag: &dyn CancellationFlag,
) -> Result<BuildResult<KT>, BuildError> {
) -> Result<LoadResult<KT>, BuildError> {
let variables = self.build_global_variables();

let config = ExecutionConfig::new(self.functions, &variables)
Expand Down Expand Up @@ -506,7 +506,7 @@ impl<'a, KT: KindTypes + 'static> Loader<'a, KT> {

self.load(cancellation_flag)?;

Ok(BuildResult {
Ok(LoadResult {
#[cfg(feature = "__private_testing_utils")]
graph: self.graph,
cursors: self.cursors,
Expand Down
233 changes: 233 additions & 0 deletions crates/metaslang/bindings/src/builder/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
mod loader;
mod resolver;

use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::rc::Rc;

use loader::{LoadResult, Loader};
use metaslang_cst::cursor::Cursor;
use metaslang_cst::kinds::KindTypes;
use metaslang_graph_builder::ast::File;
use metaslang_graph_builder::functions::Functions;
use resolver::Resolver;
use semver::Version;
use stack_graphs::graph::StackGraph;

pub(crate) type GraphHandle = stack_graphs::arena::Handle<stack_graphs::graph::Node>;
pub(crate) type FileHandle = stack_graphs::arena::Handle<stack_graphs::graph::File>;
pub(crate) type CursorID = usize;

pub use crate::graph::BindingGraph;

pub(crate) struct DefinitionBindingInfo<KT: KindTypes + 'static> {
pub(crate) definiens: Cursor<KT>,
parents: Vec<GraphHandle>,
extension_scope: Option<GraphHandle>,
inherit_extensions: bool,
}

pub(crate) struct ReferenceBindingInfo {
parents: Vec<GraphHandle>,
}

pub struct BindingGraphBuilder<KT: KindTypes + 'static> {
graph_builder_file: File<KT>,
functions: Functions<KT>,
pub(crate) stack_graph: StackGraph,
pub(crate) cursors: HashMap<GraphHandle, Cursor<KT>>,
pub(crate) definitions_info: HashMap<GraphHandle, DefinitionBindingInfo<KT>>,
pub(crate) references_info: HashMap<GraphHandle, ReferenceBindingInfo>,
pub(crate) cursor_to_definitions: HashMap<CursorID, GraphHandle>,
pub(crate) cursor_to_references: HashMap<CursorID, GraphHandle>,
extension_hooks: HashSet<GraphHandle>,
}

#[derive(Clone)]
pub enum FileDescriptor {
User(String),
System(String),
}

#[derive(Debug)]
pub(crate) struct FileDescriptorError;

impl FileDescriptor {
// Internal functions to convert a FileDescriptor to and from a string for
// representation inside the stack graph

pub(crate) fn as_string(&self) -> String {
match self {
Self::User(path) => format!("user:{path}"),
Self::System(path) => format!("system:{path}"),
}
}

pub(crate) fn try_from(value: &str) -> Result<Self, FileDescriptorError> {
value
.strip_prefix("user:")
.map(|path| FileDescriptor::User(path.into()))
.or_else(|| {
value
.strip_prefix("system:")
.map(|path| FileDescriptor::System(path.into()))
})
.ok_or(FileDescriptorError)
}

pub(crate) fn from(value: &str) -> Self {
Self::try_from(value)
.unwrap_or_else(|_| panic!("{value} should be a valid file descriptor"))
}

pub fn get_path(&self) -> &str {
match self {
Self::User(path) => path,
Self::System(path) => path,
}
}

pub fn is_system(&self) -> bool {
matches!(self, Self::System(_))
}

pub fn is_user(&self) -> bool {
matches!(self, Self::User(_))
}

pub fn is_user_path(&self, path: &str) -> bool {
matches!(self, Self::User(user_path) if user_path == path)
}
}

pub trait PathResolver<KT: KindTypes + 'static> {
fn resolve_path(&self, context_path: &str, path_to_resolve: &Cursor<KT>) -> Option<String>;
}

impl<KT: KindTypes + 'static> BindingGraphBuilder<KT> {
pub fn create(
version: Version,
binding_rules: &str,
path_resolver: Rc<dyn PathResolver<KT>>,
) -> Self {
let graph_builder_file =
File::from_str(binding_rules).expect("Bindings stack graph builder parse error");
let stack_graph = StackGraph::new();
let functions = loader::default_functions(version, path_resolver);

Self {
graph_builder_file,
functions,
stack_graph,
cursors: HashMap::new(),
definitions_info: HashMap::new(),
references_info: HashMap::new(),
cursor_to_definitions: HashMap::new(),
cursor_to_references: HashMap::new(),
extension_hooks: HashSet::new(),
}
}

pub fn add_system_file(&mut self, file_path: &str, tree_cursor: Cursor<KT>) {
let file_kind = FileDescriptor::System(file_path.into());
let file = self.stack_graph.get_or_create_file(&file_kind.as_string());
_ = self.add_file_internal(file, tree_cursor);
}

pub fn add_user_file(&mut self, file_path: &str, tree_cursor: Cursor<KT>) {
let file_kind = FileDescriptor::User(file_path.into());
let file = self.stack_graph.get_or_create_file(&file_kind.as_string());
_ = self.add_file_internal(file, tree_cursor);
}

#[cfg(feature = "__private_testing_utils")]
pub fn add_user_file_returning_graph(
&mut self,
file_path: &str,
tree_cursor: Cursor<KT>,
) -> metaslang_graph_builder::graph::Graph<KT> {
let file_kind = FileDescriptor::User(file_path.into());
let file = self.stack_graph.get_or_create_file(&file_kind.as_string());
let result = self.add_file_internal(file, tree_cursor);
result.graph
}

fn add_file_internal(&mut self, file: FileHandle, tree_cursor: Cursor<KT>) -> LoadResult<KT> {
let loader = Loader::new(
&self.graph_builder_file,
&self.functions,
&mut self.stack_graph,
file,
tree_cursor,
);
let mut result = loader
.execute(&loader::NoCancellation)
.expect("Internal error while building bindings");

for (handle, cursor) in result.cursors.drain() {
let cursor_id = cursor.node().id();
if self.stack_graph[handle].is_definition() {
self.cursor_to_definitions.insert(cursor_id, handle);
} else {
self.cursor_to_references.insert(cursor_id, handle);
}
self.cursors.insert(handle, cursor);
}
self.definitions_info
.extend(result.definitions_info.drain());
self.references_info.extend(result.references_info.drain());
self.extension_hooks.extend(result.extension_hooks.drain());

result
}

pub fn resolve(self) -> Rc<BindingGraph<KT>> {
let resolver = Resolver::new(&self);
let resolved_references = resolver.resolve();
BindingGraph::build(self, resolved_references)
}

pub(crate) fn get_parents(&self, handle: GraphHandle) -> Vec<GraphHandle> {
if self.is_definition(handle) {
self.definitions_info
.get(&handle)
.map(|info| info.parents.clone())
.unwrap_or_default()
} else {
self.references_info
.get(&handle)
.map(|info| info.parents.clone())
.unwrap_or_default()
}
}

fn is_definition(&self, handle: GraphHandle) -> bool {
self.stack_graph[handle].is_definition()
}

fn is_reference(&self, handle: GraphHandle) -> bool {
self.stack_graph[handle].is_reference()
}

fn get_extension_scope(&self, handle: GraphHandle) -> Option<GraphHandle> {
self.definitions_info
.get(&handle)
.and_then(|info| info.extension_scope)
}

fn inherits_extensions(&self, handle: GraphHandle) -> bool {
self.definitions_info
.get(&handle)
.is_some_and(|info| info.inherit_extensions)
}

fn get_file(&self, handle: GraphHandle) -> Option<FileDescriptor> {
self.stack_graph[handle]
.file()
.map(|file| FileDescriptor::from(self.stack_graph[file].name()))
}

pub(crate) fn is_extension_hook(&self, node_handle: GraphHandle) -> bool {
self.extension_hooks.contains(&node_handle)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use stack_graphs::stitching::{
};
use stack_graphs::{CancellationError, NoCancellation};

use crate::{BindingGraphBuilder, GraphHandle};
use super::{BindingGraphBuilder, GraphHandle};

pub(crate) struct Resolver<'a, KT: KindTypes + 'static> {
owner: &'a BindingGraphBuilder<KT>,
Expand Down
96 changes: 96 additions & 0 deletions crates/metaslang/bindings/src/graph/definition.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use std::fmt::{self, Debug, Display};
use std::rc::Rc;

use metaslang_cst::cursor::Cursor;
use metaslang_cst::kinds::KindTypes;

use super::{BindingGraph, BindingLocation};
use crate::builder::{FileDescriptor, GraphHandle};
use crate::graph::DisplayCursor;

#[derive(Clone)]
pub struct Definition<KT: KindTypes + 'static> {
pub(crate) owner: Rc<BindingGraph<KT>>,
pub(crate) handle: GraphHandle,
}

impl<KT: KindTypes + 'static> Definition<KT> {
pub fn id(&self) -> usize {
self.get_cursor().node().id()
}

pub fn name_location(&self) -> BindingLocation<KT> {
match self.get_file() {
FileDescriptor::System(_) => BindingLocation::built_in(),
FileDescriptor::User(file_id) => {
BindingLocation::user_file(file_id, self.get_cursor().to_owned())
}
}
}

pub fn definiens_location(&self) -> BindingLocation<KT> {
match self.get_file() {
FileDescriptor::System(_) => BindingLocation::built_in(),
FileDescriptor::User(file_id) => {
BindingLocation::user_file(file_id, self.get_definiens_cursor().to_owned())
}
}
}

pub fn get_cursor(&self) -> &Cursor<KT> {
&self
.owner
.definitions
.get(&self.handle)
.expect("Definition handle is valid")
.cursor
}

pub fn get_definiens_cursor(&self) -> &Cursor<KT> {
&self
.owner
.definitions
.get(&self.handle)
.expect("Definition handle is valid")
.definiens
}

pub fn get_file(&self) -> FileDescriptor {
self.owner
.get_file(
self.owner
.definitions
.get(&self.handle)
.expect("Definition handle is valid")
.file,
)
.expect("Definition does not have a valid file descriptor")
}
}

impl<KT: KindTypes + 'static> Display for Definition<KT> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"definition {}",
DisplayCursor {
cursor: self.get_cursor(),
file: self.get_file()
}
)
}
}

impl<KT: KindTypes + 'static> Debug for Definition<KT> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}

impl<KT: KindTypes + 'static> PartialEq for Definition<KT> {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.owner, &other.owner) && self.handle == other.handle
}
}

impl<KT: KindTypes + 'static> Eq for Definition<KT> {}
Loading

0 comments on commit edf12f2

Please sign in to comment.