Skip to content

Commit

Permalink
Add AST Passes and the #[toplevel] attribute. (#458)
Browse files Browse the repository at this point in the history
* add toplevel attribute

* add "has" function for attributes
  • Loading branch information
UnsignedByte authored Sep 16, 2024
1 parent 69e9ab3 commit ca53c90
Show file tree
Hide file tree
Showing 12 changed files with 436 additions and 54 deletions.
11 changes: 8 additions & 3 deletions crates/ast/src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ pub enum NumAttr {}
/// An flag attribute
#[derive(Enum, Clone, Copy, PartialEq, EnumString)]
pub enum BoolAttr {
/// This is a toplevel component
#[strum(serialize = "toplevel")]
TopLevel,
/// Use a counter based FSM design
#[strum(serialize = "counter_fsm")]
CounterFSM,
/// Dummy attribute because the [Enum] trait derivation throws errors if there is only one varianat
#[strum(disabled)]
Dummy,
}

/// Represents a single attribute. This is a private enum that is used during
Expand Down Expand Up @@ -50,6 +50,11 @@ impl Attributes {
}
}

/// Check if an attribute is set
pub fn has(&self, attr: impl Into<Attr>) -> bool {
self.attrs[attr.into()].is_some()
}

/// Get the value of an attribute.
pub fn get(&self, attr: impl Into<Attr>) -> Option<u64> {
self.attrs[attr.into()].map(|(v, _)| v)
Expand Down
27 changes: 12 additions & 15 deletions crates/ast/src/component.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::BoolAttr;

use super::{Command, Id, Signature};
use fil_gen as gen;
use gen::GenConfig;
Expand Down Expand Up @@ -44,30 +46,19 @@ impl Component {
}
}

#[derive(Default)]
pub struct Namespace {
/// Imported files
pub imports: Vec<String>,
/// Define externals and their files
pub externs: Vec<Extern>,
/// Components defined in this file
pub components: Vec<Component>,
/// Top-level component id
pub toplevel: String,
/// Top level bindings
pub bindings: Vec<u64>,
}

impl Namespace {
pub fn new(toplevel: String) -> Self {
Self {
imports: Vec::default(),
externs: Vec::default(),
components: Vec::default(),
bindings: Vec::default(),
toplevel,
}
}

/// Returns true if the namespace declares at least one generative module
pub fn requires_gen(&self) -> bool {
self.externs.iter().any(|Extern { gen, .. }| gen.is_some())
Expand Down Expand Up @@ -103,8 +94,14 @@ impl Namespace {
/// Get the index to the top-level component.
/// Currently, this is the distinguished "main" component
pub fn main_idx(&self) -> Option<usize> {
self.components.iter().position(|c| {
c.sig.name.inner() == &Id::from(self.toplevel.clone())
})
self.components
.iter()
.position(|c| c.sig.attributes.has(BoolAttr::TopLevel))
}

/// Get the toplevel component name
pub fn toplevel(&self) -> Option<&str> {
self.main_idx()
.map(|idx| self.components[idx].sig.name.inner().as_ref())
}
}
7 changes: 5 additions & 2 deletions crates/ast/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -902,8 +902,11 @@ impl FilamentParser {
Ok(match_nodes!(
input.into_children();
[imports(imps), comp_or_ext(mixed).., _EOI] => {
let mut namespace = ast::Namespace::new("main".to_string());
namespace.imports = imps;
let mut namespace = ast::Namespace {
imports: imps,
..Default::default()
};

for m in mixed {
match m {
BodyEl::Ext(sig) => namespace.externs.push(sig),
Expand Down
3 changes: 3 additions & 0 deletions crates/filament/src/ast_passes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod toplevel;

pub use toplevel::TopLevel;
84 changes: 84 additions & 0 deletions crates/filament/src/ast_passes/toplevel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use crate::ast_visitor::{Action, Visitor};
use fil_ast as ast;
use fil_utils::{Diagnostics, Error, GPosIdx};

/// Sets the proper FSM Attributes for every component
#[derive(Default)]
pub struct TopLevel {
/// Set to true if we find a toplevel component
has_toplevel: Option<GPosIdx>,
/// Error reporting
diag: Diagnostics,
}

impl Visitor for TopLevel {
fn name() -> &'static str {
"fsm-attributes"
}

fn signature(&mut self, sig: &mut ast::Signature) -> Action {
if sig.attributes.get(ast::BoolAttr::TopLevel) == Some(1) {
if self.has_toplevel.is_some() {
let err = Error::malformed("Multiple top-level components")
.add_note(self.diag.add_info(
"first top-level component here",
self.has_toplevel.unwrap(),
))
.add_note(
self.diag.add_info(
"second top-level component here",
sig.attributes
.get_loc(ast::BoolAttr::TopLevel)
.unwrap(),
),
);

self.diag.add_error(err);
} else {
self.has_toplevel = Some(
sig.attributes.get_loc(ast::BoolAttr::TopLevel).unwrap(),
);
}
}

// Stop traversal into component body
Action::Stop
}

fn external(&mut self, ext: &mut ast::Extern) {
for sig in &mut ext.comps {
if sig.attributes.get(ast::BoolAttr::TopLevel) == Some(1) {
let err =
Error::malformed("External components cannot be top-level")
.add_note(
self.diag.add_info(
"toplevel attribute here",
sig.attributes
.get_loc(ast::BoolAttr::TopLevel)
.unwrap(),
),
);

self.diag.add_error(err);
}
}
}

fn after_traversal(mut self) -> Option<u64> {
self.diag.report_all()
}

fn finish(&mut self, ast: &mut ast::Namespace) {
// If no toplevel component was found, find the component with the name "main"
if self.has_toplevel.is_none() {
for comp in ast.components.iter_mut() {
if comp.sig.name.as_ref() == "main" {
// Add the toplevel attribute to the component
comp.sig.attributes.set(ast::BoolAttr::TopLevel, 1);

return;
}
}
}
}
}
3 changes: 3 additions & 0 deletions crates/filament/src/ast_visitor/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod visitor;

pub use visitor::{Action, Construct, Visitor};
Loading

0 comments on commit ca53c90

Please sign in to comment.