Skip to content

Commit

Permalink
Merge pull request #383 from nblei/cycle_check
Browse files Browse the repository at this point in the history
Cycle check
  • Loading branch information
dalance authored Sep 16, 2023
2 parents 14bb9ad + 19b3e1f commit 2afa65f
Show file tree
Hide file tree
Showing 9 changed files with 415 additions and 2 deletions.
27 changes: 27 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/analyzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ strnum_bitwidth = {workspace = true}
thiserror = {workspace = true}
veryl-metadata = {version = "0.5.5", path = "../metadata"}
veryl-parser = {version = "0.5.5", path = "../parser"}
daggy = "0.8.0"
bimap = "0.6.3"

[dev-dependencies]
toml = {workspace = true}
29 changes: 29 additions & 0 deletions crates/analyzer/src/analyzer_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ use veryl_parser::veryl_token::VerylToken;

#[derive(Error, Diagnostic, Debug)]
pub enum AnalyzerError {
#[diagnostic(
severity(Error),
code(cyclice_type_dependency),
help(""),
url("https://dalance.github.io/veryl/book/06_appendix/02_semantic_error.html#cyclic_type_dependency")
)]
#[error("Cyclic dependency between {start} and {end}")]
CyclicTypeDependency {
start: String,
end: String,
#[source_code]
input: NamedSource,
#[label("Error location")]
error_location: SourceSpan,
},
#[diagnostic(
severity(Error),
code(duplicated_identifier),
Expand Down Expand Up @@ -410,6 +425,20 @@ impl AnalyzerError {
)
}

pub fn cyclic_type_dependency(
source: &str,
start: &str,
end: &str,
token: &VerylToken,
) -> Self {
AnalyzerError::CyclicTypeDependency {
start: start.into(),
end: end.into(),
input: AnalyzerError::named_source(source, token),
error_location: token.token.into(),
}
}

pub fn duplicated_identifier(identifier: &str, source: &str, token: &VerylToken) -> Self {
AnalyzerError::DuplicatedIdentifier {
identifier: identifier.to_string(),
Expand Down
7 changes: 7 additions & 0 deletions crates/analyzer/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod check_statement;
pub mod check_system_function;
pub mod create_reference;
pub mod create_symbol_table;
pub mod create_type_dag;
use check_attribute::*;
use check_direction::*;
use check_enum::*;
Expand All @@ -29,6 +30,8 @@ use crate::analyzer_error::AnalyzerError;
use veryl_metadata::Lint;
use veryl_parser::veryl_walker::Handler;

use self::create_type_dag::CreateTypeDag;

pub struct Pass1Handlers<'a> {
check_attribute: CheckAttribute<'a>,
check_direction: CheckDirection<'a>,
Expand Down Expand Up @@ -88,6 +91,7 @@ pub struct Pass2Handlers<'a> {
check_instance: CheckInstance<'a>,
check_msb_lsb: CheckMsbLsb<'a>,
create_reference: CreateReference<'a>,
create_type_dag: CreateTypeDag<'a>,
}

impl<'a> Pass2Handlers<'a> {
Expand All @@ -99,6 +103,7 @@ impl<'a> Pass2Handlers<'a> {
check_instance: CheckInstance::new(text),
check_msb_lsb: CheckMsbLsb::new(text),
create_reference: CreateReference::new(text),
create_type_dag: CreateTypeDag::new(text),
}
}

Expand All @@ -110,6 +115,7 @@ impl<'a> Pass2Handlers<'a> {
&mut self.check_instance as &mut dyn Handler,
&mut self.check_msb_lsb as &mut dyn Handler,
&mut self.create_reference as &mut dyn Handler,
&mut self.create_type_dag as &mut dyn Handler,
]
}

Expand All @@ -121,6 +127,7 @@ impl<'a> Pass2Handlers<'a> {
ret.append(&mut self.check_instance.errors);
ret.append(&mut self.check_msb_lsb.errors);
ret.append(&mut self.create_reference.errors);
ret.append(&mut self.create_type_dag.errors);
ret
}
}
192 changes: 192 additions & 0 deletions crates/analyzer/src/handlers/create_type_dag.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
use crate::{
analyzer_error::AnalyzerError,
symbol_table::SymbolPathNamespace,
type_dag::{self, Context, DagError},
};
use veryl_parser::{
resource_table,
veryl_grammar_trait::{
EnumDeclaration, ScopedIdentifier, StructUnion, StructUnionDeclaration, TypeDefDeclaration,
VerylGrammarTrait,
},
veryl_token::VerylToken,
ParolError,
};
use veryl_parser::{
veryl_grammar_trait::Identifier,
veryl_walker::{Handler, HandlerPoint},
};

#[derive(Default)]
pub struct CreateTypeDag<'a> {
text: &'a str,
pub errors: Vec<AnalyzerError>,
parent: Option<u32>,
point: HandlerPoint,
ctx: Context,
}

impl<'a> CreateTypeDag<'a> {
pub fn new(text: &'a str) -> Self {
Self {
text,
..Default::default()
}
}

fn insert_node(
&mut self,
path: &SymbolPathNamespace,
name: &str,
token: &VerylToken,
) -> Option<u32> {
match type_dag::insert_node(path, name, token) {
Ok(n) => Some(n),
Err(e) => {
self.errors.push(self.to_analyzer_error(e));
None
}
}
}

fn to_analyzer_error(&self, de: DagError) -> AnalyzerError {
match de {
DagError::Cyclic(s, e) => {
let token: VerylToken = VerylToken {
token: e.token,
comments: vec![],
};
let start = match resource_table::get_str_value(s.token.text) {
Some(s) => s,
None => "<unknown StrId>".into(),
};
let end = match resource_table::get_str_value(e.token.text) {
Some(s) => s,
None => "<unknown StrId>".into(),
};
AnalyzerError::cyclic_type_dependency(self.text, &start, &end, &token)
}
DagError::UnableToResolve(b) => {
let t = b.as_ref();
AnalyzerError::undefined_identifier(&t.name, self.text, &t.token)
}
}
}

fn insert_edge(&mut self, s: u32, e: u32, edge: Context) {
// Reversing this order to make traversal work
match type_dag::insert_edge(e, s, edge) {
Ok(_) => {}
Err(er) => {
self.errors.push(self.to_analyzer_error(er));
}
}
}
}

impl<'a> Handler for CreateTypeDag<'a> {
fn set_point(&mut self, p: HandlerPoint) {
self.point = p;
}
}

impl<'a> VerylGrammarTrait for CreateTypeDag<'a> {
fn veryl(&mut self, _arg: &veryl_parser::veryl_grammar_trait::Veryl) -> Result<(), ParolError> {
if let HandlerPoint::After = self.point {
// Evaluate DAG
}
Ok(())
}

fn struct_union_declaration(&mut self, arg: &StructUnionDeclaration) -> Result<(), ParolError> {
match self.point {
HandlerPoint::Before => {
let path: SymbolPathNamespace = arg.identifier.as_ref().into();
let name = arg.identifier.identifier_token.text();
let token = arg.identifier.identifier_token.clone();
self.parent = self.insert_node(&path, &name, &token);
// Unused for now, but will be useful in the future
// to do this struct vs union chec
match &*arg.struct_union {
StructUnion::Struct(_) => self.ctx = Context::Struct,
StructUnion::Union(_) => self.ctx = Context::Union,
}
}
HandlerPoint::After => {
self.parent = None;
self.ctx = Context::Irrelevant;
}
}
Ok(())
}

fn type_def_declaration(&mut self, arg: &TypeDefDeclaration) -> Result<(), ParolError> {
match self.point {
HandlerPoint::Before => {
let path: SymbolPathNamespace = arg.identifier.as_ref().into();
let name = arg.identifier.identifier_token.text();
let token = arg.identifier.identifier_token.clone();
self.parent = self.insert_node(&path, &name, &token);
self.ctx = Context::TypeDef;
}
HandlerPoint::After => {
self.parent = None;
self.ctx = Context::TypeDef;
}
}
Ok(())
}

fn scoped_identifier(&mut self, arg: &ScopedIdentifier) -> Result<(), ParolError> {
if let HandlerPoint::Before = self.point {
match self.ctx {
Context::Irrelevant => {}
_ => {
let path: SymbolPathNamespace = arg.into();
let name = to_string(arg);
let token = arg.identifier.identifier_token.clone();
let child = self.insert_node(&path, &name, &token);
if let (Some(parent), Some(child)) = (self.parent, child) {
self.insert_edge(parent, child, self.ctx);
}
}
}
}
Ok(())
}

fn enum_declaration(&mut self, arg: &EnumDeclaration) -> Result<(), ParolError> {
match self.point {
HandlerPoint::Before => {
let path: SymbolPathNamespace = arg.identifier.as_ref().into();
let name = arg.identifier.identifier_token.text();
let token = arg.identifier.identifier_token.clone();
self.parent = self.insert_node(&path, &name, &token);
self.ctx = Context::Enum;
}
HandlerPoint::After => {
self.parent = None;
self.ctx = Context::Irrelevant;
}
}
Ok(())
}
}

fn to_string(sid: &ScopedIdentifier) -> String {
let mut rv: String = "".into();

let f = |id: &Identifier, scope: bool| -> String {
let mut s: String = (if scope { "::" } else { "" }).into();
s.push_str(&id.identifier_token.text());
s
};
rv.push_str(&f(&sid.identifier, false));

for sidl in sid.scoped_identifier_list.iter() {
let id = sidl.identifier.as_ref();
rv.push_str(&f(id, true));
}

rv
}
1 change: 1 addition & 0 deletions crates/analyzer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ pub mod namespace;
pub mod namespace_table;
pub mod symbol;
pub mod symbol_table;
pub mod type_dag;
pub use analyzer::Analyzer;
pub use analyzer_error::AnalyzerError;
17 changes: 16 additions & 1 deletion crates/analyzer/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
use crate::evaluator::{Evaluated, Evaluator};
use crate::namespace::Namespace;
use std::cell::Cell;
use std::cell::{Cell, RefCell};
use std::fmt;
use veryl_parser::resource_table::StrId;
use veryl_parser::veryl_grammar_trait as syntax_tree;
use veryl_parser::veryl_token::Token;
use veryl_parser::veryl_walker::VerylWalker;
use veryl_parser::Stringifier;

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SymbolId(usize);

thread_local!(static SYMBOL_ID: RefCell<usize> = RefCell::new(0));

pub fn new_symbol_id() -> SymbolId {
SYMBOL_ID.with(|f| {
let mut ret = f.borrow_mut();
*ret += 1;
SymbolId(*ret)
})
}

#[derive(Debug, Clone)]
pub struct Symbol {
pub token: Token,
pub id: SymbolId,
pub kind: SymbolKind,
pub namespace: Namespace,
pub references: Vec<Token>,
Expand All @@ -28,6 +42,7 @@ impl Symbol {
) -> Self {
Self {
token: *token,
id: new_symbol_id(),
kind,
namespace: namespace.to_owned(),
references: Vec::new(),
Expand Down
Loading

0 comments on commit 2afa65f

Please sign in to comment.