Skip to content

Commit

Permalink
Add Ast
Browse files Browse the repository at this point in the history
  • Loading branch information
justinpombrio committed Mar 12, 2024
1 parent d5a4cf1 commit 28d5845
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 91 deletions.
276 changes: 209 additions & 67 deletions src/language/ast.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use super::forest::{Forest, NodeIndex};
use super::language_set::{Construct, DocCondition, LanguageSet, StyleLabel, ValidNotation};
use super::language_set::{
Arity, Construct, DocCondition, Language, LanguageSet, StyleLabel, ValidNotation,
};
use super::text::Text;
use crate::infra::SynlessBug;
use crate::infra::{bug, SynlessBug};
use partial_pretty_printer as ppp;
use std::fmt;

Expand All @@ -14,108 +16,248 @@ struct AstNode {
text: Option<Text>,
}

struct DocStorage {
pub struct DocStorage {
language_set: LanguageSet,
forest: Forest<AstNode>,
next_id: AstId,
}

/********************************************
* AstRef: implements PrettyDoc *
********************************************/
#[derive(Debug, Clone, Copy)]
pub struct Ast(NodeIndex);

// TODO: These will need more data, and should be moved somewhere.
type Style = ();
// TODO: doc
pub struct Bookmark(NodeIndex);

#[derive(Clone, Copy)]
struct AstRef<'d> {
storage: &'d DocStorage,
node_index: NodeIndex,
impl DocStorage {
fn next_id(&mut self) -> AstId {
let id = self.next_id.0;
self.next_id.0 += 1;
AstId(id)
}
}

impl<'d> AstRef<'d> {
fn node(self) -> &'d AstNode {
self.storage.forest.data(self.node_index)
impl Ast {
pub fn new_hole(s: &mut DocStorage, lang: Language) -> Ast {
Ast::new(s, lang.hole_construct(&s.language_set))
}
}

impl<'d> ppp::PrettyDoc<'d> for AstRef<'d> {
type Id = AstId;
type Style = Style;
type StyleLabel = StyleLabel;
type Condition = DocCondition;
pub fn new(s: &mut DocStorage, construct: Construct) -> Ast {
let id = s.next_id();
match construct.arity(&s.language_set) {
Arity::Texty => Ast(s.forest.new_node(AstNode {
id,
construct,
text: Some(Text::new()),
})),
Arity::Listy(_) => Ast(s.forest.new_node(AstNode {
id,
construct,
text: None,
})),
Arity::Fixed(sorts) => {
let parent = s.forest.new_node(AstNode {
id,
construct,
text: None,
});
let num_children = sorts.len(&s.language_set);
let hole_construct = construct.language().hole_construct(&s.language_set);
for _ in 0..num_children {
let child_id = s.next_id();
let child = s.forest.new_node(AstNode {
id: child_id,
construct: hole_construct,
text: None,
});
s.forest.insert_last_child(parent, child);
}
Ast(parent)
}
}
}

fn id(self) -> AstId {
self.node().id
/// Determine the number of siblings that this node has, including itself.
pub fn num_siblings(&self, s: &DocStorage) -> usize {
if let Some(parent) = s.forest.parent(self.0) {
s.forest.num_children(parent)
} else {
1
}
}

fn notation(self) -> &'d ValidNotation {
self.node().construct.notation(&self.storage.language_set)
/// Determine this node's index among its siblings. Returns `0` when at the root.
pub fn sibling_index(self, s: &DocStorage) -> usize {
s.forest.sibling_index(self.0)
}

fn condition(self, condition: &DocCondition) -> bool {
// TODO
false
/// Returns `true` if this is the root of the tree, and `false` if
/// it isn't (and thus this node has a parent).
pub fn is_at_root(self, s: &DocStorage) -> bool {
s.forest.parent(self.0).is_none()
}

fn lookup_style(self, style_label: StyleLabel) -> Style {
// TODO
()
/// Return the number of children this node has. For a Fixed node, this is
/// its arity. For a Listy node, this is its current number of children.
/// For text, this is considered 0.
fn num_children(self, s: &DocStorage) -> usize {
s.forest.num_children(self.0)
}

fn node_style(self) -> Style {
// TODO
()
pub fn parent(self, s: &DocStorage) -> Option<Ast> {
s.forest.parent(self.0).map(Ast)
}

fn num_children(self) -> Option<usize> {
if self.node().text.is_some() {
pub fn next_sibling(self, s: &DocStorage) -> Option<Ast> {
s.forest.next(self.0).map(Ast)
}

pub fn prev_sibling(self, s: &DocStorage) -> Option<Ast> {
s.forest.prev(self.0).map(Ast)
}

pub fn first_sibling(self, s: &DocStorage) -> Ast {
Ast(s.forest.first_sibling(self.0))
}

pub fn last_sibling(self, s: &DocStorage) -> Ast {
Ast(s.forest.last_sibling(self.0))
}

pub fn root(self, s: &DocStorage) -> Ast {
Ast(s.forest.root(self.0))
}

/// Save a bookmark to return to later.
pub fn bookmark(self) -> Bookmark {
Bookmark(self.0)
}

/// Jump to a previously saved bookmark, as long as that
/// bookmark's node is present somewhere in this tree. This will
/// work even if the Tree has been modified since the bookmark was
/// created. However, it will return `None` if the bookmark's node
/// has since been deleted, or if it is currently located in a
/// different tree.
pub fn goto_bookmark(self, mark: Bookmark, s: &DocStorage) -> Option<Ast> {
if s.forest.is_valid(mark.0) && s.forest.root(self.0) == s.forest.root(mark.0) {
Some(Ast(mark.0))
} else {
None
}
}

/// Check if `other` is allowed where `self` currently is, according to its parent's arity.
fn accepts_replacement(self, s: &DocStorage, other: Ast) -> bool {
if let Some(parent) = s.forest.parent(self.0) {
let sort = match Ast(parent).arity(s) {
Arity::Fixed(sorts) => sorts.get(&s.language_set, self.sibling_index(s)).bug(),
Arity::Listy(sort) => sort,
Arity::Texty => bug!("Texty parent!"),
};
let other_construct = s.forest.data(other.0).construct;
sort.accepts(&s.language_set, other_construct)
} else {
Some(self.storage.forest.num_children(self.node_index))
true
}
}

fn unwrap_text(self) -> &'d str {
self.node().text.as_ref().bug().as_str()
fn is_listy_and_accepts_child(self, s: &DocStorage, other: Ast) -> bool {
let other_construct = s.forest.data(other.0).construct;
match self.arity(s) {
Arity::Fixed(_) => false,
Arity::Listy(sort) => sort.accepts(&s.language_set, other_construct),
Arity::Texty => false,
}
}

fn unwrap_child(self, i: usize) -> AstRef<'d> {
let mut child = self.storage.forest.first_child(self.node_index).bug();
for _ in 0..i {
child = self.storage.forest.next(child).bug();
// TODO: doc
pub fn swap(self, s: &mut DocStorage, other: Ast) -> bool {
if self.accepts_replacement(s, other) && other.accepts_replacement(s, self) {
s.forest.swap(self.0, other.0)
} else {
false
}
AstRef {
storage: self.storage,
node_index: child,
}

pub fn arity(self, s: &DocStorage) -> Arity {
s.forest.data(self.0).construct.arity(&s.language_set)
}

/// Borrow the text of a texty node. Panics if not texty.
pub fn text(self, s: &DocStorage) -> &Text {
s.forest
.data(self.0)
.text
.as_ref()
.bug_msg("Ast::text() - not texty")
}

/// Mutably borrow the text of a texty node. Panics if not texty.
pub fn text_mut(self, s: &mut DocStorage) -> &mut Text {
s.forest
.data_mut(self.0)
.text
.as_mut()
.bug_msg("Ast::text_mut() - not texty")
}

/// Go to this node's `n`'th child.
/// Panics if `n` is out of bounds, or if this node is texty.
pub fn nth_child(self, s: &DocStorage, n: usize) -> Ast {
Ast(s.forest.nth_child(self.0, n).bug_msg("Ast::nth_child"))
}

// TODO: doc (new_sibling must be root)
pub fn insert_before(self, s: &mut DocStorage, new_sibling: Ast) -> bool {
if let Some(parent) = self.parent(s) {
if parent.is_listy_and_accepts_child(s, new_sibling) {
s.forest.insert_before(self.0, new_sibling.0);
true
} else {
false
}
} else {
false
}
}

fn unwrap_last_child(self) -> AstRef<'d> {
let first_child = self.storage.forest.first_child(self.node_index).bug();
let last_child = self.storage.forest.last_sibling(first_child);
AstRef {
storage: self.storage,
node_index: last_child,
// TODO: doc (new_sibling must be root)
pub fn insert_after(self, s: &mut DocStorage, new_sibling: Ast) -> bool {
if let Some(parent) = self.parent(s) {
if parent.is_listy_and_accepts_child(s, new_sibling) {
s.forest.insert_after(self.0, new_sibling.0);
true
} else {
false
}
} else {
false
}
}

fn unwrap_prev_sibling(self, _: Self, _: usize) -> AstRef<'d> {
AstRef {
storage: self.storage,
node_index: self.storage.forest.prev(self.node_index).bug(),
// TODO: doc (new_child must be root)
pub fn insert_last_child(self, s: &mut DocStorage, new_child: Ast) -> bool {
if self.is_listy_and_accepts_child(s, new_child) {
s.forest.insert_last_child(self.0, new_child.0);
true
} else {
false
}
}
}

impl<'d> fmt::Debug for AstRef<'d> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "AstRef({:?})", self.node_index)
// TODO: doc (parent must be listy)
pub fn remove(self, s: &mut DocStorage) -> bool {
if let Some(parent) = self.parent(s) {
match parent.arity(s) {
Arity::Fixed(_) => false,
Arity::Texty => false,
Arity::Listy(_) => {
s.forest.detach(self.0);
true
}
}
} else {
false
}
}
}

// Ast methods:
// - navigation, bookmarks
// - editing: text, children
// - grammar: get construct, arity
// - construction: hole, text, branch
// - PrettyDoc: get construct, notation
35 changes: 32 additions & 3 deletions src/language/forest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ impl<D> Forest<D> {
self.arena[node].child
}

/// Get the `node`'s n'th child, if any.
pub fn nth_child(&self, node: NodeIndex, n: usize) -> Option<NodeIndex> {
if let Some(first_child) = self.arena[node].child {
let mut child = first_child;
for _ in 0..n {
child = self.arena[child].next;
if child == first_child {
return None;
}
}
Some(child)
} else {
None
}
}

/// Get the `node`'s previous sibling, if any.
pub fn prev(&self, node: NodeIndex) -> Option<NodeIndex> {
if self.is_first(node) {
Expand Down Expand Up @@ -158,6 +174,16 @@ impl<D> Forest<D> {
}
}

pub fn sibling_index(&self, node: NodeIndex) -> usize {
let mut sibling = self.first_sibling(node);
let mut sibling_index = 0;
while sibling != node {
sibling = self.arena[sibling].next;
sibling_index += 1;
}
sibling_index
}

/// Borrows the data stored inside `node`.
pub fn data(&self, node: NodeIndex) -> &D {
&self.arena[node].data
Expand Down Expand Up @@ -203,12 +229,13 @@ impl<D> Forest<D> {
/// Swap the positions of two nodes (and their descendants). The two nodes may be part
/// of the same tree or different trees, but they must not overlap - i.e. one node must
/// not be an ancestor of the other. Unless they're the same node, which is a no-op.
pub fn swap(&mut self, node_1: NodeIndex, node_2: NodeIndex) {
/// If the nodes do overlap, returns `false` and does nothing.
pub fn swap(&mut self, node_1: NodeIndex, node_2: NodeIndex) -> bool {
if node_1 == node_2 {
return;
return true;
}
if self.overlaps(node_1, node_2) {
bug!("Forest - can't swap overlapping nodes");
return false;
}

let crack_1 = Crack::new_remove(self, node_1);
Expand All @@ -219,6 +246,8 @@ impl<D> Forest<D> {

let crack_1_again = Crack::new_remove(self, self.swap_dummy);
crack_1_again.fill(self, node_2);

true
}

/// Insert the root node `node` before `at`, so that `node` becomes its previous sibling.
Expand Down
Loading

0 comments on commit 28d5845

Please sign in to comment.