use crate::*; use AssemblerErrorVariant as ErrVar; use indexmap::IndexMap; static mut ID: usize = 0; macro_rules! new_id { () => { unsafe { let id = ID; ID += 1; id }}; } impl SemanticProgram { pub fn assemble(&self) -> Vec { let environment = Environment { macro_definitions: &self.macro_definitions, label_definitions: &self.label_definitions, arguments: &IndexMap::new(), id: new_id!(), }; let mut assembled_tokens = Vec::new(); for token in &self.body { let tokens = environment.reify_semantic_token(token); assembled_tokens.extend(tokens); } return assembled_tokens; } } pub struct Environment<'a> { pub macro_definitions: &'a IndexMap, pub label_definitions: &'a IndexMap, pub arguments: &'a IndexMap, pub id: usize, } impl<'a> Environment<'a> { // This is only ever called for the highest level body tokens, never for invocations. fn reify_semantic_token(&self, token: &SemanticToken) -> Vec { let mut assembled_tokens = Vec::new(); match token { SemanticToken::Word(pbl) => { let word = self.reify_packed_binary_literal(pbl); assembled_tokens.push(AssembledToken::Word(word)); } SemanticToken::Invocation(invocation) => { match self.reify_invocation(invocation) { Ok(argument) => match argument { Argument::Block(block) => assembled_tokens.extend(block), Argument::Integer(_) => { let variant = AssemblerErrorVariant::NotABlock; let source = invocation.source.clone(); let error = AssemblerError { source, variant }; assembled_tokens.push(AssembledToken::Error(error)) } } Err(error) => assembled_tokens.push(AssembledToken::Error(error)), } } SemanticToken::LabelDefinition(definition) => { assembled_tokens.push(AssembledToken::LabelDefinition(definition.clone())); } SemanticToken::PinnedAddress(address) => { assembled_tokens.push(AssembledToken::PinnedAddress(address.clone())); } SemanticToken::Error(_) => (), } return assembled_tokens; } fn reify_packed_binary_literal(&self, pbl: &PackedBinaryLiteral) -> AssembledWord { let mut assembled_fields = Vec::new(); let mut errors = Vec::new(); for field in &pbl.fields { let name = field.name.to_string(); match self.reify_integer_reference(&name, &field.source) { Ok(value) => assembled_fields.push( AssembledField { source: field.source.clone(), value, bits: field.bits, shift: field.shift, } ), Err(error) => errors.push(error), }; } let source = pbl.source.clone(); let value = pbl.value; let bits = pbl.bits; AssembledWord { source, bits, fields: assembled_fields, value, errors } } fn reify_integer_reference(&self, name: &str, source: &SourceSpan) -> Result { match self.reify_reference(name, source)? { Argument::Integer(integer) => Ok(integer), Argument::Block(_) => Err( AssemblerError { source: source.clone(), variant: ErrVar::NotAnInteger, } ), } } fn reify_reference(&self, name: &str, source: &SourceSpan) -> Result { let source = source.clone(); if let Some(argument) = self.arguments.get(name) { Ok(argument.clone()) } else if let Some(definition) = self.macro_definitions.get(name) { self.reify_value(&definition.value) } else if let Some(label) = self.label_definitions.get(name) { let name = Tracked::from(self.tag_label_name(&label.name), source); Ok(Argument::Integer(IntegerArgument::LabelReference(name))) } else { let variant = ErrVar::DefinitionNotFound(name.to_string()); Err(AssemblerError { source, variant }) } } fn tag_label_name(&self, name: &str) -> String { match name.contains(':') { true => format!("{name}:{}", self.id), false => name.to_string(), } } fn reify_value(&self, value: &Value) -> Result { match value { Value::Integer(integer) => { let value = match &integer { Integer::Literal(integer) => { IntegerArgument::Integer(integer.clone()) } Integer::Expression(expr) => { let expr = self.reify_constant_expression(expr)?; IntegerArgument::Expression(expr) } Integer::LabelReference(name) => { let name = Tracked::from(self.tag_label_name(name), name.source.clone()); IntegerArgument::LabelReference(name) } Integer::String(string) => { IntegerArgument::String(string.clone()) } }; Ok(Argument::Integer(value)) } Value::Block(block) => { let mut assembled_tokens = Vec::new(); for token in block { match &token { SemanticToken::Word(pbl) => { let word = self.reify_packed_binary_literal(pbl); assembled_tokens.push(AssembledToken::Word(word)); } SemanticToken::Invocation(invocation) => { match self.reify_invocation(invocation)? { Argument::Block(block) => assembled_tokens.extend(block), Argument::Integer(_) => { let source = invocation.source.clone(); let variant = AssemblerErrorVariant::IntegerInBlock; return Err(AssemblerError { source, variant}); } } } SemanticToken::LabelDefinition(definition) => { let mut definition = definition.clone(); definition.name.push_str(&format!(":{}", self.id)); let token = AssembledToken::LabelDefinition(definition); assembled_tokens.push(token); } SemanticToken::PinnedAddress(address) => { let token = AssembledToken::PinnedAddress(address.to_owned()); assembled_tokens.push(token); } SemanticToken::Error(_) => (), } } Ok(Argument::Block(assembled_tokens)) } Value::Invocation(invocation) => { self.reify_invocation(invocation) } } } fn reify_invocation(&self, invocation: &Invocation) -> Result { macro_rules! err { ($variant:expr) => { Err(AssemblerError { source: invocation.source.clone(), variant: $variant }) }; } if let Some(argument) = self.arguments.get(&invocation.name) { let expected = 0; let received = invocation.arguments.len(); if received != expected { return err!(ErrVar::IncorrectArgumentCount(expected, received)); } Ok(argument.clone()) } else if let Some(definition) = self.macro_definitions.get(&invocation.name) { // Check that the correct number of arguments were provided. let received = invocation.arguments.len(); let expected = definition.arguments.len(); if received != expected { return err!(ErrVar::IncorrectArgumentCount(expected, received)); } let mut arguments = IndexMap::new(); for (i, argument) in invocation.arguments.iter().enumerate() { // Check that the correct types of arguments were provided. let arg_invocation = self.reify_value(&argument.value)?; let arg_invocation_type = match &arg_invocation { Argument::Integer(_) => ArgumentVariant::Integer, Argument::Block(_) => ArgumentVariant::Block, }; let arg_definition_type = definition.arguments[i].variant; if arg_invocation_type != arg_definition_type { let variant = ErrVar::IncorrectArgumentType( arg_definition_type, arg_invocation_type ); return Err(AssemblerError { source: argument.source.clone(), variant }); } let name = definition.arguments[i].name.clone(); arguments.insert(name, arg_invocation); } let environment = Environment { macro_definitions: &self.macro_definitions, label_definitions: &self.label_definitions, arguments: &arguments, id: new_id!(), }; environment.reify_value(&definition.value) } else if let Some(label) = self.label_definitions.get(&invocation.name) { let expected = 0; let received = invocation.arguments.len(); if received != expected { return err!(ErrVar::IncorrectArgumentCount(expected, received)); } let name = Tracked::from(self.tag_label_name(&label.name), label.source.clone()); Ok(Argument::Integer(IntegerArgument::LabelReference(name))) } else { err!(ErrVar::DefinitionNotFound(invocation.name.to_string())) } } fn reify_constant_expression(&self, expr: &Expression) -> Result { use ExpressionTokenVariant as ExprVar; let mut assembled_tokens = Vec::new(); for token in &expr.tokens { let assembled_token = match &token.variant { ExprVar::Literal(value) => { let source = token.source.clone(); let integer = TrackedInteger { source, value: *value }; AssembledExpressionToken::Integer(integer) } ExprVar::Operator(operator) => { AssembledExpressionToken::Operator(*operator) } ExprVar::Invocation(name) => { match self.reify_integer_reference(&name, &token.source)? { IntegerArgument::LabelReference(name) => { AssembledExpressionToken::LabelReference(name) } IntegerArgument::Integer(integer) => { AssembledExpressionToken::Integer(integer) } IntegerArgument::Expression(expr) => { AssembledExpressionToken::Expression(Box::new(expr)) }, IntegerArgument::String(string) => { let source = string.source.clone(); let variant = AssemblerErrorVariant::StringInExpression; return Err(AssemblerError { source, variant }) } } } ExprVar::Error(_) => continue, }; assembled_tokens.push(assembled_token); } Ok(AssembledExpression { source: expr.source.clone(), tokens: assembled_tokens }) } }