Skip to content

Commit

Permalink
support untyped enum declaration
Browse files Browse the repository at this point in the history
(refs: #539)
  • Loading branch information
taichi-ishitani committed Aug 21, 2024
1 parent 3516e3c commit 369d3f7
Show file tree
Hide file tree
Showing 14 changed files with 7,047 additions and 6,858 deletions.
91 changes: 42 additions & 49 deletions crates/analyzer/src/handlers/check_enum.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use crate::analyzer_error::AnalyzerError;
use crate::evaluator::{Evaluated, Evaluator};
use crate::evaluator::Evaluator;
use crate::symbol::{EnumMemberValue, SymbolKind};
use crate::symbol_table;
use veryl_parser::veryl_grammar_trait::*;
use veryl_parser::veryl_token::Token;
use veryl_parser::veryl_walker::{Handler, HandlerPoint};
use veryl_parser::ParolError;

pub struct CheckEnum<'a> {
pub errors: Vec<AnalyzerError>,
text: &'a str,
point: HandlerPoint,
enum_variants: usize,
enum_member_values: Vec<(Evaluated, Token)>,
}

impl<'a> CheckEnum<'a> {
Expand All @@ -19,8 +18,6 @@ impl<'a> CheckEnum<'a> {
errors: Vec::new(),
text,
point: HandlerPoint::Before,
enum_variants: 0,
enum_member_values: Vec::new(),
}
}
}
Expand All @@ -37,37 +34,46 @@ fn calc_width(value: usize) -> usize {

impl<'a> VerylGrammarTrait for CheckEnum<'a> {
fn enum_declaration(&mut self, arg: &EnumDeclaration) -> Result<(), ParolError> {
match self.point {
HandlerPoint::Before => {
self.enum_variants = 0;
self.enum_member_values.clear();
}
HandlerPoint::After => {
let mut evaluator = Evaluator::new();
let r#type: crate::symbol::Type = arg.scalar_type.as_ref().into();
let width = evaluator.type_width(r#type);
if let Some(width) = width {
if calc_width(self.enum_variants - 1) > width {
let name = arg.identifier.identifier_token.to_string();
self.errors.push(AnalyzerError::too_much_enum_variant(
&name,
self.enum_variants,
width,
self.text,
&arg.identifier.as_ref().into(),
));
}
if let HandlerPoint::Before = self.point {
let enum_symbol = symbol_table::resolve(arg.identifier.as_ref()).unwrap();
if let SymbolKind::Enum(r#enum) = enum_symbol.found.kind {
if let Some(r#type) = r#enum.r#type {
if let Some(width) = Evaluator::new().type_width(r#type) {
let variants = r#enum.members.len();
if calc_width(variants - 1) > width {
let name = arg.identifier.identifier_token.to_string();
self.errors.push(AnalyzerError::too_much_enum_variant(
&name,
variants,
width,
self.text,
&arg.identifier.as_ref().into(),
));
}

for id in r#enum.members {
let member_symbol = symbol_table::get(id).unwrap();
if let SymbolKind::EnumMember(member) = member_symbol.kind {
let (member_width, member_value) = match member.value {
EnumMemberValue::ExplicitValue(_expression, evaluated) => {
if let Some(x) = evaluated {
(calc_width(x), x)
} else {
(0, 0)
}
}
EnumMemberValue::ImplicitValue(x) => (calc_width(x), x),
};

for (enum_value, token) in &self.enum_member_values {
if let Evaluated::Fixed { value, .. } = enum_value {
if calc_width(*value as usize) > width {
self.errors.push(AnalyzerError::too_large_enum_variant(
&token.to_string(),
*value,
width,
self.text,
&token.into(),
));
if member_width > width {
self.errors.push(AnalyzerError::too_large_enum_variant(
&member_symbol.token.to_string(),
member_value as isize,
width,
self.text,
&member_symbol.token.into(),
));
}
}
}
}
Expand All @@ -76,17 +82,4 @@ impl<'a> VerylGrammarTrait for CheckEnum<'a> {
}
Ok(())
}

fn enum_item(&mut self, arg: &EnumItem) -> Result<(), ParolError> {
if let HandlerPoint::Before = self.point {
self.enum_variants += 1;
if let Some(ref x) = arg.enum_item_opt {
let token = arg.identifier.identifier_token.token;
let mut evaluator = Evaluator::new();
let evaluated = evaluator.expression(&x.expression);
self.enum_member_values.push((evaluated, token));
}
}
Ok(())
}
}
46 changes: 42 additions & 4 deletions crates/analyzer/src/handlers/create_symbol_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ use crate::analyzer_error::AnalyzerError;
use crate::attribute::AllowItem;
use crate::attribute::Attribute as Attr;
use crate::attribute_table;
use crate::evaluator::Evaluated;
use crate::evaluator::Evaluator;
use crate::namespace::Namespace;
use crate::namespace_table;
use crate::symbol;
use crate::symbol::ClockDomain as SymClockDomain;
use crate::symbol::Direction as SymDirection;
use crate::symbol::Type as SymType;
use crate::symbol::{
ConnectTarget, DocComment, EnumMemberProperty, EnumProperty, FunctionProperty,
ConnectTarget, DocComment, EnumMemberProperty, EnumMemberValue, EnumProperty, FunctionProperty,
GenericParameterProperty, InstanceProperty, InterfaceProperty, ModportFunctionMemberProperty,
ModportProperty, ModportVariableMemberProperty, ModuleProperty, PackageProperty, Parameter,
ParameterProperty, ParameterScope, ParameterValue, Port, PortProperty, StructMemberProperty,
Expand Down Expand Up @@ -42,6 +44,7 @@ pub struct CreateSymbolTable<'a> {
attribute_lines: HashSet<u32>,
struct_or_union: Option<StructOrUnion>,
enum_member_prefix: Option<String>,
enum_member_value: Option<EnumMemberValue>,
enum_members: Vec<Option<SymbolId>>,
struct_union_members: Vec<Option<SymbolId>>,
affiniation: Vec<VariableAffiniation>,
Expand Down Expand Up @@ -583,6 +586,9 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> {
// default prefix
self.enum_member_prefix = Some(arg.identifier.identifier_token.to_string());

// reset enum value
self.enum_member_value = None;

// overridden prefix by attribute
let attrs = attribute_table::get(&arg.r#enum.enum_token.token);
for attr in attrs {
Expand All @@ -595,8 +601,11 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> {
self.namespace.pop();
self.enum_member_prefix = None;

let r#type = arg.scalar_type.as_ref().into();
let members: Vec<_> = self.enum_members.drain(0..).flatten().collect();
let r#type = arg
.enum_declaration_opt
.as_ref()
.map(|x| x.scalar_type.as_ref().into());
let property = EnumProperty { r#type, members };
let kind = SymbolKind::Enum(property);
self.insert_symbol(&arg.identifier.identifier_token.token, kind, false);
Expand All @@ -607,9 +616,36 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> {

fn enum_item(&mut self, arg: &EnumItem) -> Result<(), ParolError> {
if let HandlerPoint::Before = self.point {
let value = arg.enum_item_opt.as_ref().map(|x| *x.expression.clone());
let value = if let Some(ref x) = arg.enum_item_opt {
let mut evaluator = Evaluator::new();
let evaluated = evaluator.expression(&x.expression);
if let Evaluated::Fixed { value, .. } = evaluated {
EnumMemberValue::ExplicitValue(*x.expression.clone(), Some(value as usize))
} else {
EnumMemberValue::ExplicitValue(*x.expression.clone(), None)
}
} else if let Some(ref x) = self.enum_member_value {
let previous_value = match x {
EnumMemberValue::ExplicitValue(_expression, evaluated) => {
if let Some(value) = evaluated {
value
} else {
// TODO:
// report error
unreachable!();
}
}
EnumMemberValue::ImplicitValue(value) => value,
};
EnumMemberValue::ImplicitValue(previous_value + 1)
} else {
EnumMemberValue::ImplicitValue(0)
};
let prefix = self.enum_member_prefix.clone().unwrap();
let property = EnumMemberProperty { value, prefix };
let property = EnumMemberProperty {
value: value.clone(),
prefix,
};
let kind = SymbolKind::EnumMember(property);
let id = self.insert_symbol(&arg.identifier.identifier_token.token, kind, false);
self.enum_members.push(id);
Expand All @@ -626,6 +662,8 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> {
if let Some(namespace) = namespace {
self.namespace.push(namespace);
}

self.enum_member_value = Some(value.clone());
}
Ok(())
}
Expand Down
20 changes: 15 additions & 5 deletions crates/analyzer/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,12 +458,16 @@ impl fmt::Display for SymbolKind {
format!("typedef alias ({})", x.r#type)
}
SymbolKind::Enum(x) => {
format!("enum ({})", x.r#type)
if x.r#type.is_some() {
format!("enum ({})", x.r#type.as_ref().unwrap())
} else {
"enum ()".to_string()
}
}
SymbolKind::EnumMember(x) => {
if let Some(ref x) = x.value {
if let EnumMemberValue::ExplicitValue(ref expression, ref _evaluated) = x.value {
let mut stringifier = Stringifier::new();
stringifier.expression(x);
stringifier.expression(expression);
format!("enum member = {}", stringifier.as_str())
} else {
"enum member".to_string()
Expand Down Expand Up @@ -995,13 +999,19 @@ pub struct TypeDefProperty {

#[derive(Debug, Clone)]
pub struct EnumProperty {
pub r#type: Type,
pub r#type: Option<Type>,
pub members: Vec<SymbolId>,
}

#[derive(Debug, Clone)]
pub enum EnumMemberValue {
ImplicitValue(usize),
ExplicitValue(syntax_tree::Expression, Option<usize>),
}

#[derive(Debug, Clone)]
pub struct EnumMemberProperty {
pub value: Option<syntax_tree::Expression>,
pub value: EnumMemberValue,
pub prefix: String,
}

Expand Down
15 changes: 15 additions & 0 deletions crates/analyzer/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,21 @@ fn too_large_enum_variant() {
errors[0],
AnalyzerError::TooLargeEnumVariant { .. }
));

let code = r#"
module ModuleB {
enum EnumB: logic<2> {
A = 3,
B,
}
}
"#;

let errors = analyze(code);
assert!(matches!(
errors[0],
AnalyzerError::TooLargeEnumVariant { .. }
));
}

#[test]
Expand Down
42 changes: 40 additions & 2 deletions crates/emitter/src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use veryl_analyzer::attribute::Attribute as Attr;
use veryl_analyzer::attribute_table;
use veryl_analyzer::evaluator::{Evaluated, Evaluator};
use veryl_analyzer::namespace::Namespace;
use veryl_analyzer::symbol::TypeModifier as SymTypeModifier;
use veryl_analyzer::symbol::{EnumMemberValue, TypeModifier as SymTypeModifier};
use veryl_analyzer::symbol::{GenericMap, Symbol, SymbolId, SymbolKind, TypeKind};
use veryl_analyzer::symbol_path::{GenericSymbolPath, SymbolPath};
use veryl_analyzer::symbol_table;
Expand Down Expand Up @@ -681,6 +681,40 @@ impl Emitter {
FunctionItem::Statement(x) => self.statement(&x.statement),
};
}

fn default_enum_type(&mut self, arg: &EnumDeclaration) {
let enum_symbol = symbol_table::resolve(arg.identifier.as_ref())
.unwrap()
.found;
if let SymbolKind::Enum(r#enum) = enum_symbol.kind {
let mut max_value = r#enum.members.len() - 1;

for id in r#enum.members {
let member_symbol = symbol_table::get(id).unwrap();
if let SymbolKind::EnumMember(member) = member_symbol.kind {
match member.value {
EnumMemberValue::ExplicitValue(_expression, evaluated) => {
if evaluated.is_some() && evaluated.unwrap() > max_value {
max_value = evaluated.unwrap();
}
}
EnumMemberValue::ImplicitValue(value) => {
if value > max_value {
max_value = value;
}
}
}
}
}

let width = if max_value == 1 {
1
} else {
usize::BITS - max_value.leading_zeros()
};
self.str(&format!("logic [{}-1:0]", width));
}
}
}

fn is_let_statement(arg: &Statement) -> bool {
Expand Down Expand Up @@ -2341,7 +2375,11 @@ impl VerylWalker for Emitter {
.append(&Some(String::from("typedef ")), &None),
);
self.space(1);
self.scalar_type(&arg.scalar_type);
if let Some(ref x) = arg.enum_declaration_opt {
self.scalar_type(&x.scalar_type);
} else {
self.default_enum_type(arg);
}
self.space(1);
self.token_will_push(&arg.l_brace.l_brace_token);
self.newline_push();
Expand Down
8 changes: 5 additions & 3 deletions crates/formatter/src/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1080,9 +1080,11 @@ impl VerylWalker for Formatter {
self.r#enum(&arg.r#enum);
self.space(1);
self.identifier(&arg.identifier);
self.colon(&arg.colon);
self.space(1);
self.scalar_type(&arg.scalar_type);
if let Some(ref x) = arg.enum_declaration_opt {
self.colon(&x.colon);
self.space(1);
self.scalar_type(&x.scalar_type);
}
self.space(1);
self.token_will_push(&arg.l_brace.l_brace_token);
self.newline_push();
Expand Down
Loading

0 comments on commit 369d3f7

Please sign in to comment.