//! LALRPOP grammar for a modified version of SPC700 assembly.
//! Note that this grammar can not parse plain SPC700 assembly, as it is not LR(1).
use miette::{SourceOffset, SourceSpan};
use lalrpop_util::ParseError;
use parking_lot::RwLock;
use flexstr::{SharedStr, shared_str, IntoSharedStr, ToSharedStr};
use std::collections::HashMap;
use std::num::{NonZeroU64, NonZeroUsize};
use std::sync::{Arc, Weak};
use crate::sema::{
AddressingMode,
AssemblyTimeValue,
Environment,
instruction::{Instruction, Mnemonic, Opcode, MemoryAddress, AddressingModeOptimization},
LabelUsageKind,
ProgramElement,
reference::{Reference, MacroParent, RelativeReferenceDirection},
Register,
value::{UnaryOperator, BinaryOperator, Size, SizedAssemblyTimeValue},
};
use crate::parser::{
apply_brr_options,
apply_sample_table_options,
source_range,
Token,
try_make_direct_page_addressing_mode,
};
use crate::directive::{DirectiveSymbol, Directive, DirectiveValue, DirectiveParameter, FillOperation};
use crate::error::AssemblyError;
use crate::AssemblyCode;
grammar<'env, 'src>(environment: &'env Arc<RwLock<Environment>>, source_code: &'src Arc<AssemblyCode>);
pub Program: Vec<ProgramElement> = "\n"* <line:( <Line> )*> => line.into_iter().flatten().collect();
Line: Vec<ProgramElement> = <mut labels:MultipleLabelsForInstruction?> <element:InstructionOrDirective> <newlines:("\n"+)> =>
(*labels.get_or_insert_default()).clone().into_iter()
.map(ProgramElement::Label)
.chain(Some(element.extend_span(newlines[0].into()))).collect();
InstructionOrDirective: ProgramElement = {
InstructionWithTestComment => ProgramElement::Instruction(<>),
DirectiveWithTestComment => ProgramElement::Directive(<>),
<directive:IncludeDirective> <file:"string"> => ProgramElement::IncludeSource {
file: std::string::String::from_utf8_lossy(&file.0).as_ref().into(),
span: source_range(directive.into(), file.1.into()),
},
MacroCall => <>,
};
IncludeDirective: SourceSpan = {
"include",
"incsrc",
};
MacroCall: ProgramElement = <start:"%"> <macro_name:"identifier"> "(" <arguments:Comma<AssemblyTimeValue>?> <end:")"> => ProgramElement::UserDefinedMacroCall {
macro_name: macro_name.0,
arguments: arguments.unwrap_or(Vec::new()),
span: source_range(start.into(), end.into()),
};
InstructionWithTestComment: Instruction = <mut instruction:Instruction> <expected:";="?> => {
#[cfg(test)]
if let Some(expected) = expected {
instruction.expected_value = Some(expected.0);
instruction.span = source_range(instruction.span.into(), expected.1.into());
}
instruction
};
DirectiveWithTestComment: Directive = <mut directive:Directive> <expected:";="?> => {
#[cfg(test)]
if let Some(expected) = expected {
directive.expected_value = Some(expected.0);
directive.span = source_range(directive.span.into(), expected.1.into());
}
directive
};
Instruction: Instruction = {
<mnemonic:Mnemonic> <first_operand:AddressingMode> <second_operand:( "," <AddressingMode> )?> => Instruction {
opcode: Opcode {
mnemonic: mnemonic.0,
first_operand: Some(first_operand),
second_operand,
addressing_mode_optimization: mnemonic.1,
},
span: mnemonic.2,
..Instruction::default()
},
<mnemonic:Mnemonic> => Instruction {
opcode: Opcode {
mnemonic: mnemonic.0,
first_operand: None,
second_operand: None,
addressing_mode_optimization: mnemonic.1,
},
span: mnemonic.2,
..Instruction::default()
},
};
// The boolean specifies whether this instruction is forced to direct page addressing.
Mnemonic: (Mnemonic, AddressingModeOptimization, SourceSpan) = {
<mnemonic:"mnemonic"> => (mnemonic.0, AddressingModeOptimization::Automatic, mnemonic.1),
<mnemonic:"mnemonic"> <b:".b"> => (mnemonic.0, AddressingModeOptimization::ForceDirectPage, source_range(mnemonic.1.into(), b.into())),
<mnemonic:"mnemonic"> <b:".w"> => (mnemonic.0, AddressingModeOptimization::PreventDirectPage, source_range(mnemonic.1.into(), b.into())),
};
Directive: Directive = {
<data_directive:TableDirective> <numbers:AssemblyTimeValueList> => {
let size = match data_directive.0 {
DirectiveSymbol::Byte | DirectiveSymbol::Db => Size::Byte,
DirectiveSymbol::Dw | DirectiveSymbol::Word => Size::Word,
DirectiveSymbol::Dl => Size::Long,
DirectiveSymbol::Dd => Size::DWord,
_ => unreachable!(),
};
Directive {
value: DirectiveValue::Table {
values: numbers.into_iter().map(|value| SizedAssemblyTimeValue { value, size }).collect(),
},
span: data_directive.1,
expected_value: None,
}
},
<directive:"org"> <address:AssemblyTimeValue> =>? Ok(Directive {
value: DirectiveValue::Org(address.try_value(directive, source_code).map_err(|err| ParseError::User {
error: AssemblyError::ReferencesInDirectiveArgument {
directive: DirectiveSymbol::Org,
src: source_code.clone(),
location: directive,
argument_location: address.source_span(),
},
})?),
span: directive,
expected_value: None,
}),
<directive:"brr"> <filename:"string"> <range:InclusionRange?> <options:IdentifierOptions> =>? Ok(Directive {
value: apply_brr_options(source_range(directive.into(), filename.1.into()), source_code,
DirectiveValue::Brr {
file: std::string::String::from_utf8_lossy(&filename.0).as_ref().into(),
range,
auto_trim: false,
directory: true,
}, options)?,
span: source_range(directive.into(), filename.1.into()),
expected_value: None,
}),
<directive:"sampletable"> <options:IdentifierOptions> =>? Ok(Directive {
value: apply_sample_table_options(directive.into(), source_code,
DirectiveValue::SampleTable {
auto_align: true,
}, options)?,
span: directive.into(),
expected_value: None,
}),
<directive:"ascii"> <text:"string"> => Directive {
value: DirectiveValue::String { text: text.0, has_null_terminator: false },
span: source_range(directive.into(), text.1.into()),
expected_value: None,
},
<directive:"asciiz"> <text:"string"> => Directive {
value: DirectiveValue::String { text: text.0, has_null_terminator: true },
span: source_range(directive.into(), text.1.into()),
expected_value: None,
},
<directive:"incbin"> <text:"string"> <range:InclusionRange?> => Directive {
value: DirectiveValue::Include {
file: std::string::String::from_utf8_lossy(&text.0).as_ref().into(),
range,
},
span: source_range(directive.into(), text.1.into()),
expected_value: None,
},
"endasm" => Directive {
value: DirectiveValue::End,
span: <>,
expected_value: None,
},
"pushpc" => Directive {
value: DirectiveValue::PushSection,
span: <>,
expected_value: None,
},
"pullpc" => Directive {
value: DirectiveValue::PopSection,
span: <>,
expected_value: None,
},
<math:"math"> ("identifier")* =>? {
Err(AssemblyError::MathPriForbidden {
src: source_code.clone(),
location: math,
}).map_err(|error| ParseError::User { error })
},
ArchDirective,
<directive:"startpos"> => Directive {
value: DirectiveValue::Startpos,
span: directive,
expected_value: None,
},
FillAlignDirective,
FillAmountDirective,
PadDirective,
Conditional,
<value_directive:FillOrPadValueDirective> <fill_value:AssemblyTimeValue> => Directive {
value: DirectiveValue::SetDirectiveParameters ({
let mut parameters = HashMap::with_capacity(2);
let (value_parameter, size_parameter, size, span) = value_directive;
parameters.insert(value_parameter, fill_value);
parameters.insert(size_parameter, AssemblyTimeValue::Literal(size as MemoryAddress, span));
parameters
}),
span: value_directive.3,
expected_value: None,
},
LabelAssignment,
UserDefinedMacro,
<directive:"namespace"> <namespace_name_or_end:"identifier"> => {
let span = source_range(directive.into(), namespace_name_or_end.1.into());
Directive {
value: if namespace_name_or_end.0 == "end" {
DirectiveValue::EndNamespace
} else {
DirectiveValue::StartNamespace { name: namespace_name_or_end.0, }
},
span,
expected_value: None,
}
},
RepeatDirective,
};
RepeatDirective: Directive = <start:"repeat"> <value:AssemblyTimeValue> "\n" <body:Program> <end:"endrepeat"> => Directive {
value: DirectiveValue::Repeat {
count: value,
body,
},
span: source_range(start.into(), end.into()),
expected_value: None,
};
TableDirective: (DirectiveSymbol, SourceSpan) = {
"db" => (DirectiveSymbol::Db, <>),
"byte" => (DirectiveSymbol::Db, <>),
"dw" => (DirectiveSymbol::Dw, <>),
"word" => (DirectiveSymbol::Dw, <>),
"dl" => (DirectiveSymbol::Dl, <>),
"dd" => (DirectiveSymbol::Dd, <>),
};
/// First parameter is the value parameter, second parameter is the size parameter.
FillOrPadValueDirective: (DirectiveParameter, DirectiveParameter, u8, SourceSpan) = {
"fillbyte" => (DirectiveParameter::FillValue, DirectiveParameter::FillSize, 1, <>),
"fillword" => (DirectiveParameter::FillValue, DirectiveParameter::FillSize, 2, <>),
"filllong" => (DirectiveParameter::FillValue, DirectiveParameter::FillSize, 3, <>),
"filldword" => (DirectiveParameter::FillValue, DirectiveParameter::FillSize, 4, <>),
"padbyte" => (DirectiveParameter::PadValue, DirectiveParameter::PadSize, 1, <>),
"padword" => (DirectiveParameter::PadValue, DirectiveParameter::PadSize, 2, <>),
"padlong" => (DirectiveParameter::PadValue, DirectiveParameter::PadSize, 3, <>),
"paddword" => (DirectiveParameter::PadValue, DirectiveParameter::PadSize, 4, <>),
};
IdentifierOptions = "identifier"*;
ArchDirective: Directive = <directive:"arch"> <arch:"identifier"> =>? {
let architecture = arch.0;
if ["spc700", "spc700-raw"].contains(&&*architecture) {
environment.read_recursive().options.report_diagnostic(AssemblyError::ArchitectureDirectiveIgnored {
src: source_code.clone(),
location: source_range(directive.into(), arch.1.into()),
});
Ok(Directive::default())
} else {
Err(AssemblyError::InvalidArchitectureDirective {
arch: architecture,
src: source_code.clone(),
location: source_range(directive.into(), arch.1.into()),
}).map_err(|error| ParseError::User { error })
}
};
FillAlignDirective: Directive = <directive:"fill"> <align_keyword:"align"> <alignment:AssemblyTimeValue> <offset:OffsetSpecifier?> => {
let location = source_range(directive.into(), offset.clone().map(|o| o.1.into()).unwrap_or(align_keyword.into()));
Directive {
value: DirectiveValue::Fill {
operation: FillOperation::ToAlignment {
offset: offset.map(|offset| offset.0),
},
parameter: alignment,
value: None,
},
span: location,
expected_value: None,
}
};
FillAmountDirective: Directive = <directive:"fill"> <amount:AssemblyTimeValue> => Directive {
value: DirectiveValue::Fill {
operation: FillOperation::Amount,
parameter: amount,
value: None,
},
span: directive,
expected_value: None,
};
PadDirective: Directive = <directive:"pad"> <address:AssemblyTimeValue> => Directive {
value: DirectiveValue::Fill {
operation: FillOperation::ToAddress,
parameter: address,
value: None,
},
span: directive,
expected_value: None,
};
OffsetSpecifier: (AssemblyTimeValue, SourceSpan) = <offset_keyword:"offset"> <offset:AssemblyTimeValue> =>
(offset, offset_keyword.into());
Conditional: Directive = {
<start:"if"> <condition:AssemblyTimeValue> "\n" <true_block:Program> <false_block:ElseBlock?> <end:"endif"> => Directive {
value: DirectiveValue::Conditional {
condition,
true_block,
false_block: false_block.unwrap_or_default(),
},
span: source_range(start.into(), end.into()),
expected_value: None,
},
};
ElseBlock: Vec<ProgramElement> = {
"else" <Program>,
<start:"elseif"> <condition:AssemblyTimeValue> <nl:"\n"> <true_block:Program> <false_block:ElseBlock> => {
vec![ProgramElement::Directive(Directive {
value: DirectiveValue::Conditional {
condition,
true_block,
false_block,
},
span: source_range(start.into(), nl.into()),
expected_value: None,
})]
},
};
UserDefinedMacro: Directive = <start:"macro"> <name:"identifier"> <arguments:UserDefinedMacroArguments?> "\n" <body:Program> <end:"endmacro"> => Directive {
value: DirectiveValue::UserDefinedMacro {
name: name.0,
arguments: MacroParent::new_formal(arguments, name.1),
body,
},
span: source_range(start.into(), end.into()),
expected_value: None,
};
UserDefinedMacroArguments: Vec<(SharedStr, SourceSpan)> = {
"(" <Comma<"identifier">> ")" => <>,
"," <Comma<"identifier">> => <>,
};
AssemblyTimeValueList = { Comma<AssemblyTimeValue>, Comma<AssemblyTimeValueWithHashPrefix> };
AssemblyTimeValueWithHashPrefix = "#" <AssemblyTimeValue>;
/// Allows both vasm and asar syntax; note that asar requires an end point.
/// vasm: `, start [, length]`
/// asar: `: start - end`
InclusionRange: SourceSpan = {
<comma:","> <start:AssemblyTimeValue> <length:( "," <AssemblyTimeValue> )?> =>? {
// usize::MAX > i64::MAX only on wasm, so the "safe" defaults will most likely never be hit.
let start = usize::try_from(start.try_value(comma.into(), source_code)
.map_err(AssemblyError::from)?).unwrap_or(usize::MIN);
let length = usize::try_from(length.map(|length| length.try_value(comma.into(), source_code)).transpose()
.map_err(AssemblyError::from)?.unwrap_or(usize::MAX as i64)).unwrap_or(usize::MAX);
Ok((start, length).into())
},
<colon:":"> <start:AssemblyTimeValue> <dash:"-range-"> <end:AssemblyTimeValue> =>? {
let colon_dash_range = source_range(colon.into(), dash.into());
let start = usize::try_from(start.try_value(colon_dash_range, source_code)
.map_err(AssemblyError::from)?).unwrap_or(usize::MIN);
let end = usize::try_from(end.try_value(colon_dash_range, source_code)
.map_err(AssemblyError::from)?).unwrap_or(usize::MAX);
if start > end {
Err(ParseError::User { error: AssemblyError::StartAboveEnd {
start,
end,
src: source_code.clone(),
location: source_range(colon.into(), dash.into()),
}})
} else {
Ok((start, end - start).into())
}
},
};
// Required for ambiguity resolution purposes.
#[inline]
MultipleLabelsForInstruction: Vec<Reference> = LabelForInstruction+;
LabelForInstruction: Reference = {
<dots:"."+> <identifier:"identifier"> ":"? "\n"* => Reference::UnresolvedLabel {
name: identifier.0.clone(),
nesting_level: dots.len(),
span: identifier.1,
value: None,
},
// Asar allows number-only identifiers, at least in local labels where ambiguity is not a problem.
<dots:"."+> <identifier:"number"> ":"? "\n"* => Reference::UnresolvedLabel {
name: identifier.1.clone(),
nesting_level: dots.len(),
span: identifier.2,
value: None,
},
<identifier:"identifier"> ":" "\n"* =>? Ok(Reference::Label(environment.write().get_global_label(&identifier.0, identifier.1, LabelUsageKind::AsDefinition, source_code)?)),
<plus:LabelingPlus> ":"? "\n"* => Reference::Relative {
direction: RelativeReferenceDirection::Forward,
id: plus.0,
value: None,
span: plus.1,
},
<minus:LabelingMinus> ":"? "\n"* => Reference::Relative {
direction: RelativeReferenceDirection::Backward,
id: minus.0,
value: None,
span: minus.1,
},
};
LabelingMinus: (NonZeroU64, SourceSpan) = {
"---" => <>,
"-" => (NonZeroU64::new(1).unwrap(), <>.into()),
"-range-" => (NonZeroU64::new(1).unwrap(), <>.into()),
};
LabelingPlus: (NonZeroU64, SourceSpan) = {
"+++" => <>,
"+" => (NonZeroU64::new(1).unwrap(), <>.into()),
};
Reference = { ReferenceBase, GlobalReferenceAsAddress };
ReferenceAsDefinition = { ReferenceBase => <>.0, GlobalReferenceAsDefinition };
ReferenceBase: (Reference, SourceSpan) = {
<dots:"."+> <identifier:"identifier"> => (Reference::UnresolvedLabel {
name: identifier.0.clone(),
nesting_level: dots.len(),
span: source_range(dots[0].into(), identifier.1.into()),
value: None,
}, source_range(dots[0].into(), identifier.1.into())),
<dots:"."+> <identifier:"number"> => (Reference::UnresolvedLabel {
name: identifier.1.clone(),
nesting_level: dots.len(),
span: source_range(dots[0].into(), identifier.2.into()),
value: None,
}, source_range(dots[0].into(), identifier.2.into())),
<start:"<"> <identifier:"identifier"> <end:">"> => (Reference::MacroArgument {
name: identifier.0,
span: source_range(start.into(), end.into()),
macro_parent: MacroParent::new_formal(None, identifier.1),
value: None,
}, source_range(start.into(), end.into())),
<pluses:"+++"> => (Reference::Relative {
direction: RelativeReferenceDirection::Forward,
id: pluses.0,
value: None,
span: pluses.1,
}, pluses.1),
<minuses:"---"> => (Reference::Relative {
direction: RelativeReferenceDirection::Backward,
id: minuses.0,
value: None,
span: minuses.1,
}, minuses.1),
"repeatcount" => (Reference::RepeatCount { span: <>, }, <>),
};
GlobalReferenceAsAddress: (Reference, SourceSpan) = <identifier:"identifier"> =>? Ok((
Reference::Label(environment.write().get_global_label(&identifier.0, identifier.1, LabelUsageKind::AsAddress, source_code)?),
identifier.1,
));
GlobalReferenceAsDefinition: Reference = <identifier:"identifier"> =>?
Ok(Reference::Label(environment.write().get_global_label(&identifier.0, identifier.1, LabelUsageKind::AsDefinition, source_code)?));
LabelAssignment: Directive = <mut reference:ReferenceAsDefinition> <eq:"="> <value:AssemblyTimeValue> => {
reference.set_location(value.clone());
Directive {
value: DirectiveValue::AssignReference { reference: reference.clone(), value },
span: source_range(reference.source_span().into(), eq.into()),
expected_value: None,
}
};
AssemblyTimeValue: AssemblyTimeValue = {
<lhs:AssemblyTimeValue> "==" <rhs:Or> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::Equals, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
<lhs:AssemblyTimeValue> "!=" <rhs:Or> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::NotEquals, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
<lhs:AssemblyTimeValue> "<=" <rhs:Or> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::LessEquals, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
<lhs:AssemblyTimeValue> ">=" <rhs:Or> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::GreaterEquals, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
<lhs:AssemblyTimeValue> ">" <rhs:Or> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::Greater, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
<lhs:AssemblyTimeValue> "<" <rhs:Or> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::Less, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
Or,
};
Or: AssemblyTimeValue = {
<lhs:Or> "|" <rhs:And> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::Or, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
And,
};
And: AssemblyTimeValue = {
<lhs:And> "&" <rhs:Xor> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::And, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
Xor,
};
Xor: AssemblyTimeValue = {
<lhs:Xor> "^" <rhs:Shift> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::Xor, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
Shift,
};
Shift: AssemblyTimeValue = {
<lhs:Shift> ">>" <rhs:Arithmetic> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::RightShift, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
<lhs:Shift> "<<" <rhs:Arithmetic> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::LeftShift, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
Arithmetic,
};
Arithmetic: AssemblyTimeValue = {
<lhs:Arithmetic> "+" <rhs:Factor> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::Add, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
<lhs:Arithmetic> "-" <rhs:Factor> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::Subtract, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
Factor,
};
Factor: AssemblyTimeValue = {
<lhs:Factor> "*" <rhs:Exponential> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::Multiply, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
<lhs:Factor> "/" <rhs:Exponential> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::Divide, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
<lhs:Factor> "%" <rhs:Exponential> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::Modulus, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
Exponential,
};
/// Note that the sub-expressions are the other way around, as exponentials have right-to-left operator precedence!
Exponential: AssemblyTimeValue = {
<lhs:Term> "**" <rhs:Exponential> => AssemblyTimeValue::BinaryOperation { lhs: Box::new(lhs.clone()), rhs: Box::new(rhs.clone()), operator: BinaryOperator::Exponentiation, span: source_range(lhs.source_span().into(), rhs.source_span().into())},
Term,
}
Term: AssemblyTimeValue = {
"number" => AssemblyTimeValue::Literal(<>.0, <>.2),
"+" <Term>,
<op:"-"> <inner_value: Term> => AssemblyTimeValue::UnaryOperation {
inner_value: Box::new(inner_value.clone()),
operator: UnaryOperator::Negate,
span: source_range(op.into(), inner_value.source_span().into())
},
<op:"~"> <inner_value: Term> => AssemblyTimeValue::UnaryOperation {
inner_value: Box::new(inner_value.clone()),
operator: UnaryOperator::Not,
span: source_range(op.into(), inner_value.source_span().into())
},
"(" <AssemblyTimeValue> ")",
Reference => AssemblyTimeValue::Reference(<>.0.clone(), <>.1),
};
AddressingMode: AddressingMode = {
<AssemblyTimeValue> => try_make_direct_page_addressing_mode(<>, AddressingMode::DirectPage, AddressingMode::Address),
"#" <AssemblyTimeValue> => AddressingMode::Immediate(<>),
"A" => AddressingMode::Register(Register::A),
"X" => AddressingMode::Register(Register::X),
"Y" => AddressingMode::Register(Register::Y),
"SP" => AddressingMode::Register(Register::SP),
"PSW" => AddressingMode::Register(Register::PSW),
"YA" => AddressingMode::Register(Register::YA),
"C" => AddressingMode::CarryFlag,
<AssemblyTimeValue> "+X" => try_make_direct_page_addressing_mode(<>, AddressingMode::DirectPageXIndexed, AddressingMode::XIndexed),
<AssemblyTimeValue> "+Y" => try_make_direct_page_addressing_mode(<>, AddressingMode::DirectPageYIndexed, AddressingMode::YIndexed),
"[" <AssemblyTimeValue> "+X" "]" => try_make_direct_page_addressing_mode(<>, AddressingMode::DirectPageXIndexedIndirect, AddressingMode::DirectPageXIndexedIndirect),
"[" <AssemblyTimeValue> "]" "+Y" => try_make_direct_page_addressing_mode(<>, AddressingMode::DirectPageIndirectYIndexed, AddressingMode::DirectPageIndirectYIndexed),
<AssemblyTimeValue> "." <"number"> =>? try_make_direct_page_addressing_mode((<>),
|(a, b)| Ok(AddressingMode::DirectPageBit(a, AddressingMode::check_bit(b.0 as u8, b.2, &source_code)?)),
|(a, b)| Ok(AddressingMode::AddressBit(a, AddressingMode::check_bit(b.0 as u8, b.2, &source_code)?))),
"/" <address:AssemblyTimeValue> => AddressingMode::NegatedAddressBit(address, 1),
"/" <address:AssemblyTimeValue> "." <bit:"number"> =>? Ok(AddressingMode::NegatedAddressBit(address, AddressingMode::check_bit(bit.0 as u8, bit.2, &source_code)?)),
"[" "X" "]" "+" => AddressingMode::IndirectXAutoIncrement,
"[" "X" "+" "]" => AddressingMode::IndirectXAutoIncrement,
"[" "X" "]" => AddressingMode::IndirectX,
"[" "Y" "]" => AddressingMode::IndirectY,
};
Comma<T>: Vec<T> = {
<mut v:(<T> ",")*> <e:T> => {
v.push(e);
v
}
};
/// Lexer API information so that LALRPOP uses the spcasm lexer found in lexer.rs
extern {
/// We provide this from the Token information.
type Location = usize;
type Error = AssemblyError;
enum Token {
"identifier" => Token::Identifier(<SharedStr>, <SourceSpan>),
"mnemonic" => Token::Mnemonic(<Mnemonic>, <SourceSpan>),
"A" => Token::Register(Register::A, <SourceSpan>),
"X" => Token::Register(Register::X, <SourceSpan>),
"Y" => Token::Register(Register::Y, <SourceSpan>),
"+X" => Token::PlusRegister(Register::X, <SourceSpan>),
"+Y" => Token::PlusRegister(Register::Y, <SourceSpan>),
"SP" => Token::Register(Register::SP, <SourceSpan>),
"PSW" => Token::Register(Register::PSW, <SourceSpan>),
"YA" => Token::Register(Register::YA, <SourceSpan>),
"C" => Token::Register(Register::C, <SourceSpan>),
"org" => Token::Directive(DirectiveSymbol::Org, <SourceSpan>),
"db" => Token::Directive(DirectiveSymbol::Db, <SourceSpan>),
"byte" => Token::Directive(DirectiveSymbol::Byte, <SourceSpan>),
"dw" => Token::Directive(DirectiveSymbol::Dw, <SourceSpan>),
"word" => Token::Directive(DirectiveSymbol::Word, <SourceSpan>),
"dl" => Token::Directive(DirectiveSymbol::Dl, <SourceSpan>),
"dd" => Token::Directive(DirectiveSymbol::Dd, <SourceSpan>),
"ascii" => Token::Directive(DirectiveSymbol::Ascii, <SourceSpan>),
"asciiz" => Token::Directive(DirectiveSymbol::Asciiz, <SourceSpan>),
"incbin" => Token::Directive(DirectiveSymbol::Incbin, <SourceSpan>),
"include" => Token::Directive(DirectiveSymbol::Include, <SourceSpan>),
"incsrc" => Token::Directive(DirectiveSymbol::Incsrc, <SourceSpan>),
"endasm" => Token::Directive(DirectiveSymbol::EndAsm, <SourceSpan>),
"brr" => Token::Directive(DirectiveSymbol::Brr, <SourceSpan>),
"sampletable" => Token::Directive(DirectiveSymbol::SampleTable, <SourceSpan>),
"pushpc" => Token::Directive(DirectiveSymbol::Pushpc, <SourceSpan>),
"pullpc" => Token::Directive(DirectiveSymbol::Pullpc, <SourceSpan>),
"arch" => Token::Directive(DirectiveSymbol::Arch, <SourceSpan>),
"offset" => Token::SpecialIdentifier("offset", <SourceSpan>),
"align" => Token::SpecialIdentifier("align", <SourceSpan>),
"repeatcount" => Token::SpecialIdentifier("repeatcount", <SourceSpan>),
"startpos" => Token::Directive(DirectiveSymbol::Startpos, <SourceSpan>),
"fill" => Token::Directive(DirectiveSymbol::Fill, <SourceSpan>),
"fillbyte" => Token::Directive(DirectiveSymbol::FillByte, <SourceSpan>),
"fillword" => Token::Directive(DirectiveSymbol::FillWord, <SourceSpan>),
"filllong" => Token::Directive(DirectiveSymbol::FillLong, <SourceSpan>),
"filldword" => Token::Directive(DirectiveSymbol::FillDWord, <SourceSpan>),
"pad" => Token::Directive(DirectiveSymbol::Pad, <SourceSpan>),
"padbyte" => Token::Directive(DirectiveSymbol::PadByte, <SourceSpan>),
"padword" => Token::Directive(DirectiveSymbol::PadWord, <SourceSpan>),
"padlong" => Token::Directive(DirectiveSymbol::PadLong, <SourceSpan>),
"paddword" => Token::Directive(DirectiveSymbol::PadDWord, <SourceSpan>),
"macro" => Token::Directive(DirectiveSymbol::Macro, <SourceSpan>),
"endmacro" => Token::Directive(DirectiveSymbol::EndMacro, <SourceSpan>),
"if" => Token::Directive(DirectiveSymbol::If, <SourceSpan>),
"elseif" => Token::Directive(DirectiveSymbol::ElseIf, <SourceSpan>),
"else" => Token::Directive(DirectiveSymbol::Else, <SourceSpan>),
"endif" => Token::Directive(DirectiveSymbol::EndIf, <SourceSpan>),
"math" => Token::Directive(DirectiveSymbol::Math, <SourceSpan>),
"namespace" => Token::Directive(DirectiveSymbol::Namespace, <SourceSpan>),
"repeat" => Token::Directive(DirectiveSymbol::Repeat, <SourceSpan>),
"endrepeat" => Token::Directive(DirectiveSymbol::EndRepeat, <SourceSpan>),
"number" => Token::Number(<i64>, <SharedStr>, <SourceSpan>),
"string" => Token::String(<Vec<u8>>, <SourceSpan>),
"#" => Token::Hash(<SourceOffset>),
"," => Token::Comma(<SourceOffset>),
"+" => Token::Plus(<SourceOffset>),
"+++" => Token::RelativeLabelPlus(<NonZeroU64>, <SourceSpan>),
"---" => Token::RelativeLabelMinus(<NonZeroU64>, <SourceSpan>),
"*" => Token::Star(<SourceOffset>),
"**" => Token::DoubleStar(<SourceSpan>),
"-" => Token::Minus(<SourceOffset>),
"-range-" => Token::RangeMinus(<SourceOffset>),
"/" => Token::Slash(<SourceOffset>),
"|" => Token::Pipe(<SourceOffset>),
"&" => Token::Ampersand(<SourceOffset>),
"^" => Token::Caret(<SourceOffset>),
"~" => Token::Tilde(<SourceOffset>),
"(" => Token::OpenParenthesis(<SourceOffset>),
")" => Token::CloseParenthesis(<SourceOffset>),
"[" => Token::OpenIndexingParenthesis(<SourceOffset>),
"]" => Token::CloseIndexingParenthesis(<SourceOffset>),
"<" => Token::OpenAngleBracket(<SourceOffset>),
">" => Token::CloseAngleBracket(<SourceOffset>),
"<<" => Token::DoubleOpenAngleBracket(<SourceSpan>),
">>" => Token::DoubleCloseAngleBracket(<SourceSpan>),
"%" => Token::Percent(<SourceOffset>),
":" => Token::Colon(<SourceOffset>),
"." => Token::Period(<SourceOffset>),
".b" => Token::ExplicitDirectPage(<SourceSpan>),
".w" => Token::ExplicitNoDirectPage(<SourceSpan>),
"=" => Token::Equals(<SourceOffset>),
"==" => Token::DoubleEquals(<SourceSpan>),
"<=" => Token::OpenAngleBracketEquals(<SourceSpan>),
">=" => Token::CloseAngleBracketEquals(<SourceSpan>),
"!=" => Token::ExclamationEquals(<SourceSpan>),
"\n" => Token::Newline(<SourceOffset>),
// Do this once/if LALRPOP supports it
// #[cfg(test)]
";=" => Token::TestComment(<Vec<u8>>, <SourceSpan>),
}
}