diff options
Diffstat (limited to 'src/parsers/assembler.rs')
-rw-r--r-- | src/parsers/assembler.rs | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/src/parsers/assembler.rs b/src/parsers/assembler.rs new file mode 100644 index 0000000..eb180e3 --- /dev/null +++ b/src/parsers/assembler.rs @@ -0,0 +1,282 @@ +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<AssembledToken> { + 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<String, MacroDefinition>, + pub label_definitions: &'a IndexMap<String, LabelDefinition>, + pub arguments: &'a IndexMap<String, Argument>, + 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<AssembledToken> { + 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<IntegerArgument, AssemblerError> { + 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<Argument, AssemblerError> { + 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<Argument, AssemblerError> { + 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); + IntegerArgument::LabelReference(name) + } + }; + 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<Argument, AssemblerError> { + 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); + Ok(Argument::Integer(IntegerArgument::LabelReference(name))) + } else { + err!(ErrVar::DefinitionNotFound(invocation.name.to_string())) + } + } + + fn reify_constant_expression(&self, expr: &Expression) -> Result<AssembledExpression, AssemblerError> { + 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)) + }, + } + } + ExprVar::Error(_) => continue, + }; + assembled_tokens.push(assembled_token); + } + Ok(AssembledExpression { source: expr.source.clone(), tokens: assembled_tokens }) + } +} + |