From 8fe2407a36259552761cd1828bb2d9c63731b312 Mon Sep 17 00:00:00 2001 From: Lennart Van Hirtum Date: Wed, 10 Jan 2024 18:10:55 +0100 Subject: [PATCH] Rework alloc_module_interface to reuse initialize_interface code and initial stages of Latency annotations --- README.md | 5 + multiply_add.sus | 5 + src/ast.rs | 8 +- src/codegen_fallback.rs | 2 +- src/dev_aid/lsp.rs | 1 - src/dev_aid/syntax_highlighting.rs | 10 +- src/flattening.rs | 198 +++++++++++++++-------------- src/instantiation/mod.rs | 8 +- src/main.rs | 2 +- src/parser.rs | 15 ++- src/tokenizer.rs | 5 +- 11 files changed, 136 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index cd62165..95e3012 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ The main goals of the language are roughly listed below: - [ ] Arbitrary FPGA hardware full flow - [x] Generative Code - [ ] Templates + ### Parsing - [x] Basic Tokenizer - [x] Basic Syntax Error Reporting @@ -45,6 +46,8 @@ The main goals of the language are roughly listed below: - [x] Can Parse Multiply-Add pipeline - [x] Can Parse Blur2 filter - [x] If Statements +- [x] Latency Specifiers +- [ ] Bound Specifiers - [ ] Structs - [x] For Loops - [ ] Multi-Interface Syntax @@ -71,6 +74,8 @@ The main goals of the language are roughly listed below: - [x] Basic LSP for VSCode integration - [x] Syntax Highlighting - [x] Error and Warning Reporting +- [ ] Hover type information +- [ ] Code completion - [ ] Per-Line Resource Utilization Reporting ### Code Generation diff --git a/multiply_add.sus b/multiply_add.sus index 87c0a8f..6de4806 100644 --- a/multiply_add.sus +++ b/multiply_add.sus @@ -360,6 +360,11 @@ module first_bit_idx_6 : bool[6] bits -> int first, bool all_zeros { } +module multiply_add_with_latencies : int a'0, int b'0, int c'0 -> int r'0 { + int tmp'1 = multiply(a, b); + reg r = tmp + c; +} + module first_bit_idx_24 : bool[24] bits -> int first { int[4] offsets; bool[4] was_nonzeros; diff --git a/src/ast.rs b/src/ast.rs index e11b6f7..0e0a232 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -44,8 +44,7 @@ pub enum IdentifierType { Output, Local, State, - Generative, - Virtual // Generated at the interfaces of submodule instantiations + Generative } impl From for Span { @@ -74,8 +73,9 @@ pub struct SignalDeclaration { pub span : Span, pub name_token : usize, pub typ : SpanTypeExpression, - pub name : Box, // File position - pub identifier_type : IdentifierType + pub name : Box, + pub identifier_type : IdentifierType, + pub latency_expr : Option } #[derive(Debug,Clone,Copy)] diff --git a/src/codegen_fallback.rs b/src/codegen_fallback.rs index 8ad7a9c..ee67f65 100644 --- a/src/codegen_fallback.rs +++ b/src/codegen_fallback.rs @@ -71,7 +71,7 @@ pub fn gen_verilog_code(md : &Module, instance : &InstantiatedModule) -> String // Don't print named inputs and outputs, already did that in interface match wire_decl.identifier_type { IdentifierType::Input | IdentifierType::Output => {continue;} - IdentifierType::Local | IdentifierType::State | IdentifierType::Generative | IdentifierType::Virtual => {} + IdentifierType::Local | IdentifierType::State | IdentifierType::Generative => {} } } let wire_or_reg = if let RealWireDataSource::Multiplexer{is_state: initial_value, sources: _} = &w.source { diff --git a/src/dev_aid/lsp.rs b/src/dev_aid/lsp.rs index 2c1c58b..f9214c8 100644 --- a/src/dev_aid/lsp.rs +++ b/src/dev_aid/lsp.rs @@ -135,7 +135,6 @@ fn get_semantic_token_type_from_ide_token(tok : &IDEToken) -> u32 { IDETokenType::Identifier(IDEIdentifierType::Value(IdentifierType::State)) => 3, IDETokenType::Identifier(IDEIdentifierType::Value(IdentifierType::Local)) => 3, IDETokenType::Identifier(IDEIdentifierType::Value(IdentifierType::Generative)) => 3, - IDETokenType::Identifier(IDEIdentifierType::Value(IdentifierType::Virtual)) => unreachable!(), IDETokenType::Identifier(IDEIdentifierType::Constant) => 9, // make it 'OPERATOR'? IDETokenType::Identifier(IDEIdentifierType::Unknown) => 2, // make it 'OPERATOR'? IDETokenType::Identifier(IDEIdentifierType::Interface) => 7, // FUNCTION diff --git a/src/dev_aid/syntax_highlighting.rs b/src/dev_aid/syntax_highlighting.rs index a0cdd0a..491ace1 100644 --- a/src/dev_aid/syntax_highlighting.rs +++ b/src/dev_aid/syntax_highlighting.rs @@ -75,7 +75,6 @@ fn pretty_print(file_text : &str, tokens : &[Token], ide_infos : &[IDEToken]) { IDETokenType::Identifier(IDEIdentifierType::Value(IdentifierType::Input)) => Style::new().blue().bright(), IDETokenType::Identifier(IDEIdentifierType::Value(IdentifierType::Output)) => Style::new().blue().dim(), IDETokenType::Identifier(IDEIdentifierType::Value(IdentifierType::Generative)) => Style::new().blue().bright().bold(), - IDETokenType::Identifier(IDEIdentifierType::Value(IdentifierType::Virtual)) => unreachable!(), IDETokenType::Identifier(IDEIdentifierType::Constant) => Style::new().blue().bold(), IDETokenType::Identifier(IDEIdentifierType::Type) => Style::new().magenta().bright(), IDETokenType::Identifier(IDEIdentifierType::Interface) => Style::new().yellow(), @@ -124,18 +123,17 @@ fn walk_name_color(all_objects : &[NamedUUID], links : &Links, result : &mut [ID Instantiation::Wire(w) => { if let &WireSource::WireRead{from_wire} = &w.source { let decl = module.flattened.instantiations[from_wire].extract_wire_declaration(); - if decl.identifier_type == IdentifierType::Virtual {continue;} // Virtual wires don't appear in the program text + if decl.is_remote_declaration.is_some() {continue;} // Virtual wires don't appear in this program text result[w.span.assert_is_single_token()].typ = IDETokenType::Identifier(IDEIdentifierType::Value(decl.identifier_type)); } } Instantiation::WireDeclaration(decl) => { - if decl.identifier_type == IdentifierType::Virtual {continue;} // Virtual wires don't appear in the program text - let Some(name_token) = decl.name_token else {continue}; - result[name_token].typ = IDETokenType::Identifier(IDEIdentifierType::Value(decl.identifier_type)); + if decl.is_remote_declaration.is_some() {continue;} // Virtual wires don't appear in this program text + result[decl.name_token].typ = IDETokenType::Identifier(IDEIdentifierType::Value(decl.identifier_type)); } Instantiation::Connection(conn) => { let decl = module.flattened.instantiations[conn.to.root].extract_wire_declaration(); - if decl.identifier_type == IdentifierType::Virtual {continue;} // Virtual wires don't appear in the program text + if decl.is_remote_declaration.is_some() {continue;} // Virtual wires don't appear in this program text result[conn.to.span.0].typ = IDETokenType::Identifier(IDEIdentifierType::Value(decl.identifier_type)); } Instantiation::SubModule(_) | Instantiation::IfStatement(_) | Instantiation::ForStatement(_) => {} diff --git a/src/flattening.rs b/src/flattening.rs index 205da30..f3ec257 100644 --- a/src/flattening.rs +++ b/src/flattening.rs @@ -1,8 +1,8 @@ use std::{ops::{Deref, Range}, iter::zip}; use crate::{ - ast::{Span, Module, Expression, SpanExpression, LocalOrGlobal, Operator, AssignableExpression, SpanAssignableExpression, Statement, CodeBlock, IdentifierType, TypeExpression, DeclIDMarker, SignalDeclaration}, - linker::{Linker, Named, Linkable, FileUUID, NamedUUID}, + ast::{Span, Module, Expression, SpanExpression, LocalOrGlobal, Operator, AssignableExpression, SpanAssignableExpression, Statement, CodeBlock, IdentifierType, TypeExpression, DeclIDMarker, DeclID}, + linker::{Linker, Named, Linkable, NamedUUID, FileUUID}, errors::{ErrorCollector, error_info}, arena_alloc::{UUID, UUIDMarker, FlatAlloc, UUIDRange}, typing::{Type, typecheck_unary_operator, get_binary_operator_types, typecheck, typecheck_is_array_indexer, BOOL_TYPE, INT_TYPE}, value::Value }; @@ -30,13 +30,8 @@ pub enum ConnectionWritePathElementComputed { pub struct ConnectionWrite { pub root : FlatID, pub path : Vec, - pub span : Span -} - -impl ConnectionWrite { - pub fn simple(root : FlatID, span : Span) -> ConnectionWrite { - ConnectionWrite { root, path: Vec::new(), span } - } + pub span : Span, + pub is_remote_declaration : Option } #[derive(Debug)] @@ -72,6 +67,7 @@ pub struct WireInstance { pub typ : Type, pub is_compiletime : bool, pub span : Span, + pub is_remote_declaration : Option, pub source : WireSource } @@ -79,14 +75,16 @@ pub struct WireInstance { pub struct WireDeclaration { pub typ : Type, pub typ_span : Span, + pub is_remote_declaration : Option, + pub name_token : usize, + pub name : Box, pub read_only : bool, pub identifier_type : IdentifierType, - pub name : Box, - pub name_token : Option + //pub latency_specifier : Option } impl WireDeclaration { pub fn get_full_decl_span(&self) -> Span { - let name_pos = self.name_token.unwrap(); // Temporary, name_token should always be present for proper declarations. + let name_pos = self.name_token; Span(self.typ_span.0, name_pos) } } @@ -96,6 +94,7 @@ pub struct SubModuleInstance { pub module_uuid : NamedUUID, pub name : Box, pub typ_span : Span, + pub is_remote_declaration : Option, pub outputs_start : usize, pub local_wires : Box<[FlatID]> } @@ -167,18 +166,29 @@ impl Instantiation { } } } + + pub fn get_location(&self) -> Option<(Span, Option)> { + match self { + Instantiation::SubModule(sm) => Some((sm.typ_span, sm.is_remote_declaration)), + Instantiation::WireDeclaration(decl) => Some((decl.typ_span, decl.is_remote_declaration)), + Instantiation::Wire(w) => Some((w.span, w.is_remote_declaration)), + Instantiation::Connection(conn) => Some((conn.to.span, conn.to.is_remote_declaration)), + Instantiation::IfStatement(_) | Instantiation::ForStatement(_) => None + } + } } -struct FlatteningContext<'l, 'm> { +struct FlatteningContext<'inst, 'l, 'm> { decl_to_flat_map : FlatAlloc, DeclIDMarker>, - instantiations : FlatAlloc, + instantiations : &'inst mut FlatAlloc, errors : ErrorCollector, + is_remote_declaration : Option, linker : &'l Linker, module : &'m Module, } -impl<'l, 'm> FlatteningContext<'l, 'm> { +impl<'inst, 'l, 'm> FlatteningContext<'inst, 'l, 'm> { pub fn map_to_type(&mut self, type_expr : &TypeExpression) -> Type { match type_expr { TypeExpression::Named(n) => { @@ -203,9 +213,49 @@ impl<'l, 'm> FlatteningContext<'l, 'm> { } } } + fn flatten_declaration(&mut self, decl_id : DeclID) -> FlatID { + let decl = &self.module.declarations[decl_id]; + + let parsed_typ_expr = self.map_to_type(&decl.typ.0); + let typ_span = decl.typ.1; + + let typ = if let Some(root_ref) = parsed_typ_expr.get_root() { + match &self.linker.links.globals[root_ref] { + Named::Module(md) => { + if let Type::Named(name) = parsed_typ_expr { + return self.alloc_module_interface(decl.name.clone(), md, name, typ_span) + } else { + todo!("Implement submodule arrays"); + //Instantiation::Error + } + } + Named::Constant(c) => { + self.errors.error_basic(typ_span, format!("This should be the type of a declaration, but it refers to the constant '{}'", c.get_full_name())); + Type::Error + } + Named::Type(_) => { + parsed_typ_expr + } + } + } else { + // Error report handled by linker + Type::Error + }; + let inst_id = self.instantiations.alloc(Instantiation::WireDeclaration(WireDeclaration{ + typ, + typ_span, + is_remote_declaration : self.is_remote_declaration, + read_only : false, + identifier_type : decl.identifier_type, + name : decl.name.clone(), + name_token : decl.name_token + })); - fn initialize_interface(&mut self) -> Box<[FlatID]> { - let wire_list : Vec = self.module.ports.iter().enumerate().map(|(id, decl_id)|{ + self.decl_to_flat_map[decl_id] = Some(inst_id); + inst_id + } + fn initialize_interface(&mut self) -> Box<[FlatID]> { + self.module.ports.iter().enumerate().map(|(id, decl_id)|{ let is_input = id < self.module.outputs_start; let decl = &self.module.declarations[*decl_id]; @@ -214,45 +264,37 @@ impl<'l, 'm> FlatteningContext<'l, 'm> { let wire_id = self.instantiations.alloc(Instantiation::WireDeclaration(WireDeclaration{ typ : typ.clone(), typ_span : decl.typ.1, - read_only: is_input, + is_remote_declaration : self.is_remote_declaration, + read_only: is_input ^ IS_SUBMODULE, identifier_type : decl.identifier_type, name : decl.name.clone(), - name_token : Some(decl.name_token) + name_token : decl.name_token })); self.decl_to_flat_map[*decl_id] = Some(wire_id); wire_id - }).collect(); - - wire_list.into_boxed_slice() + }).collect() } fn alloc_module_interface(&mut self, name : Box, module : &Module, module_uuid : NamedUUID, typ_span : Span) -> FlatID { - let mut local_wires = Vec::new(); - for (port_id, decl_id) in module.ports.iter().enumerate() { - let is_input = port_id < module.outputs_start; - let decl = &module.declarations[*decl_id]; - - let typ = self.map_to_type(&decl.typ.0); - - let wire_id = self.instantiations.alloc(Instantiation::WireDeclaration(WireDeclaration{ - typ, - typ_span, - read_only : !is_input, - identifier_type : IdentifierType::Virtual, - name : format!("{}_{}", &name, &decl.name).into_boxed_str(), - name_token : None - })); - - local_wires.push(wire_id); - } + let mut nested_context = FlatteningContext { + decl_to_flat_map: module.declarations.iter().map(|_| None).collect(), + instantiations: self.instantiations, + errors: ErrorCollector::new(module.link_info.file), // Temporary ErrorCollector, unused + is_remote_declaration: Some(module_uuid), + linker: self.linker, + module, + }; + + let local_wires = nested_context.initialize_interface::(); self.instantiations.alloc(Instantiation::SubModule(SubModuleInstance{ name, module_uuid, + is_remote_declaration : self.is_remote_declaration, typ_span, outputs_start : module.outputs_start, - local_wires : local_wires.into_boxed_slice() + local_wires })) } // Returns the module, full interface, and the output range for the function call syntax @@ -304,7 +346,7 @@ impl<'l, 'm> FlatteningContext<'l, 'm> { continue; }*/ let func_input_port = &submodule_local_wires[field]; - self.instantiations.alloc(Instantiation::Connection(Connection{num_regs: 0, from: arg_read_side, to: ConnectionWrite::simple(*func_input_port, *name_expr_span)})); + self.instantiations.alloc(Instantiation::Connection(Connection{num_regs: 0, from: arg_read_side, to: ConnectionWrite{root : *func_input_port, path : Vec::new(), span : *name_expr_span, is_remote_declaration : self.is_remote_declaration}})); } } @@ -361,7 +403,7 @@ impl<'l, 'm> FlatteningContext<'l, 'm> { } }; - let wire_instance = WireInstance{typ : Type::Unknown, span : *expr_span, is_compiletime, source}; + let wire_instance = WireInstance{typ : Type::Unknown, span : *expr_span, is_compiletime, source, is_remote_declaration : self.is_remote_declaration,}; Some(self.instantiations.alloc(Instantiation::Wire(wire_instance))) } fn flatten_assignable_expr(&mut self, (expr, span) : &SpanAssignableExpression) -> Option { @@ -375,7 +417,7 @@ impl<'l, 'm> FlatteningContext<'l, 'm> { self.errors.error_with_info(*span, "Cannot Assign to Read-Only value", vec![decl_info]); return None } - ConnectionWrite{root, path : Vec::new(), span : *span} + ConnectionWrite{root, path : Vec::new(), span : *span, is_remote_declaration : self.is_remote_declaration,} } AssignableExpression::ArrayIndex(arr_box) => { let (arr, idx_expr, _bracket_span) = arr_box.deref(); @@ -391,52 +433,11 @@ impl<'l, 'm> FlatteningContext<'l, 'm> { } }) } - fn flatten_declaration(&mut self, decl : &SignalDeclaration) -> FlatID { - assert!(decl.identifier_type != IdentifierType::Input); - assert!(decl.identifier_type != IdentifierType::Output); - - let parsed_typ_expr = self.map_to_type(&decl.typ.0); - let typ_span = decl.typ.1; - - let typ = if let Some(root_ref) = parsed_typ_expr.get_root() { - match &self.linker.links.globals[root_ref] { - Named::Module(md) => { - if let Type::Named(name) = parsed_typ_expr { - return self.alloc_module_interface(decl.name.clone(), md, name, typ_span) - } else { - todo!("Implement submodule arrays"); - //Instantiation::Error - } - } - Named::Constant(c) => { - self.errors.error_basic(typ_span, format!("This should be the type of a declaration, but it refers to the constant '{}'", c.get_full_name())); - Type::Error - } - Named::Type(_) => { - parsed_typ_expr - } - } - } else { - // Error report handled by linker - Type::Error - }; - self.instantiations.alloc(Instantiation::WireDeclaration(WireDeclaration{ - typ, - typ_span, - read_only : false, - identifier_type : decl.identifier_type, - name : decl.name.clone(), - name_token : Some(decl.name_token) - })) - } fn flatten_code(&mut self, code : &CodeBlock) { for (stmt, stmt_span) in &code.statements { match stmt { Statement::Declaration(decl_id) => { - let decl = &self.module.declarations[*decl_id]; - let wire_id = self.flatten_declaration(decl); - - self.decl_to_flat_map[*decl_id] = Some(wire_id); + let _wire_id = self.flatten_declaration(*decl_id); } Statement::Assign{to, expr : (Expression::FuncCall(func_and_args), func_span), eq_sign_position} => { let Some((md, interface, outputs_range)) = self.desugar_func_call(&func_and_args, func_span.1) else {continue}; @@ -461,7 +462,7 @@ impl<'l, 'm> FlatteningContext<'l, 'm> { // temporary let module_port_wire_decl = self.instantiations[*field].extract_wire_declaration(); - let module_port_proxy = self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : module_port_wire_decl.typ.clone(), is_compiletime : module_port_wire_decl.identifier_type == IdentifierType::Generative, span : *func_span, source : WireSource::WireRead { from_wire: *field }})); + let module_port_proxy = self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : module_port_wire_decl.typ.clone(), is_compiletime : module_port_wire_decl.identifier_type == IdentifierType::Generative, span : *func_span, is_remote_declaration : self.is_remote_declaration, source : WireSource::WireRead { from_wire: *field }})); self.instantiations.alloc(Instantiation::Connection(Connection{num_regs : to_i.num_regs, from: module_port_proxy, to: write_side})); } }, @@ -499,9 +500,7 @@ impl<'l, 'm> FlatteningContext<'l, 'm> { if_stmt.else_end = else_end; } Statement::For{var : decl_id, range, code} => { - let decl = &self.module.declarations[*decl_id]; - let loop_var_decl = self.flatten_declaration(decl); - self.decl_to_flat_map[*decl_id] = Some(loop_var_decl); + let loop_var_decl = self.flatten_declaration(*decl_id); let start = self.flatten_expr(&range.from); let end = self.flatten_expr(&range.to); @@ -572,26 +571,27 @@ impl FlattenedModule { It is template-preserving */ pub fn initialize(linker : &Linker, module : &Module, starts_with_errors : bool) -> FlattenedModule { + let mut instantiations = FlatAlloc::new(); let mut context = FlatteningContext{ decl_to_flat_map: module.declarations.iter().map(|_| None).collect(), - instantiations: FlatAlloc::new(), + instantiations: &mut instantiations, errors: ErrorCollector::new(module.link_info.file), + is_remote_declaration : None, linker, module, }; context.errors.did_error.set(starts_with_errors); - let interface_ports = context.initialize_interface(); + let interface_ports = context.initialize_interface::(); context.flatten_code(&module.code); - let flat_mod = FlattenedModule { - instantiations : context.instantiations, + FlattenedModule { errors : context.errors, + instantiations : instantiations, interface_ports, outputs_start : module.outputs_start - }; - flat_mod + } } /* Type Checking */ @@ -763,8 +763,10 @@ impl FlattenedModule { // Now produce warnings from the unused list for (id, inst) in &self.instantiations { if !is_instance_used_map[id] { - if let Instantiation::WireDeclaration(WireDeclaration{typ : _, typ_span : _, read_only : _, identifier_type : _, name : _, name_token : Some(name_token)}) = inst { - self.errors.warn_basic(Span::from(*name_token), "Unused Variable: This variable does not affect the output ports of this module"); + if let Instantiation::WireDeclaration(decl) = inst { + if decl.is_remote_declaration.is_none() { + self.errors.warn_basic(Span::from(decl.name_token), "Unused Variable: This variable does not affect the output ports of this module"); + } } } } diff --git a/src/instantiation/mod.rs b/src/instantiation/mod.rs index f220269..c59b18f 100644 --- a/src/instantiation/mod.rs +++ b/src/instantiation/mod.rs @@ -180,10 +180,6 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { } } } - fn extract_bool_from_value(&self, val : &Value, span : Span) -> Option { - let Value::Bool(val) = val else {self.errors.error_basic(span, format!("Value is not a bool, it is {val:?} instead")); return None}; - Some(*val) - } fn concretize_type(&self, typ : &Type, span : Span) -> Option { match typ { Type::Error | Type::Unknown => unreachable!("Bad types should be caught in flattening: {}", typ.to_string(self.linker)), @@ -319,8 +315,8 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { SubModuleOrWire::Wire(w) => Some(*w), SubModuleOrWire::CompileTimeValue(v) => { let value = v.clone(); - let Instantiation::Wire(WireInstance{typ, source : _, is_compiletime : _, span}) = &self.flattened.instantiations[flat_id] else {unreachable!()}; - let typ = self.concretize_type(typ, *span)?; + let Instantiation::Wire(wire) = &self.flattened.instantiations[flat_id] else {unreachable!()}; + let typ = self.concretize_type(&wire.typ, wire.span)?; let name = self.get_unique_name(); Some(self.wires.alloc(RealWire{source : RealWireDataSource::Constant{value}, original_wire : flat_id, typ, name})) } diff --git a/src/main.rs b/src/main.rs index e62a1fa..d97b0fa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,7 +35,7 @@ fn codegen_to_file(linker : &Linker, id : NamedUUID, md : &Module) -> Option<()> let module_name = md.link_info.name.deref(); if inst.errors.did_error.get() { - println!("There were errors in {module_name}"); + println!("There were instantiation errors in {module_name}"); return None; } //println!("Generating Verilog for {module_name}:"); diff --git a/src/parser.rs b/src/parser.rs index b5ee780..9cfb014 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -320,9 +320,9 @@ impl<'file> ASTParserContext<'file> { } } - fn add_declaration(&mut self, type_expr : SpanTypeExpression, name : TokenContent, identifier_type : IdentifierType, scope : &mut LocalVariableContext<'_, 'file>) -> DeclID { + fn add_declaration(&mut self, type_expr : SpanTypeExpression, name : TokenContent, identifier_type : IdentifierType, latency_expr : Option, scope : &mut LocalVariableContext<'_, 'file>) -> DeclID { let span = Span(type_expr.1.0, name.position); - let decl = SignalDeclaration{typ : type_expr, span, name_token : name.position, name : self.file_text[name.text.clone()].into(), identifier_type}; + let decl = SignalDeclaration{typ : type_expr, span, name_token : name.position, name : self.file_text[name.text.clone()].into(), identifier_type, latency_expr}; let decl_id = self.declarations.alloc(decl); if let Err(conflict) = scope.add_declaration(&self.file_text[name.text.clone()], decl_id) { self.errors.error_with_info(span, format!("This name was already declared previously"), vec![ @@ -439,7 +439,8 @@ impl<'file> ASTParserContext<'file> { fn parse_signal_declaration(&mut self, token_stream : &mut TokenStream, identifier_type : IdentifierType, scope : &mut LocalVariableContext<'_, 'file>) -> Option { let sig_type = self.try_parse_type(token_stream, scope)?; let name = self.eat_identifier(token_stream, "signal declaration")?; - Some(self.add_declaration(sig_type, name, identifier_type, scope)) + let latency_expr = self.parse_optional_latency_decl(token_stream, scope); + Some(self.add_declaration(sig_type, name, identifier_type, latency_expr, scope)) } fn try_parse_type(&mut self, token_stream : &mut TokenStream, scope : &LocalVariableContext) -> Option { @@ -455,6 +456,11 @@ impl<'file> ASTParserContext<'file> { Some(cur_type) } + fn parse_optional_latency_decl(&mut self, token_stream : &mut TokenStream, scope : &LocalVariableContext<'_, 'file>) -> Option { + let _acc_tok = token_stream.eat_is_plain(kw("'"))?; + self.parse_expression(token_stream, scope) + } + fn try_parse_declaration(&mut self, token_stream : &mut TokenStream, scope : &mut LocalVariableContext<'_, 'file>) -> Option<(DeclID, Span)> { let mut state_kw = token_stream.eat_is_plain(kw("state")); let generative_kw = token_stream.eat_is_plain(kw("gen")); @@ -481,7 +487,8 @@ impl<'file> ASTParserContext<'file> { let typ = self.try_parse_type(token_stream, scope)?; let name_token = token_stream.eat_is_plain(TOKEN_IDENTIFIER)?; - let local_idx = self.add_declaration(typ, name_token.clone(), identifier_type, scope); + let latency_expr = self.parse_optional_latency_decl(token_stream, scope); + let local_idx = self.add_declaration(typ, name_token.clone(), identifier_type, latency_expr, scope); Some((local_idx, Span::from(name_token.position))) } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 54a91f5..893874f 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -50,7 +50,7 @@ pub const ALL_KEYWORDS : [(&'static str, u8); 19] = [ // Extra data is opreator prescedence. Lower number is higher prescedence of operators // ordered by which to prefer when parsing -pub const ALL_SYMBOLS : [(&'static str, u8); 33] = [ +pub const ALL_SYMBOLS : [(&'static str, u8); 34] = [ // 'Meta symbols', for comments. Not actually used in further parsing ("/*", 0), ("//", 0), @@ -86,7 +86,8 @@ pub const ALL_SYMBOLS : [(&'static str, u8); 33] = [ ("]", 0), (",", 0), (";", 0), - (":", 0) + (":", 0), + ("'", 0) ]; pub const TOKEN_IDENTIFIER : TokenTypeIdx = (ALL_KEYWORDS.len() + ALL_SYMBOLS.len()) as TokenTypeIdx;