diff options
Diffstat (limited to 'src/stages')
-rw-r--r-- | src/stages/bytecode.rs | 272 | ||||
-rw-r--r-- | src/stages/bytecode_tokens.rs | 84 | ||||
-rw-r--r-- | src/stages/compiler.rs | 2 | ||||
-rw-r--r-- | src/stages/intermediate.rs | 626 | ||||
-rw-r--r-- | src/stages/intermediate_tokens.rs | 100 | ||||
-rw-r--r-- | src/stages/mod.rs | 4 | ||||
-rw-r--r-- | src/stages/semantic.rs | 72 | ||||
-rw-r--r-- | src/stages/semantic_tokens.rs | 14 |
8 files changed, 387 insertions, 787 deletions
diff --git a/src/stages/bytecode.rs b/src/stages/bytecode.rs deleted file mode 100644 index 71f1ff0..0000000 --- a/src/stages/bytecode.rs +++ /dev/null @@ -1,272 +0,0 @@ -use crate::*; - -use std::collections::HashMap; - - -pub fn parse_bytecode(tokens: Vec<Tracked<IntermediateToken>>, width: Option<u32>) -> Result<Vec<Segment>, Vec<Tracked<BytecodeError>>> { - BytecodeParser::new(width).parse(tokens) -} - - -pub struct BytecodeParser { - width: Option<u32>, - addresses: HashMap<String, Tracked<usize>>, - address: usize, - segment_address: usize, - segment_source: Option<SourceSpan>, - segments: Vec<Segment>, - words: Vec<Tracked<Word>>, - errors: Vec<Tracked<BytecodeError>>, -} - -impl BytecodeParser { - pub fn new(width: Option<u32>) -> Self { - Self { - width, - addresses: HashMap::new(), - address: 0, - segment_address: 0, - segment_source: None, - segments: Vec::new(), - words: Vec::new(), - errors: Vec::new(), - } - } - - pub fn parse(mut self, tokens: Vec<Tracked<IntermediateToken>>) -> Result<Vec<Segment>, Vec<Tracked<BytecodeError>>> { - // Register all labels with address 0. - for token in &tokens { - if let IntermediateToken::LabelDefinition(name) = &token.value { - let tracked = Tracked::from(0, token.source.clone()); - if let Some(_) = self.addresses.insert(name.clone(), tracked) { - unreachable!("Uncaught duplicate label definition '{name}'"); - } - } - } - // Attempt to calculate all label addresses naively ahead of time. - // This will give false results if we pin an address calculated from a label address. - let mut address = 0; - for token in &tokens { - let source = &token.source; - match &token.value { - IntermediateToken::LabelDefinition(name) => { - let tracked = Tracked::from(address, source.clone()); - self.addresses.insert(name.clone(), tracked); - } - IntermediateToken::Word(_) => { - address += 1; - } - IntermediateToken::PinnedAddress(pinned) => { - // Attempt to calculate a sane initial value for a pinned address. - match &pinned.value { - IntermediateInteger::Integer(value) => { - address = (*value).try_into().unwrap_or(0); - } - IntermediateInteger::Expression(expression) => { - let result = self.evaluate_expression(&expression, &pinned.source); - address = result.try_into().unwrap_or(0); - } - IntermediateInteger::LabelReference(_) => { - let error = BytecodeError::PinnedLabel; - self.errors.push(Tracked::from(error, source.clone())); - } - } - } - } - } - // Return unrecoverable errors. - if !self.errors.is_empty() { - return Err(self.errors); - } - - for i in 0..4 { - info!("Attempting iteration {} of bytecode assembly stage", i+1); - // Erase the previous parse attempt. - self.segments.clear(); - self.errors.clear(); - // Attempt to parse the program. - let previous_addresses = self.addresses.clone(); - self.parse_iteration(&tokens); - // Return unrecoverable errors. - if !self.errors.is_empty() { - return Err(self.errors); - } - // Check label stability - if self.check_for_instability(&previous_addresses) { - continue; - } - // Check for backtrack - if self.check_for_backtrack() { - continue; - }; - // Program is stable, return. - info!("Stabilised in {} iteration of bytecode assembly stage", i+1); - return Ok(self.segments); - } - - return Err(self.errors); - } - - /// Attempt to parse the full program using the current label values. - fn parse_iteration(&mut self, tokens: &[Tracked<IntermediateToken>]) { - for token in tokens { - let source = &token.source; - match &token.value { - IntermediateToken::Word(word) => { - let word = self.evaluate_word(word, source); - // Check that the word width fits the provided width. - if let Some(width) = self.width { - if word.width != width { - let error = BytecodeError::IncorrectWidth(width, word.width); - self.errors.push(Tracked::from(error, source.clone())); - } - } - self.words.push(word); - self.address += 1; - } - IntermediateToken::PinnedAddress(integer) => { - // Calculate the address of the new segment. - let pinned = match &integer.value { - IntermediateInteger::Integer(value) => { - (*value).try_into().unwrap_or(0) - } - IntermediateInteger::Expression(expression) => { - let result = self.evaluate_expression(&expression, &integer.source); - result.try_into().unwrap_or(0) - } - IntermediateInteger::LabelReference(_) => - // Already handled when registering initial label values. - unreachable!(), - }; - // Start a new segment. - let words = std::mem::take(&mut self.words); - if !words.is_empty() { - let address = self.segment_address; - let source = std::mem::take(&mut self.segment_source); - let segment = Segment { address, source, words }; - self.segments.push(segment); - } - self.segment_source = Some(integer.source.clone()); - self.address = pinned; - self.segment_address = pinned; - } - IntermediateToken::LabelDefinition(name) => { - // Record the latest known address of this label. - let address = self.addresses.get_mut(name).unwrap(); - address.value = self.address; - } - } - } - // Finish final segment. - let words = std::mem::take(&mut self.words); - if !words.is_empty() { - let address = self.segment_address; - let source = std::mem::take(&mut self.segment_source); - let segment = Segment { address, source, words }; - self.segments.push(segment); - } - } - - fn evaluate_expression(&mut self, expression: &IntermediateExpression, source: &SourceSpan) -> isize { - let mut stack = ExpressionStack::new(); - for token in &expression.tokens { - let source = &token.source; - match &token.value { - IntermediateExpressionToken::Integer(integer) => match integer { - IntermediateInteger::Integer(value) => { - stack.push(*value); - } - IntermediateInteger::Expression(expression) => { - stack.push(self.evaluate_expression(expression, source)); - } - IntermediateInteger::LabelReference(name) => { - stack.push(self.evaluate_label_reference(name)); - } - } - IntermediateExpressionToken::Operator(operator) => { - if let Err(err) = stack.apply(*operator, source) { - let error = BytecodeError::StackError(err); - self.errors.push(Tracked::from(error, source.clone())) - } - } - } - } - match stack.pull_result() { - Ok(value) => value, - Err(err) => { - let error = BytecodeError::StackError(Tracked::from(err, source.clone())); - self.errors.push(Tracked::from(error, source.clone())); - 0 - } - } - } - - fn evaluate_label_reference(&mut self, name: &Tracked<String>) -> isize { - if let Some(address) = self.addresses.get(&name.to_string()) { - address.value as isize - } else { - unreachable!("Uncaught unresolved label reference '{name}'") - } - } - - fn evaluate_word(&mut self, word: &IntermediateWord, source: &SourceSpan) -> Tracked<Word> { - let mut word_value = word.value; - for field in &word.fields { - let field_source = &field.value.value.source; - let field_value = match &field.value.value.value { - IntermediateInteger::Expression(expression) => { - self.evaluate_expression(expression, source) - } - IntermediateInteger::LabelReference(name) => { - self.evaluate_label_reference(name) - } - IntermediateInteger::Integer(value) => { - *value - } - }; - let value_width = width(field_value); - if field.width < value_width { - let error = BytecodeError::ValueTooWide(field.width, value_width); - self.errors.push(Tracked::from(error, field_source.clone())); - } else { - let mask = 2_usize.pow(field.width as u32) - 1; - let clamped_value = (field_value as usize) & mask; - word_value |= (clamped_value << field.shift) as usize; - } - } - let word = Word { width: word.width, value: word_value }; - return Tracked::from(word, source.clone()); - } - - fn check_for_instability(&mut self, previous_addresses: &HashMap<String, Tracked<usize>>) -> bool { - let mut instability_occurred = false; - for (name, previous_address) in previous_addresses.iter() { - let current_address = &self.addresses[name]; - if current_address != previous_address { - info!("Label '{name}' was unstable, moving from address 0x{:04x} to 0x{:04x}", - previous_address.value, current_address.value); - let error = BytecodeError::UnstableLabel(name.to_string()); - self.errors.push(Tracked::from(error, previous_address.source.clone())); - instability_occurred = true; - } - } - return instability_occurred; - } - - fn check_for_backtrack(&mut self) -> bool { - let mut backtrack_occurred = false; - let mut current_address = 0; - for segment in &self.segments { - if segment.address < current_address { - let error = BytecodeError::PinnedAddressBacktrack(segment.address, current_address); - if let Some(source) = &segment.source { - self.errors.push(Tracked::from(error, source.clone())); - } - info!("Backtrack occurred with segment at address 0x{:04x}", segment.address); - backtrack_occurred = true; - } - current_address = segment.address + segment.words.len(); - } - return backtrack_occurred; - } -} diff --git a/src/stages/bytecode_tokens.rs b/src/stages/bytecode_tokens.rs deleted file mode 100644 index 17b13b8..0000000 --- a/src/stages/bytecode_tokens.rs +++ /dev/null @@ -1,84 +0,0 @@ -use crate::*; - - -pub struct Segment { - pub address: usize, - /// Source of the address value. - pub source: Option<SourceSpan>, - pub words: Vec<Tracked<Word>>, -} - -pub struct Word { - pub value: usize, - pub width: u32, -} - -impl std::fmt::Display for Word { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - if self.width == 0 { - write!(f, "0") - } else { - for i in (0..self.width).rev() { - let is_first_bit = i+1 == self.width; - if !is_first_bit && (i+1) % 4 == 0 { - write!(f, "_")?; - } - match (self.value >> i) & 1 { - 0 => write!(f, "0")?, - _ => write!(f, "1")?, - } - } - Ok(()) - } - } -} - -pub enum BytecodeError { - /// expected, received - IncorrectWidth(u32, u32), - /// pinned, real - PinnedAddressBacktrack(usize, usize), - /// expected, received - ValueTooWide(u32, u32), - PinnedLabel, - UnstableLabel(String), - StackError(Tracked<StackError>), -} - - -pub fn report_bytecode_errors(errors: &[Tracked<BytecodeError>], source_code: &str) { - for error in errors { - report_bytecode_error(error, source_code); - } -} - -fn report_bytecode_error(error: &Tracked<BytecodeError>, source_code: &str) { - let context = Context { source_code: &source_code, source: &error.source }; - let message = match &error.value { - BytecodeError::IncorrectWidth(expected, received) => - &format!("Word is {received} bits wide, but was expected to have a fixed width of {expected} bits"), - BytecodeError::PinnedAddressBacktrack(pinned, real) => - &format!("Cannot pin to address {pinned} when address is already {real}"), - BytecodeError::PinnedLabel => - &format!("Cannot pin directly to a label"), - BytecodeError::UnstableLabel(name) => - &format!("Label '{name}' never stabilised"), - BytecodeError::StackError(stack_error) => { - report_stack_error(stack_error, source_code); return; }, - BytecodeError::ValueTooWide(expected, received) => - &format!("Field is {expected} bits wide, but received a value that is {received} bits wide"), - }; - - report_source_issue(LogLevel::Error, &context, message); -} - - -pub fn print_segment(segment: &Segment) { - println!("SEGMENT: 0x{:>04x}", segment.address); - // Find maximum width of all words in the segment. - let width = segment.words.iter().map(|w| w.to_string().chars().count()).max().unwrap_or(0); - for word in &segment.words { - let string = word.to_string(); - println!(" {string:>w$}", w=width as usize); - } -} diff --git a/src/stages/compiler.rs b/src/stages/compiler.rs index 1476ec8..b4680c2 100644 --- a/src/stages/compiler.rs +++ b/src/stages/compiler.rs @@ -16,10 +16,12 @@ pub fn parse_symbols(source_code: &str, path: Option<&Path>) -> Option<Vec<Symbo Ok(syntactic) => syntactic, Err(_errors) => return None, }; + let semantic = match parse_semantic(syntactic) { Ok(semantic) => semantic, Err(_errors) => return None, }; + // Convert symbols to the format required by the assembler library. let parsed = SymbolParser::new().parse(&semantic); let mut symbols = Vec::new(); diff --git a/src/stages/intermediate.rs b/src/stages/intermediate.rs index cfe33b7..7523baf 100644 --- a/src/stages/intermediate.rs +++ b/src/stages/intermediate.rs @@ -1,163 +1,170 @@ use crate::*; -use assembler::{DefinitionType, SourceLocation, SourcePosition, SymbolRole}; - use indexmap::{IndexSet, IndexMap}; -static mut ID: usize = 0; -macro_rules! next_id { () => { unsafe { let id = ID; ID += 1; id }}; } +static mut ENVIRONMENT_ID: usize = 0; +macro_rules! next_id { + () => { + unsafe { + let id = ENVIRONMENT_ID; + ENVIRONMENT_ID += 1; + id + } + }; +} + +pub struct LabelAddress { + pub previous: usize, + pub current: usize, + pub touched: bool, +} + +#[derive(Eq, Hash, PartialEq, Clone)] +pub struct SymbolSignature { + pub name: String, + pub arg_count: usize, +} + +impl std::fmt::Display for SymbolSignature { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(f, "{}::{}", self.name, self.arg_count) + } +} + +struct Environment { + arguments: IndexMap<String, Tracked<IntermediateValue>>, + id: usize, +} + pub fn parse_intermediate(semantic: Vec<Tracked<SemanticToken>>) -> Result<Vec<Tracked<IntermediateToken>>, Vec<Tracked<IntermediateError>>> { IntermediateParser::new(semantic).parse() } - struct IntermediateParser { semantic: Vec<Tracked<SemanticToken>>, - label_names: IndexSet<Tracked<String>>, - macro_names: IndexSet<Tracked<String>>, - macro_definitions: IndexMap<String, MacroDefinition>, + label_names: IndexSet<Tracked<SymbolSignature>>, + label_addresses: IndexMap<Tracked<String>, LabelAddress>, + macro_definitions: IndexMap<SymbolSignature, Tracked<MacroDefinition>>, + environment_stack: Vec<Environment>, + address: usize, intermediate: Vec<Tracked<IntermediateToken>>, errors: Vec<Tracked<IntermediateError>>, } impl IntermediateParser { - pub fn new(semantic: Vec<Tracked<SemanticToken>>) -> Self { + /// Initialise a new parser. + pub fn new(mut semantic: Vec<Tracked<SemanticToken>>) -> Self { + // Collect all label and macro definitions ahead of time. let mut label_names = IndexSet::new(); - let mut macro_names = IndexSet::new(); + let mut macro_definitions = IndexMap::new(); + // Get the signatures of all defined labels. for symbol in SymbolParser::new().parse(&semantic) { - match symbol.role { - SymbolRole::Definition(DefinitionType::MustPrecedeReference) => { - // Only consider macro definitions, not macro argument definitions. - if symbol.namespace.is_empty() { - if !macro_names.insert(Tracked::from(symbol.name.clone(), symbol.source)) { - unreachable!("Uncaught duplicate macro definition '{}'", symbol.name); - } - } - } - SymbolRole::Definition(DefinitionType::CanFollowReference) => { - if !label_names.insert(Tracked::from(symbol.name.clone(), symbol.source)) { - unreachable!("Uncaught duplicate label definition '{}'", symbol.name); - } + if let SymbolRoleDetailed::LabelDefinition = symbol.role { + let signature = SymbolSignature { name: symbol.name.clone(), arg_count: symbol.arg_count }; + if !label_names.insert(Tracked::from(signature.clone(), symbol.source)) { + unreachable!("Uncaught duplicate label definition '{signature}'"); } - SymbolRole::Reference => (), } } - + // Strip all semantic macro definitions from the semantic tokens. + let definitions = semantic.extract_if(.., + |t| if let SemanticToken::MacroDefinition(_) = t.value { true } else { false }); + for definition in definitions { + let source = definition.source; + let SemanticToken::MacroDefinition(definition) = definition.value else { unreachable!() }; + let name = definition.name.value.clone(); + let arg_count = definition.arguments.len(); + let signature = SymbolSignature { name, arg_count }; + if macro_definitions.insert(signature.clone(), Tracked::from(definition, source)).is_some() { + unreachable!("Uncaught duplicate macro definition '{signature}'") + } + } Self { semantic, label_names, - macro_names, - macro_definitions: IndexMap::new(), + label_addresses: IndexMap::new(), + macro_definitions, + environment_stack: Vec::new(), + address: 0, intermediate: Vec::new(), errors: Vec::new(), } } pub fn parse(mut self) -> Result<Vec<Tracked<IntermediateToken>>, Vec<Tracked<IntermediateError>>> { - for token in self.semantic { - let source = &token.source; - match token.value { - SemanticToken::MacroDefinition(definition) => { - // Invoke the body to see if it contains undefined macros. - let error_count = self.errors.len(); - let mut arguments = IndexMap::new(); - // Prepare dummy argument values. - let null = SourceSpan { - string: String::new(), - in_merged: SourceLocation { - path: None, - start: SourcePosition::ZERO, - end: SourcePosition::ZERO, - }, - in_source: None, - child: None, - }; - for argument in &definition.arguments { - let value = match argument.variant { - ArgumentType::Integer => { - let integer = IntermediateInteger::Integer(0); - let tracked = Tracked::from(integer, null.clone()); - IntermediateValue::Integer(tracked) - } - ArgumentType::Block => { - IntermediateValue::Block(Vec::new()) - } - ArgumentType::String => { - let string = String::new(); - let chars = Vec::new(); - let literal = StringLiteral { string, chars }; - let tracked = Tracked::from(literal, null.clone()); - IntermediateValue::String(tracked) - } - }; - let tracked = Tracked::from(value, null.clone()); - arguments.insert(argument.name.clone(), tracked); - } - // Register macro definition with empty body so that macro can invoke itself. - let name = definition.name.to_string(); - let dummy_definition = MacroDefinition { - name: definition.name, - arguments: definition.arguments, - body: MacroDefinitionBody::Block(Vec::new()), - }; - if self.macro_definitions.insert(name.clone(), dummy_definition).is_some() { - unreachable!("Uncaught duplicate macro definition '{}'", name); - } - let mut env = Environment { - label_names: &self.label_names, - macro_names: &self.macro_names, - macro_definitions: &self.macro_definitions, - arguments, - errors: &mut self.errors, - id: next_id!(), - }; - env.parse_macro_definition_body(&definition.body, source); - if self.errors.len() != error_count { - break; - } - // Replace dummy macro body with original macro body. - if let Some(registered) = self.macro_definitions.get_mut(&name) { - registered.body = definition.body; - } - } - SemanticToken::BlockToken(block_token) => { - let mut env = Environment { - label_names: &self.label_names, - macro_names: &self.macro_names, - macro_definitions: &self.macro_definitions, - arguments: IndexMap::new(), - errors: &mut self.errors, - id: next_id!(), - }; - let mut tokens = env.parse_block_token(&block_token, source); - self.intermediate.append(&mut tokens); - } + let semantic = std::mem::take(&mut self.semantic); + + for i in 0..MAX_ITERATIONS_TO_STABILISE { + info!("Attempting iteration {} of intermediate assembly stage", i+1); + // Erase the previous parse attempt. + self.address = 0; + self.intermediate.clear(); + self.errors.clear(); + self.environment_stack.clear(); + unsafe { ENVIRONMENT_ID = 0; } + // Update label addresses. + for (_, address) in &mut self.label_addresses { + address.previous = address.current; + address.touched = false; } + // Attempt to parse the program (which is now all block tokens). + for token in &semantic { + let source = &token.source; + let SemanticToken::BlockToken(ref block_token) = token.value else { unreachable!() }; + let env = Environment { arguments: IndexMap::new(), id: next_id!() }; + self.environment_stack.push(env); + let mut tokens = self.parse_block_token(&block_token, source); + self.intermediate.append(&mut tokens); + } + // Return unrecoverable errors. + if !self.errors.is_empty() { + return Err(self.errors); + } + // Check label stability + if self.check_for_instability(false) { + continue; + } + // Program is stable, return. + info!("Stabilised in iteration {} of intermediate assembly stage", i+1); + return Ok(self.intermediate); } - match self.errors.is_empty() { - true => Ok(self.intermediate), - false => Err(self.errors), - } + + self.check_for_instability(true); + return Err(self.errors); } -} + /// Check if any label is still stabilising. + fn check_for_instability(&mut self, create_error: bool) -> bool { + for (name, address) in &self.label_addresses { + if address.touched && address.current != address.previous { + info!("Label '{name}' was unstable, moving from address 0x{:04x} to 0x{:04x}", + address.previous, address.current); + if create_error { + let error = IntermediateError::LabelNeverStabilised(name.to_string()); + self.errors.push(Tracked::from(error, name.source.clone())); + } + return true; + } + } + return false; + } -struct Environment<'a> { - label_names: &'a IndexSet<Tracked<String>>, - macro_names: &'a IndexSet<Tracked<String>>, - macro_definitions: &'a IndexMap<String, MacroDefinition>, - arguments: IndexMap<String, Tracked<IntermediateValue>>, - errors: &'a mut Vec<Tracked<IntermediateError>>, - id: usize, -} + /// Get the current environment (the environment at the top of the stack). + fn env(&self) -> &Environment { + self.environment_stack.last().unwrap_or_else(|| + unreachable!("No environment on the stack")) + } -impl<'a> Environment<'a> { - // Attach the invocation ID to every macro label name + // Attach the environment ID to a local label name. fn tag_name(&self, name: &str) -> String { + // If a local label belongs to a macro, the name of that macro + // has been prefixed with the local label name in the + // resolve_label_name method during the semantic parsing stage, + // using a ':' character as a separator. match name.contains(':') { - true => format!("{name}:{}", self.id), + true => format!("{name}:{}", self.env().id), false => name.to_string(), } } @@ -166,7 +173,7 @@ impl<'a> Environment<'a> { match &body { MacroDefinitionBody::Integer(integer) => { let token = self.parse_integer_token(&integer, &source)?; - let integer = IntermediateValue::Integer(token); + let integer = IntermediateValue::Integer(token.value); Some(Tracked::from(integer, source.clone())) } MacroDefinitionBody::Invocation(invocation) => { @@ -180,9 +187,9 @@ impl<'a> Environment<'a> { let value = IntermediateValue::Block(tokens); Some(Tracked::from(value, source.clone())) } - MacroDefinitionBody::String(string) => { - let string = self.parse_string_token(string, &source)?; - let integer = IntermediateValue::String(string); + MacroDefinitionBody::List(list) => { + let list = self.parse_list_token(list, &source)?; + let integer = IntermediateValue::List(list.value); Some(Tracked::from(integer, source.clone())) } } @@ -192,60 +199,77 @@ impl<'a> Environment<'a> { let mut intermediate = Vec::new(); match block { BlockToken::LabelDefinition(name) => { - let token = IntermediateToken::LabelDefinition(self.tag_name(name)); - intermediate.push(Tracked::from(token, source.clone())); + let signature = SymbolSignature { name: name.to_string(), arg_count: 0 }; + if !self.label_names.contains(&signature) { + unreachable!("Unrecognised name for label definition"); + } + let tagged_name = self.tag_name(name); + let tracked = Tracked::from(tagged_name.clone(), source.clone()); + self.label_addresses.entry(tracked) + .and_modify(|a| { + if a.touched { unreachable!("Label '{tagged_name}' was already touched during this cycle.") } + a.previous = a.current; + a.current = self.address; + a.touched = true; + }) + .or_insert(LabelAddress { + previous: 0, + current: self.address, + touched: true, + }); } - BlockToken::PinnedAddress(address) => { - if let Some(integer) = self.parse_integer_token(address, &address.source) { - let token = IntermediateToken::PinnedAddress(integer); + BlockToken::PinnedAddress(integer) => { + if let Some(pinned) = self.parse_integer_token(integer, &integer.source) { + let pinned = **pinned as usize; + if pinned < self.address { + let error = IntermediateError::PinnedAddressBacktrack(pinned, self.address); + self.errors.push(Tracked::from(error, source.clone())); + } + self.address = pinned; + let token = IntermediateToken::PinnedAddress(pinned); intermediate.push(Tracked::from(token, source.clone())); } } BlockToken::ConditionalBlock(cond) => { if let Some(predicate) = self.parse_integer_token(&cond.predicate, &cond.predicate.source) { - if let Some(source) = integer_contains_label_reference(&predicate) { - let error = IntermediateError::LabelReferenceInConditionPredicate; - let new_source = cond.predicate.source.clone().wrap(source); - self.errors.push(Tracked::from(error, new_source)); - } else { - match evaluate_integer(&predicate, &cond.predicate.source) { - Ok(value) => if value != 0 { - let mut body = self.parse_block_token(&cond.body, &cond.body.source); - if let Some(source) = block_contains_label_definition(&cond.body, &cond.body.source) { - let error = IntermediateError::LabelDefinitionInConditionBody; - let new_source = cond.body.source.clone().wrap(source); - self.errors.push(Tracked::from(error, new_source)); - } else { - intermediate.append(&mut body); - } - }, - Err(error) => self.errors.push(error), + if **predicate != 0 { + if let Some(label_source) = block_contains_label_definition(&cond.body, &cond.body.source) { + let error = IntermediateError::LabelDefinitionInConditionBody; + let new_source = cond.body.source.clone().wrap(label_source); + self.errors.push(Tracked::from(error, new_source)); + } else { + let mut body = self.parse_block_token(&cond.body, &cond.body.source); + intermediate.append(&mut body); } - }; + } } } BlockToken::WordTemplate(word_template) => { - let mut fields = Vec::new(); - for bit_field in &word_template.fields { - let name = bit_field.name.to_string(); - let source = &bit_field.source; + let word_width = word_template.width; + let mut word_value = word_template.value; + for field in &word_template.fields { + let name = field.name.to_string(); + let field_source = &field.source; let invocation = Invocation { name, arguments: Vec::new() }; - if let Some(value) = self.parse_integer_invocation(&invocation, source) { - let field = IntermediateField { - width: bit_field.width, - shift: bit_field.shift, - value, - }; - fields.push(Tracked::from(field, bit_field.source.clone())); + if let Some(result) = self.parse_integer_invocation(&invocation, field_source) { + let field_value = **result; + // Inject field value into real value. + let value_width = width(field_value); + if field.width < value_width { + let error = IntermediateError::ValueTooWide(field.width, value_width); + let new_source = field_source.wrap(result.source); + self.errors.push(Tracked::from(error, new_source)); + } else { + let mask = 2_usize.pow(field.width as u32) - 1; + let clamped_value = (field_value as usize) & mask; + word_value |= (clamped_value << field.shift) as usize; + } } } - let word = IntermediateWord { - value: word_template.value, - width: word_template.width, - fields, - }; + let word = IntermediateWord { width: word_width, value: word_value }; let token = IntermediateToken::Word(word); intermediate.push(Tracked::from(token, source.clone())); + self.address += 1; } BlockToken::Block(blocks) => { for block in blocks { @@ -259,15 +283,14 @@ impl<'a> Environment<'a> { } } } - return intermediate; } fn parse_integer_token(&mut self, integer: &IntegerToken, source: &SourceSpan) -> Option<Tracked<IntermediateInteger>> { match integer { IntegerToken::IntegerLiteral(value) => { - let integer = IntermediateInteger::Integer(*value); - Some(Tracked::from(integer, source.clone())) + let tracked = Tracked::from(*value, source.clone()); + Some(Tracked::from(tracked, source.clone())) } IntegerToken::Expression(expression) => { self.parse_expression(expression, source) @@ -278,45 +301,61 @@ impl<'a> Environment<'a> { } } - fn parse_string_token(&mut self, string: &StringToken, source: &SourceSpan) -> Option<Tracked<StringLiteral>> { - match string { - StringToken::StringLiteral(literal) => { - Some(Tracked::from(literal.clone(), source.clone())) + fn parse_list_token(&mut self, list: &ListToken, source: &SourceSpan) -> Option<Tracked<IntermediateList>> { + match list { + ListToken::StringLiteral(literal) => { + Some(Tracked::from(literal.chars.clone(), source.clone())) } - StringToken::Invocation(invocation) => { - self.parse_string_invocation(&invocation, source) + ListToken::ListLiteral(literal) => { + let mut integers = Vec::new(); + for token in literal { + let integer = self.parse_integer_token(&token.value, &token.source)?; + integers.push(integer.value); + } + Some(Tracked::from(integers, source.clone())) + } + ListToken::Invocation(invocation) => { + self.parse_list_invocation(&invocation, source) } } } fn parse_integer_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Tracked<IntermediateInteger>> { - match self.parse_invocation(invocation, source)?.value { - IntermediateValue::Integer(integer) => Some(integer), - IntermediateValue::Block(_) | IntermediateValue::String(_) => { + let result = self.parse_invocation(invocation, source)?; + match result.value { + IntermediateValue::Integer(integer) => { + let source = integer.source.clone(); + Some(Tracked::from(integer, source)) + } + IntermediateValue::Block(_) | IntermediateValue::List(_) => { let error = IntermediateError::ExpectedInteger; - self.errors.push(Tracked::from(error, source.clone())); + self.errors.push(Tracked::from(error, result.source)); None } } } fn parse_block_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Vec<Tracked<IntermediateToken>>> { - match self.parse_invocation(invocation, source)?.value { + let result = self.parse_invocation(invocation, source)?; + let source = result.source; + match result.value { IntermediateValue::Block(tokens) => Some(tokens), - IntermediateValue::Integer(_) | IntermediateValue::String(_) => { + IntermediateValue::Integer(_) | IntermediateValue::List(_) => { let error = IntermediateError::ExpectedBlock; - self.errors.push(Tracked::from(error, source.clone())); + self.errors.push(Tracked::from(error, source)); None } } } - fn parse_string_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Tracked<StringLiteral>> { - match self.parse_invocation(invocation, source)?.value { - IntermediateValue::String(literal) => Some(literal), + fn parse_list_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Tracked<IntermediateList>> { + let result = self.parse_invocation(invocation, source)?; + let source = result.source; + match result.value { + IntermediateValue::List(literal) => Some(Tracked::from(literal, source)), IntermediateValue::Integer(_) | IntermediateValue::Block(_) => { - let error = IntermediateError::ExpectedString; - self.errors.push(Tracked::from(error, source.clone())); + let error = IntermediateError::ExpectedList; + self.errors.push(Tracked::from(error, source)); None } } @@ -324,8 +363,13 @@ impl<'a> Environment<'a> { fn parse_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Tracked<IntermediateValue>> { let received_count = invocation.arguments.len(); - if let Some(argument) = self.arguments.get(&invocation.name) { - // This invocation is a macro argument + let signature = SymbolSignature { + name: invocation.name.clone(), + arg_count: invocation.arguments.len(), + }; + if let Some(argument) = self.env().arguments.get(&invocation.name) { + // This invocation is a macro argument. + // Check that no arguments were provided. if received_count != 0 { let error = IntermediateError::IncorrectArgumentCount(0, received_count); self.errors.push(Tracked::from(error, source.clone())); @@ -333,21 +377,24 @@ impl<'a> Environment<'a> { } else { Some(argument.clone()) } - } else if let Some(label_name) = self.label_names.get(&invocation.name) { - // This invocation is a label reference + } else if let Some(label) = self.label_names.get(&signature) { + // This invocation is a label reference. + // Check that no arguments were provided. if received_count != 0 { let error = IntermediateError::IncorrectArgumentCount(0, received_count); self.errors.push(Tracked::from(error, source.clone())); None } else { - let name = self.tag_name(label_name); - let tracked = Tracked::from(name, label_name.source.clone()); - let integer = IntermediateInteger::LabelReference(tracked); - let tracked = Tracked::from(integer, source.clone()); - let value = IntermediateValue::Integer(tracked); - Some(Tracked::from(value, source.clone())) + let tagged_name = self.tag_name(&signature.name); + let address = self.label_addresses.get(&tagged_name) + .and_then(|a| Some(a.current)).or(Some(0)).unwrap(); + let tracked = Tracked::from(address as isize, label.source.clone()); + let integer = IntermediateValue::Integer(tracked); + Some(Tracked::from(integer, source.clone())) } - } else if let Some(definition) = self.macro_definitions.get(&invocation.name) { + } else if let Some(definition) = self.macro_definitions.get(&signature) { + // This invocation is a macro reference. + let definition = definition.clone(); // Check that the correct number of arguments were provided. let expected_count = definition.arguments.len(); if received_count != expected_count { @@ -363,9 +410,9 @@ impl<'a> Environment<'a> { arguments.push(received_value); } // Invoke the invocation multiple times. - let repetitions = arguments.iter().map(|a| a.len()).max().unwrap_or(1); + let invocations = arguments.iter().map(|a| a.len()).max().unwrap_or(1); let mut values = Vec::new(); - for i in 0..repetitions { + for i in 0..invocations { // Construct an argument map for this invocation. let mut argument_map = IndexMap::new(); for (a, argument) in arguments.iter().enumerate() { @@ -392,16 +439,11 @@ impl<'a> Environment<'a> { }; } // Invoke the macro once. - let mut env = Environment { - label_names: &self.label_names, - macro_names: &self.macro_names, - macro_definitions: &self.macro_definitions, - arguments: argument_map, - errors: &mut self.errors, - id: next_id!(), - }; - // Save the result of this macro invocation. - values.push(env.parse_macro_definition_body(&definition.body, source)?); + let env = Environment { arguments: argument_map, id: next_id!() }; + self.environment_stack.push(env); + let result = self.parse_macro_definition_body(&definition.body, source); + self.environment_stack.pop().unwrap(); + values.push(result?); } if values.len() == 1 { // If the macro was invoked once, return the value. @@ -412,7 +454,7 @@ impl<'a> Environment<'a> { let mut block = Vec::new(); for value in values { match value.value { - IntermediateValue::Integer(_) | IntermediateValue::String(_) => { + IntermediateValue::Integer(_) | IntermediateValue::List(_) => { let error = IntermediateError::ExpectedBlock; self.errors.push(Tracked::from(error, value.source)); return None; @@ -425,26 +467,21 @@ impl<'a> Environment<'a> { Some(Tracked::from(IntermediateValue::Block(block), source.clone())) } } - } else if let Some(macro_name) = self.macro_names.get(&invocation.name) { - let error = IntermediateError::InvocationBeforeDefinition; - let source = source.clone().wrap(macro_name.source.clone()); - self.errors.push(Tracked::from(error, source)); - None } else { - unreachable!("Uncaught unresolved reference '{}'", invocation.name); + unreachable!("Uncaught unresolved reference '{signature}'"); } } fn parse_invocation_argument(&mut self, argument: &Tracked<InvocationArgument>, expected_type: ArgumentType) -> Option<RepeatedArgument> { let source = &argument.source; let (received_value, received_type) = match &argument.value { - InvocationArgument::StringToken(string) => { - let string = self.parse_string_token(string, source)?; - self.parse_invocation_string_argument(string, expected_type)? + InvocationArgument::ListToken(list) => { + let list = self.parse_list_token(list, source)?; + self.parse_invocation_list_argument(list, expected_type)? } InvocationArgument::IntegerToken(integer) => { let tracked = self.parse_integer_token(&integer, &argument.source)?; - let value = IntermediateValue::Integer(tracked); + let value = IntermediateValue::Integer(tracked.value); (RepeatedArgument::Loop(value), ArgumentType::Integer) } InvocationArgument::BlockToken(block) => { @@ -459,8 +496,8 @@ impl<'a> Environment<'a> { (RepeatedArgument::Loop(value.value), ArgumentType::Integer), IntermediateValue::Block(_) => (RepeatedArgument::Loop(value.value), ArgumentType::Block), - IntermediateValue::String(string) => - self.parse_invocation_string_argument(string, expected_type)? + IntermediateValue::List(list) => + self.parse_invocation_list_argument(Tracked::from(list, value.source), expected_type)? } } }; @@ -472,100 +509,93 @@ impl<'a> Environment<'a> { return Some(received_value); } - fn parse_invocation_string_argument(&mut self, string: Tracked<StringLiteral>, expected_type: ArgumentType) -> Option<(RepeatedArgument, ArgumentType)> { + fn parse_invocation_list_argument(&mut self, list: Tracked<IntermediateList>, expected_type: ArgumentType) -> Option<(RepeatedArgument, ArgumentType)> { if let ArgumentType::Integer = expected_type { let mut values = Vec::new(); - for c in &string.chars { - let integer = IntermediateInteger::Integer(**c); - let tracked = Tracked::from(integer, c.source.clone()); - values.push(IntermediateValue::Integer(tracked)); + for value in &list.value { + values.push(IntermediateValue::Integer(value.clone())); } Some((RepeatedArgument::List(values), ArgumentType::Integer)) } else { - let value = IntermediateValue::String(string); - Some((RepeatedArgument::Loop(value), ArgumentType::String)) + let value = IntermediateValue::List(list.value); + Some((RepeatedArgument::Loop(value), ArgumentType::List)) } } fn parse_expression(&mut self, expression: &Expression, source: &SourceSpan) -> Option<Tracked<IntermediateInteger>> { - let mut intermediate = Vec::new(); - let mut error = false; - + let mut stack = ExpressionStack::new(); for token in &expression.tokens { let source = &token.source; match &token.value { - ExpressionToken::IntegerToken(integer) => { - let Some(integer) = self.parse_integer_token(integer, source) else { - error = true; continue; - }; - let token = IntermediateExpressionToken::Integer(integer.value); - intermediate.push(Tracked::from(token, integer.source)); + ExpressionToken::IntegerToken(token) => { + let integer = self.parse_integer_token(token, source)?; + stack.push(IntermediateValue::Integer(integer.value)); } - ExpressionToken::Operator(operator) => { - let token = IntermediateExpressionToken::Operator(*operator); - intermediate.push(Tracked::from(token, source.clone())); + ExpressionToken::ListToken(token) => { + let list = self.parse_list_token(token, source)?; + stack.push(IntermediateValue::List(list.value)); } ExpressionToken::Invocation(invocation) => { - let Some(integer) = self.parse_integer_invocation(invocation, source) else { - error = true; continue; - }; - let token = IntermediateExpressionToken::Integer(integer.value); - intermediate.push(Tracked::from(token, integer.source)); + if let Some(value) = self.parse_invocation(invocation, source) { + if let IntermediateValue::Block(_) = &value.value { + let error = IntermediateError::InvalidBlockInExpression; + let new_source = source.clone().wrap(value.source.clone()); + self.errors.push(Tracked::from(error, new_source)); + return None; + } + stack.push(value.value); + } + } + ExpressionToken::Operator(operator) => { + if let Err(expr_error) = stack.apply(*operator, source) { + let error = IntermediateError::ExpressionError(expr_error); + self.errors.push(Tracked::from(error, source.clone())); + return None; + } } } } - - if error { return None; } - let expression = IntermediateExpression { tokens: intermediate }; - let integer = IntermediateInteger::Expression(expression); - Some(Tracked::from(integer, source.clone())) + match stack.pull_result() { + Ok(value) => { + let tracked = Tracked::from(value, source.clone()); + Some(Tracked::from(tracked, source.clone())) + } + Err(expr_error) => { + let tracked = Tracked::from(expr_error, source.clone()); + let error = IntermediateError::ExpressionError(tracked); + self.errors.push(Tracked::from(error, source.clone())); + None + } + } } } + macro_rules! return_some { ($option:expr) => { if $option.is_some() { return $option; } }; } -fn integer_contains_label_reference(integer: &IntermediateInteger) -> Option<SourceSpan> { - match integer { - IntermediateInteger::Integer(_) => None, - IntermediateInteger::LabelReference(label) => Some(label.source.clone()), - IntermediateInteger::Expression(expr) => expression_contains_label_reference(expr), - } -} - -fn expression_contains_label_reference(expression: &IntermediateExpression) -> Option<SourceSpan> { - for token in &expression.tokens { - if let IntermediateExpressionToken::Integer(integer) = &token.value { - if let Some(child) = integer_contains_label_reference(&integer) { - return Some(token.source.clone().wrap(child)); - } - } - } - return None; -} +// Check if a block token contains a label definition. fn block_contains_label_definition(block: &BlockToken, source: &SourceSpan) -> Option<SourceSpan> { match &block { - BlockToken::LabelDefinition(_) => { - return Some(source.clone()); - } - BlockToken::Invocation(invocation) => { - return_some!(invocation_contains_label_definition(invocation)) - } - BlockToken::Block(blocks) => { + BlockToken::LabelDefinition(_) => + return Some(source.clone()), + BlockToken::Invocation(invocation) => + return_some!(invocation_contains_label_definition(invocation)), + BlockToken::Block(blocks) => for block in blocks { return_some!(block_contains_label_definition(block, &block.source)) - } - } + }, _ => (), } return None; } +// Check if the arguments passed to an invocation contain a label definition. fn invocation_contains_label_definition(invocation: &Invocation) -> Option<SourceSpan> { for argument in &invocation.arguments { match &argument.value { @@ -580,45 +610,3 @@ fn invocation_contains_label_definition(invocation: &Invocation) -> Option<Sourc } return None; } - -fn evaluate_integer(integer: &IntermediateInteger, source: &SourceSpan) -> Result<isize, Tracked<IntermediateError>> { - match integer { - IntermediateInteger::Integer(value) => Ok(*value), - IntermediateInteger::LabelReference(name) => - unreachable!("Uncaught label reference '{name}' in condition predicate or pinned address value"), - IntermediateInteger::Expression(expr) => evaluate_expression(expr, source), - } -} - -fn evaluate_expression(expression: &IntermediateExpression, source: &SourceSpan) -> Result<isize, Tracked<IntermediateError>> { - let mut stack = ExpressionStack::new(); - for token in &expression.tokens { - let source = &token.source; - match &token.value { - IntermediateExpressionToken::Integer(integer) => match integer { - IntermediateInteger::Integer(value) => { - stack.push(*value); - } - IntermediateInteger::Expression(expression) => { - stack.push(evaluate_expression(&expression, source)?); - } - IntermediateInteger::LabelReference(name) => { - unreachable!("Uncaught label reference '{name}' in condition predicate"); - } - } - IntermediateExpressionToken::Operator(operator) => { - if let Err(stack_error) = stack.apply(*operator, source) { - let error = IntermediateError::StackError(stack_error); - return Err(Tracked::from(error, token.source.clone())); - } - } - } - } - match stack.pull_result() { - Ok(value) => Ok(value), - Err(err) => { - let error = Tracked::from(err, source.clone()); - Err(Tracked::from(IntermediateError::StackError(error), source.clone())) - } - } -} diff --git a/src/stages/intermediate_tokens.rs b/src/stages/intermediate_tokens.rs index 8b7cfaf..71dbd62 100644 --- a/src/stages/intermediate_tokens.rs +++ b/src/stages/intermediate_tokens.rs @@ -4,8 +4,7 @@ use crate::*; #[derive(Clone)] pub enum IntermediateToken { Word(IntermediateWord), - PinnedAddress(Tracked<IntermediateInteger>), - LabelDefinition(String), + PinnedAddress(usize), } #[derive(Clone)] @@ -13,42 +12,18 @@ pub struct IntermediateWord { pub value: usize, /// Width of the word in bits. pub width: u32, - pub fields: Vec<Tracked<IntermediateField>>, } #[derive(Clone)] -pub struct IntermediateField { - pub value: Tracked<IntermediateInteger>, - /// Width of the field in bits. - pub width: u32, - /// Number of bits to the right of the field in the word. - pub shift: u32, -} - -#[derive(Clone)] -pub enum IntermediateInteger { - Integer(isize), - Expression(IntermediateExpression), - LabelReference(Tracked<String>), -} - -#[derive(Clone)] -pub struct IntermediateExpression { - pub tokens: Vec<Tracked<IntermediateExpressionToken>>, -} - -#[derive(Clone)] -pub enum IntermediateExpressionToken { +pub enum IntermediateValue { Integer(IntermediateInteger), - Operator(Operator), + List(IntermediateList), + Block(IntermediateBlock), } -#[derive(Clone)] -pub enum IntermediateValue { - Integer(Tracked<IntermediateInteger>), - Block(Vec<Tracked<IntermediateToken>>), - String(Tracked<StringLiteral>), -} +pub type IntermediateInteger = Tracked<isize>; +pub type IntermediateList = Vec<Tracked<isize>>; +pub type IntermediateBlock = Vec<Tracked<IntermediateToken>>; pub enum RepeatedArgument { Loop(IntermediateValue), @@ -67,17 +42,21 @@ impl RepeatedArgument { pub enum IntermediateError { ExpectedInteger, ExpectedBlock, - ExpectedString, + ExpectedList, ListExhausted, - LabelReferenceInConditionPredicate, LabelDefinitionInConditionBody, LabelReferenceInPinnedAddress, - StackError(Tracked<StackError>), - InvocationBeforeDefinition, + InvalidBlockInExpression, + ExpressionError(Tracked<ExpressionError>), /// expected, received IncorrectArgumentCount(usize, usize), /// expected, received IncorrectArgumentType(ArgumentType, ArgumentType), + /// pinned, real + PinnedAddressBacktrack(usize, usize), + /// expected, received + ValueTooWide(u32, u32), + LabelNeverStabilised(String), } pub fn report_intermediate_errors(errors: &[Tracked<IntermediateError>], source_code: &str) { @@ -93,24 +72,30 @@ fn report_intermediate_error(error: &Tracked<IntermediateError>, source_code: &s "An integer value was expected here", IntermediateError::ExpectedBlock => "A block value was expected here", - IntermediateError::ExpectedString => + IntermediateError::ExpectedList => "A string value was expected here", IntermediateError::ListExhausted => - "This string is shorter than another string passed to the same invocation", - IntermediateError::LabelReferenceInConditionPredicate => - "The predicate of a conditional block cannot contain a label reference", + "This list is shorter than another list passed to the same invocation", IntermediateError::LabelDefinitionInConditionBody => "The body of a conditional block cannot contain a label definition", IntermediateError::LabelReferenceInPinnedAddress => "The value of a pinned address cannot contain a label reference", - IntermediateError::StackError(stack_error) => { - report_stack_error(stack_error, source_code); return; }, - IntermediateError::InvocationBeforeDefinition => - &format!("Macro cannot be invoked before it has been defined"), + IntermediateError::InvalidBlockInExpression => + "Expression cannot contain an invocation that expands to a block value", + + IntermediateError::ExpressionError(expression_error) => { + report_expression_error(expression_error, source_code); return; }, IntermediateError::IncorrectArgumentCount(expected, received) => &format!("Expected {expected} arguments, but received {received} instead"), IntermediateError::IncorrectArgumentType(expected, received) => &format!("Expected {expected} value but received {received} value instead"), + IntermediateError::PinnedAddressBacktrack(pinned, real) => + &format!("Cannot pin to address {pinned} when address is already {real}"), + IntermediateError::ValueTooWide(expected, received) => + &format!("Field is {expected} bits wide, but received a value that is {received} bits wide"), + + IntermediateError::LabelNeverStabilised(name) => + &format!("Label '{name}' never stabilised"), }; report_source_issue(LogLevel::Error, &context, message); @@ -121,35 +106,10 @@ pub fn print_intermediate_token(i: usize, token: &IntermediateToken) { match token { IntermediateToken::Word(word) => { indent!(i, "Word({:>0w$b})", word.value, w = word.width as usize); - for field in &word.fields { - print_intermediate_integer(i+1, &field.value.value); - } } IntermediateToken::PinnedAddress(address) => { - indent!(i, "PinnedAddress"); - print_intermediate_integer(i+1, address); + indent!(i, "PinnedAddress({address})"); } - IntermediateToken::LabelDefinition(name) => - indent!(i, "LabelDefinition({name})"), } } -fn print_intermediate_integer(i: usize, integer: &IntermediateInteger) { - match integer { - IntermediateInteger::Integer(value) => - indent!(i, "Integer({value})"), - IntermediateInteger::LabelReference(name) => - indent!(i, "LabelReference({name})"), - IntermediateInteger::Expression(expression) => { - indent!(i, "Expression"); - for token in &expression.tokens { - match &token.value { - IntermediateExpressionToken::Integer(integer) => - print_intermediate_integer(i+1, integer), - IntermediateExpressionToken::Operator(operator) => - indent!(i+1, "Operator({operator})"), - } - } - } - } -} diff --git a/src/stages/mod.rs b/src/stages/mod.rs index 571fd65..bf0d5a6 100644 --- a/src/stages/mod.rs +++ b/src/stages/mod.rs @@ -5,8 +5,6 @@ mod semantic; mod semantic_tokens; mod intermediate; mod intermediate_tokens; -mod bytecode; -mod bytecode_tokens; pub use compiler::*; pub use syntactic::*; @@ -15,8 +13,6 @@ pub use semantic::*; pub use semantic_tokens::*; pub use intermediate::*; pub use intermediate_tokens::*; -pub use bytecode::*; -pub use bytecode_tokens::*; #[macro_export] diff --git a/src/stages/semantic.rs b/src/stages/semantic.rs index fdd1766..96cf7af 100644 --- a/src/stages/semantic.rs +++ b/src/stages/semantic.rs @@ -306,22 +306,44 @@ impl SemanticParser { } } - /// Parse the remaining syntactic tokens as the contents of a block. - fn parse_block(&mut self) -> Vec<Tracked<BlockToken>> { - let mut tokens = Vec::new(); - while !self.syntactic.is_empty() { - if let Some(token) = self.pull_block_token(SemanticLocation::BlockLiteral) { - tokens.push(token); + /// Attempt to pull a token that can be used in an expression. + fn pull_expression_token(&mut self) -> Option<Tracked<ExpressionToken>> { + match self.pull_macro_definition_body_token()? { + MacroDefinitionBody::Block(mut tokens) => { + assert_eq!(tokens.len(), 1); + let token = tokens.pop().unwrap(); + let error = SemanticError::InvalidBlockInExpression; + self.errors.push(Tracked::from(error, token.source)); + None + } + MacroDefinitionBody::Invocation(invocation) => { + // Attempt to parse the invocation as an operator. + if invocation.arguments.is_empty() { + if let Some(operator) = Operator::from_str(&invocation.name) { + let expr = ExpressionToken::Operator(operator); + return Some(Tracked::from(expr, invocation.source)) + } + } + // Parse invocation as an invocation. + let expr = ExpressionToken::Invocation(invocation.value); + Some(Tracked::from(expr, invocation.source)) + } + MacroDefinitionBody::Integer(integer) => { + let expr = ExpressionToken::IntegerToken(Box::new(integer.value)); + Some(Tracked::from(expr, integer.source)) + } + MacroDefinitionBody::List(list) => { + let expr = ExpressionToken::ListToken(list.value); + Some(Tracked::from(expr, list.source)) } } - tokens } - /// Parse the remaining syntactic tokens as a list of integer tokens. - fn parse_integer_list(&mut self, location: SemanticLocation) -> Vec<Tracked<IntegerToken>> { + /// Parse the remaining syntactic tokens as the contents of a block. + fn parse_block(&mut self) -> Vec<Tracked<BlockToken>> { let mut tokens = Vec::new(); while !self.syntactic.is_empty() { - if let Some(token) = self.pull_integer_token(location) { + if let Some(token) = self.pull_block_token(SemanticLocation::BlockLiteral) { tokens.push(token); } } @@ -331,34 +353,8 @@ impl SemanticParser { /// Parse the remaining syntactic tokens as the contents of an expression. fn parse_expression(&mut self) -> Expression { let mut tokens = Vec::new(); - for token in self.parse_integer_list(SemanticLocation::Expression) { - let source = token.source; - match token.value { - IntegerToken::IntegerLiteral(value) => { - let integer = Box::new(IntegerToken::IntegerLiteral(value)); - let token = ExpressionToken::IntegerToken(integer); - tokens.push(Tracked::from(token, source)); - } - IntegerToken::Expression(expression) => { - let integer = Box::new(IntegerToken::Expression(expression)); - let token = ExpressionToken::IntegerToken(integer); - tokens.push(Tracked::from(token, source)); - } - IntegerToken::Invocation(invocation) => { - // Parse the invocation as an operator instead. - if invocation.arguments.is_empty() { - if let Some(operator) = Operator::from_str(&invocation.name) { - let token = ExpressionToken::Operator(operator); - tokens.push(Tracked::from(token, source)); - continue; - } - } - // Parse the invocation as an invocation. - let integer = Box::new(IntegerToken::Invocation(invocation)); - let token = ExpressionToken::IntegerToken(integer); - tokens.push(Tracked::from(token, source)); - } - } + while let Some(token) = self.pull_expression_token() { + tokens.push(token); } Expression { tokens } } diff --git a/src/stages/semantic_tokens.rs b/src/stages/semantic_tokens.rs index a873df0..7aa6093 100644 --- a/src/stages/semantic_tokens.rs +++ b/src/stages/semantic_tokens.rs @@ -6,12 +6,14 @@ pub enum SemanticToken { BlockToken(BlockToken), } +#[derive(Clone)] pub struct MacroDefinition { pub name: Tracked<String>, pub arguments: Vec<Tracked<ArgumentDefinition>>, pub body: MacroDefinitionBody, } +#[derive(Clone)] pub struct ArgumentDefinition { pub name: String, pub variant: ArgumentType, @@ -34,6 +36,7 @@ impl std::fmt::Display for ArgumentType { } } +#[derive(Clone)] pub enum MacroDefinitionBody { Integer(Tracked<IntegerToken>), Block(Vec<Tracked<BlockToken>>), @@ -41,21 +44,25 @@ pub enum MacroDefinitionBody { Invocation(Tracked<Invocation>), } +#[derive(Clone)] pub struct ConditionalBlock { pub predicate: Tracked<IntegerToken>, pub body: Tracked<BlockToken>, } +#[derive(Clone)] pub enum IntegerToken { IntegerLiteral(isize), Expression(Expression), Invocation(Invocation), } +#[derive(Clone)] pub struct Expression { pub tokens: Vec<Tracked<ExpressionToken>>, } +#[derive(Clone)] pub enum ExpressionToken { IntegerToken(Box<IntegerToken>), ListToken(ListToken), @@ -63,6 +70,7 @@ pub enum ExpressionToken { Operator(Operator), } +#[derive(Clone)] pub enum BlockToken { LabelDefinition(String), PinnedAddress(Tracked<IntegerToken>), @@ -72,17 +80,20 @@ pub enum BlockToken { Invocation(Invocation), } +#[derive(Clone)] pub enum ListToken { StringLiteral(StringLiteral), ListLiteral(Vec<Tracked<IntegerToken>>), Invocation(Invocation), } +#[derive(Clone)] pub struct Invocation { pub name: String, pub arguments: Vec<Tracked<InvocationArgument>>, } +#[derive(Clone)] pub enum InvocationArgument { IntegerToken(IntegerToken), BlockToken(BlockToken), @@ -137,6 +148,7 @@ pub enum SemanticError { InvalidArgumentDefinition, InvalidInvocationArgument, + InvalidBlockInExpression, GlobalLabelInMacroDefinition, LocalLabelWithoutNamespace, @@ -202,6 +214,8 @@ fn report_semantic_error(error: &Tracked<SemanticError>, source_code: &str) { "Argument definition must take one of the following forms: name, {name}, \"name\", or [name]", SemanticError::InvalidInvocationArgument => "This token cannot be used in an invocation argument", + SemanticError::InvalidBlockInExpression => + "Expression cannot contain a block token", SemanticError::GlobalLabelInMacroDefinition => &format!("Macro definition cannot contain a global label"), |