Skip to content

Commit

Permalink
Make Edge::label required instead of optional.
Browse files Browse the repository at this point in the history
This involves a couple of changes outside of just changing the field type:
1. `CursorIterator` must be able to produce a label for every node now. This wasn't possible before because the root nodes of a cursor had no parent, and so could not derive a label. I've changed `Cursor` in the following ways to fix this:
    * `Cursor::parent` is a new type, `Parent<T>`, instead of `Option<Rc<PathAncestor<T>>>`. `Parent<T>` is an enum with three variants - `None`, `Open(Rc<PathAncestor<T>>)`, and `Closed(Rc<PathAncestor<T>>)`. A `Closed` parent is used when a cursor is spawned from an arbitrary non-root node, and means that the parent can be read but not traversed to. `None` is only used for the true root node.
2. `KindTypes::EdgeLabel` is given a `Default` implementation, which for now just returns the first `PredefinedLabel` variant. This is needed in order to be able to set a "concrete" `EdgeLabel` value from the `metaslang_cst` crate.
3. Added a `Root` variant to `PredefinedLabel`. This will be used when a node is the true root node. For now this will be the default value for `KindTypes::EdgeLabel`.
  • Loading branch information
mjoerussell committed Dec 30, 2024
1 parent ba075d1 commit b018180
Show file tree
Hide file tree
Showing 15 changed files with 85 additions and 51 deletions.
1 change: 1 addition & 0 deletions crates/codegen/language/definition/src/model/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ pub struct Topic {
)]
#[strum(serialize_all = "snake_case")]
pub enum PredefinedLabel {
Root,
Item,
Variant,
Separator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,9 @@ pub enum EdgeLabel {
}

impl crate::cst::EdgeLabelExtensions for EdgeLabel {}

impl Default for EdgeLabel {
fn default() -> Self {
Self::{{ model.kinds.predefined_labels[0] | pascal_case }}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ impl ParserResult {
label: prev_label, ..
}) = self.significant_node_mut()
{
*prev_label = Some(label);
*prev_label = label;
}
// Also allow to name a single trivia terminal node
else if let ParserResult::Match(Match { nodes, .. }) = &mut self {
if let [node] = nodes.as_mut_slice() {
if node.as_terminal().is_some_and(|tok| tok.kind.is_trivia()) {
node.label = Some(label);
node.label = label;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ impl PrecedenceHelper {
let left_nodes = match left {
Some(Expression { nodes }) => {
vec![Edge {
label: Some(left_label),
label: left_label,
node: Node::nonterminal(child_kind, nodes),
}]
}
Expand All @@ -185,7 +185,7 @@ impl PrecedenceHelper {
let right_nodes = match right {
Some(Expression { nodes }) => {
vec![Edge {
label: Some(right_label),
label: right_label,
node: Node::nonterminal(child_kind, nodes),
}]
}
Expand All @@ -197,7 +197,7 @@ impl PrecedenceHelper {

Expression {
nodes: vec![Edge {
label: Some(EdgeLabel::Variant),
label: EdgeLabel::Variant,
node: Node::nonterminal(kind, children),
}],
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ interface cst {
/// Represents a connection between nodes in the syntax tree.
record edge {
/// Optional label describing the relationship between nodes.
label: option<edge-label>,
label: edge-label,
/// The target node of this edge.
node: node,
}
Expand All @@ -167,7 +167,7 @@ interface cst {
/// Returns the current node under the cursor.
node: func() -> node;
/// Returns the label of the edge from the parent to the current node, if any.
label: func() -> option<edge-label>;
label: func() -> edge-label;

/// Returns the current text offset of the cursor.
text-offset: func() -> text-index;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,15 +162,15 @@ impl Helper {
let Edge { label, node } = self.node.children.get(self.index)?;

match label {
// Skip unlabeled nodes:
| None
// Skip root nodes:
| EdgeLabel::Root
// Skip trivia:
| Some(EdgeLabel::LeadingTrivia | EdgeLabel::TrailingTrivia) => {
| EdgeLabel::LeadingTrivia | EdgeLabel::TrailingTrivia => {
self.index += 1;
continue;
}
// Otherwise, return the edge:
Some(other_label) => {
other_label => {
return Some((*other_label, node.clone()));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl IntoFFI<ffi::Edge> for rust::Edge {
#[inline]
fn _into_ffi(self) -> ffi::Edge {
ffi::Edge {
label: self.label.map(IntoFFI::_into_ffi),
label: IntoFFI::_into_ffi(self.label),
node: self.node._into_ffi(),
}
}
Expand Down Expand Up @@ -219,8 +219,8 @@ define_refcell_wrapper! { Cursor {
self._borrow_ffi().node()._into_ffi()
}

fn label(&self) -> Option<ffi::EdgeLabel> {
self._borrow_ffi().label().map(IntoFFI::_into_ffi)
fn label(&self) -> ffi::EdgeLabel {
IntoFFI::_into_ffi(self._borrow_ffi().label())
}

fn text_offset(&self) -> ffi::TextIndex {
Expand Down
66 changes: 46 additions & 20 deletions crates/metaslang/cst/src/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,34 @@ use crate::kinds::KindTypes;
use crate::nodes::{Edge, Node, NonterminalNode};
use crate::text_index::{TextIndex, TextRange};

#[derive(Clone, Debug, PartialEq, Eq)]
enum Parent<T: KindTypes> {
None,
Open(Rc<PathAncestor<T>>),
Closed(Rc<PathAncestor<T>>),
}

impl<T: KindTypes> Parent<T> {
fn as_closed(&self) -> Parent<T> {
match self {
Parent::Open(ancestor) => Parent::Closed(ancestor.to_owned()),
_ => self.clone(),
}
}

fn get_readable(&self) -> Option<&Rc<PathAncestor<T>>> {
match self {
Parent::Open(ancestor) => Some(ancestor),
Parent::Closed(ancestor) => Some(ancestor),
Parent::None => None,
}
}
}

/// A node in the ancestor path of a [`Cursor`].
#[derive(Clone, Debug, PartialEq, Eq)]
struct PathAncestor<T: KindTypes> {
parent: Option<Rc<PathAncestor<T>>>,
parent: Parent<T>,
nonterminal_node: Rc<NonterminalNode<T>>,
child_number: usize,
text_offset: TextIndex,
Expand All @@ -21,7 +45,7 @@ struct PathAncestor<T: KindTypes> {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Cursor<T: KindTypes> {
/// The parent path of this cursor
parent: Option<Rc<PathAncestor<T>>>,
parent: Parent<T>,
/// The node the cursor is currently pointing to.
node: Node<T>,
/// The index of the current child node in the parent's children.
Expand Down Expand Up @@ -59,7 +83,7 @@ impl<T: KindTypes> Cursor<T> {
impl<T: KindTypes> Cursor<T> {
pub(crate) fn new(node: Node<T>, text_offset: TextIndex) -> Self {
Self {
parent: None,
parent: Parent::None,
node,
child_number: 0,
text_offset,
Expand All @@ -75,9 +99,9 @@ impl<T: KindTypes> Cursor<T> {

/// Completes the cursor, setting it to the root node.
pub fn complete(&mut self) {
if let Some(parent) = &self.parent.clone() {
if let Parent::Open(parent) = &self.parent.clone() {
let mut parent = parent;
while let Some(grandparent) = &parent.parent {
while let Parent::Open(grandparent) = &parent.parent {
parent = grandparent;
}
self.set_from_ancestor_node(parent);
Expand All @@ -92,7 +116,7 @@ impl<T: KindTypes> Cursor<T> {
pub fn spawn(&self) -> Self {
Self {
is_completed: false,
parent: None,
parent: self.parent.as_closed(),
node: self.node.clone(),
child_number: 0,
text_offset: self.text_offset,
Expand All @@ -109,12 +133,13 @@ impl<T: KindTypes> Cursor<T> {
self.node.clone()
}

pub fn label(&self) -> Option<T::EdgeLabel> {
self.parent.as_ref().and_then(|parent| {
pub fn label(&self) -> T::EdgeLabel {
if let Some(parent) = self.parent.get_readable() {
let this = &parent.nonterminal_node.children[self.child_number];

this.label
})
} else {
T::EdgeLabel::default()
}
}

/// Returns the text offset that corresponds to the beginning of the currently pointed to node.
Expand All @@ -132,10 +157,10 @@ impl<T: KindTypes> Cursor<T> {
/// 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 {
if let Parent::Open(parent) = &self.parent {
let mut parent = parent;
depth += 1;
while let Some(grandparent) = &parent.parent {
while let Parent::Open(grandparent) = &parent.parent {
depth += 1;
parent = grandparent;
}
Expand Down Expand Up @@ -224,7 +249,7 @@ impl<T: KindTypes> Cursor<T> {
///
/// 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() {
if let Parent::Open(parent) = &self.parent.clone() {
self.set_from_ancestor_node(parent);

true
Expand All @@ -246,7 +271,7 @@ impl<T: KindTypes> Cursor<T> {
// 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.nonterminal_node.children.first().cloned() {
self.parent = Some(new_parent);
self.parent = Parent::Open(new_parent);
self.node = new_child.node;
self.child_number = 0;

Expand All @@ -273,7 +298,7 @@ impl<T: KindTypes> Cursor<T> {
for sibling in &new_parent.nonterminal_node.children[..self.child_number] {
self.text_offset += sibling.text_len();
}
self.parent = Some(new_parent);
self.parent = Parent::Open(new_parent);

return true;
}
Expand Down Expand Up @@ -303,7 +328,7 @@ impl<T: KindTypes> Cursor<T> {
for sibling in &new_parent.nonterminal_node.children[..self.child_number] {
self.text_offset += sibling.text_len();
}
self.parent = Some(new_parent);
self.parent = Parent::Open(new_parent);

return true;
}
Expand All @@ -320,7 +345,7 @@ impl<T: KindTypes> Cursor<T> {
return false;
}

if let Some(parent) = &self.parent {
if let Parent::Open(parent) = &self.parent {
let new_child_number = self.child_number + 1;
if let Some(new_child) = parent.nonterminal_node.children.get(new_child_number) {
self.text_offset += self.node.text_len();
Expand All @@ -342,7 +367,7 @@ impl<T: KindTypes> Cursor<T> {
return false;
}

if let Some(parent) = &self.parent {
if let Parent::Open(parent) = &self.parent {
if self.child_number > 0 {
let new_child_number = self.child_number - 1;
let new_child = &parent.nonterminal_node.children[new_child_number];
Expand Down Expand Up @@ -438,14 +463,15 @@ impl<T: KindTypes> Iterator for CursorIterator<T> {
}

pub struct AncestorsIterator<T: KindTypes> {
current: Option<Rc<PathAncestor<T>>>,
current: Parent<T>,
}

impl<T: KindTypes> Iterator for AncestorsIterator<T> {
type Item = Rc<NonterminalNode<T>>;

fn next(&mut self) -> Option<Self::Item> {
if let Some(current) = self.current.take() {
// if let Some(current) = self.current.take() {
if let Parent::Open(current) = self.current.clone() {
self.current = current.parent.clone();
Some(Rc::clone(&current.nonterminal_node))
} else {
Expand Down
2 changes: 1 addition & 1 deletion crates/metaslang/cst/src/kinds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub trait TerminalKindExtensions: BaseKind {

pub trait NonterminalKindExtensions: BaseKind {}

pub trait EdgeLabelExtensions: BaseKind {}
pub trait EdgeLabelExtensions: BaseKind + Default {}

pub trait KindTypes: std::fmt::Debug + Clone + PartialEq {
type NonterminalKind: NonterminalKindExtensions;
Expand Down
7 changes: 5 additions & 2 deletions crates/metaslang/cst/src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,17 @@ pub enum Node<T: KindTypes> {

#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct Edge<T: KindTypes> {
pub label: Option<T::EdgeLabel>,
pub label: T::EdgeLabel,
pub node: Node<T>,
}

impl<T: KindTypes> Edge<T> {
/// Creates an anonymous node (without a label).
pub fn anonymous(node: Node<T>) -> Self {
Self { label: None, node }
Self {
label: T::EdgeLabel::default(),
node,
}
}
}

Expand Down
13 changes: 5 additions & 8 deletions crates/metaslang/cst/src/query/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ impl<T: KindTypes + 'static> Cursor<T> {
NodeKind::Nonterminal(nonterminal.kind) == *node_kind
}
NodeSelector::NodeText { .. } => false,
NodeSelector::EdgeLabel { edge_label } => Some(*edge_label) == self.label(),
NodeSelector::EdgeLabel { edge_label } => *edge_label == self.label(),
NodeSelector::EdgeLabelAndNodeKind {
edge_label,
node_kind,
} => {
Some(*edge_label) == self.label()
*edge_label == self.label()
&& NodeKind::Nonterminal(nonterminal.kind) == *node_kind
}
NodeSelector::EdgeLabelAndNodeText { .. } => false,
Expand All @@ -54,18 +54,15 @@ impl<T: KindTypes + 'static> Cursor<T> {
NodeKind::Terminal(terminal.kind) == *node_kind
}
NodeSelector::NodeText { node_text } => terminal.text == *node_text,
NodeSelector::EdgeLabel { edge_label } => Some(*edge_label) == self.label(),
NodeSelector::EdgeLabel { edge_label } => *edge_label == self.label(),
NodeSelector::EdgeLabelAndNodeKind {
edge_label,
node_kind,
} => {
Some(*edge_label) == self.label()
&& NodeKind::Terminal(terminal.kind) == *node_kind
}
} => *edge_label == self.label() && NodeKind::Terminal(terminal.kind) == *node_kind,
NodeSelector::EdgeLabelAndNodeText {
edge_label,
node_text,
} => Some(*edge_label) == self.label() && terminal.text == *node_text,
} => *edge_label == self.label() && terminal.text == *node_text,
},
}
}
Expand Down
7 changes: 1 addition & 6 deletions crates/metaslang/graph_builder/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,6 @@ pub mod stdlib {
let index = parent
.children()
.iter()
.filter(|edge| edge.label.is_some())
.position(|edge| edge.node == node)
.ok_or(ExecutionError::FunctionFailed(
"named-child-index".into(),
Expand Down Expand Up @@ -403,11 +402,7 @@ pub mod stdlib {
) -> Result<Value, ExecutionError> {
let cursor = &graph[parameters.param()?.into_syntax_node_ref()?];
parameters.finish()?;
let named_child_count = cursor
.children()
.iter()
.filter(|edge| edge.label.is_some())
.count();
let named_child_count = cursor.children().len();
Ok(Value::Integer(named_child_count as u32))
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/metaslang/graph_builder/tests/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ use {log as _, regex as _, serde_json as _, smallvec as _, string_interner as _,
Clone,
Copy,
Debug,
Default,
Eq,
PartialEq,
serde::Serialize,
strum_macros::IntoStaticStr,
strum_macros::EnumString,
)]
pub enum DummyKind {
#[default]
Module,
}
impl metaslang_cst::kinds::TerminalKindExtensions for DummyKind {}
Expand Down
Loading

0 comments on commit b018180

Please sign in to comment.