aboutsummaryrefslogtreecommitdiff
path: root/lib/xixanta/src/assembler.rs
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2024-09-26 15:53:57 +0200
committerMiquel Sabaté Solà <mikisabate@gmail.com>2024-12-12 07:41:30 +0100
commit184c39579227c0add80a61d489c242120001d6b6 (patch)
tree48737e0e00b5fb655090a1c9a4fc216dcf278cc0 /lib/xixanta/src/assembler.rs
parent54e1239fcccb2383e5caa88844a7229b5441c31e (diff)
downloadtools.nes-184c39579227.tar.gz
tools.nes-184c39579227.zip
Re-work the parser from scratch
The initial implementation was incredibly naive on how complex an assembler can actually be. Because of this, it never bothered to define and produce a proper AST, and hence it was overall extremely fragile on (not so) corner cases. This commit re-writes everything from scratch for the parser, and it should really be the last fundamental change to the parser other than minor additions/features here and there. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
Diffstat (limited to 'lib/xixanta/src/assembler.rs')
-rw-r--r--lib/xixanta/src/assembler.rs3203
1 files changed, 1600 insertions, 1603 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index 1a542ac..1e9854d 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -1,33 +1,52 @@
use crate::context::{Context, PValue};
use crate::errors::ParseError;
-use crate::instruction::{
- AddressingMode, Encodable, Fill, Generic, Instruction, Label, Literal, Node, PString, Scoped,
-};
+use crate::instruction::{AddressingMode, Bundle};
use crate::mapping::{Mapping, Segment};
-use crate::opcodes::{INSTRUCTIONS, OPCODES};
-use std::collections::hash_map::Entry;
+use crate::opcodes::INSTRUCTIONS;
+use crate::parser::{NodeType, PNode, Parser};
use std::collections::HashMap;
-use std::io::{self, BufRead, Read};
-use std::ops::Range;
+use std::io::Read;
// TODO: proper AST: WRITE_PPU_DATA from NES is a good example
// TODO: for christ's sake, automated tests!
// TODO: macros are meant to be global!
// TODO: instead of mapping.nodes having a value of vec<node>, the value should be a Context.
// TODO: proc's, labels, macros, and scopes can be merged dramatically.
+// TODO: allow pointer arithmetic (e.g. 'adc #List::ptr + 1').
// TODO: more to_owned() stuff, more rustacean way of doing things, more ...
// TODO: warning on empty segments
type Result<T> = std::result::Result<T, ParseError>;
+#[derive(Debug, Clone, PartialEq)]
+pub enum LiteralMode {
+ Hexadecimal,
+ Binary,
+ Plain,
+}
+
pub struct Assembler {
line: usize,
column: usize,
context: Context,
+ literal_mode: Option<LiteralMode>,
+ only_context: bool,
+ force_decimal: bool,
mapping: Mapping,
offsets: HashMap<String, usize>,
}
+// Control statements which end up affecting which context we are in.
+const TOUCH_CONTEXT: [&str; 7] = [
+ ".scope",
+ ".endscope",
+ ".proc",
+ ".endproc",
+ ".macro",
+ ".endmacro",
+ ".segment",
+];
+
impl Assembler {
pub fn new(segments: Vec<Segment>) -> Self {
assert!(segments.len() > 0);
@@ -40,6 +59,9 @@ impl Assembler {
Self {
line: 0,
column: 0,
+ literal_mode: None,
+ only_context: false,
+ force_decimal: false,
context: Context::new(),
mapping: Mapping::new(segments),
offsets,
@@ -58,1566 +80,1587 @@ impl Assembler {
}
}
- pub fn assemble_nodes(&mut self, reader: impl Read) -> Result<()> {
- self.from_reader(reader)?;
- self.context.global();
- self.evaluate()?;
- self.resolve_labels()?;
+ pub fn assemble(&mut self, reader: impl Read) -> Result<Vec<Bundle>> {
+ let mut res = vec![];
- Ok(())
- }
+ let mut parser = Parser::new();
+ parser.parse(reader)?;
- pub fn assemble(&mut self, reader: impl Read) -> Result<Vec<&dyn Encodable>> {
- let mut instructions: Vec<&dyn Encodable> = vec![];
+ // println!("{:#?}", parser.nodes);
- self.assemble_nodes(reader)?;
+ // NOTE: first step: unroll macros, update context, set variables.
- let mut idx: usize = 0;
- for segment in &self.mapping.segments {
- let mut size: usize = 0;
+ self.only_context = true;
+ for node in parser.nodes.clone() {
+ match node.node_type {
+ NodeType::Assignment => {
+ self.evaluate_assignment(node)?;
- while idx < segment.start.into() {
- match &segment.fill {
- Some(fill) => instructions.push(fill),
- None => instructions.push(&Fill { value: 0x00 }),
+ println!("{:#?}", self.context);
}
- idx += 1;
- }
-
- for node in &self.mapping.nodes[&segment.name] {
- match node {
- Node::Instruction(instr) => {
- instructions.push(instr);
- size += usize::from(instr.size());
- }
- Node::Literal(lit) => {
- instructions.push(lit);
- size += usize::from(lit.size());
- }
- _ => {}
+ NodeType::Control => {
+ self.evaluate_control(node)?;
}
- }
-
- if size > segment.size {
- return Err(ParseError {
- line: 0,
- message: format!(
- "segment '{}' expected a size of '{}' bytes but '{}' bytes were produced instead",
- segment.name, size, segment.size
- ),
- });
- }
- idx += size;
- if segment.fill.is_none() {
- continue;
- }
-
- while size < segment.size {
- instructions.push(segment.fill.as_ref().unwrap());
- size += 1;
- idx += 1;
+ _ => {}
}
}
+ self.only_context = false;
- Ok(instructions)
- }
+ // Check for unclosed scope definition.
+ if !self.context.is_global() {
+ return Err(self.parser_error(
+ format!(
+ "definition for '{}' has not been closed",
+ self.context.name()
+ )
+ .as_str(),
+ ));
+ }
- pub fn disassemble(&mut self, reader: impl Read) -> Result<Vec<&dyn Encodable>> {
- self.from_byte_reader(reader)?;
+ // NOTE: second step: let's rock.
- let mut instructions: Vec<&dyn Encodable> = vec![];
- for node in self.mapping.current() {
- println!("{:#?}", node);
- match node {
- Node::Instruction(instr) => instructions.push(instr),
- Node::Literal(lit) => instructions.push(lit),
+ for node in parser.nodes {
+ match node.node_type {
+ NodeType::Instruction => {
+ res.push(self.evaluate_node(node)?);
+ }
+ NodeType::Control => {
+ self.evaluate_control(node)?;
+ }
_ => {}
}
}
- Ok(instructions)
- }
+ // NOTE: third step: update addresses of referenced labels.
+ // TODO
- pub fn from_reader<R: Read>(&mut self, reader: R) -> Result<()> {
- for line in io::BufReader::new(reader).lines() {
- // TODO: instead of this, accumulate errors so to give as many
- // errors as possible.
- self.parse_line(line?.as_str())?;
- self.line += 1;
- }
- Ok(())
+ // println!("{:#?}", res);
+ Ok(res)
}
- pub fn parse_line(&mut self, line: &str) -> Result<()> {
- self.column = 0;
+ // pub fn disassemble(&mut self, reader: impl Read) -> Result<Vec<&dyn Encodable>> {
+ // self.from_byte_reader(reader)?;
- if !self.skip_whitespace(line) {
- return Ok(());
- }
+ // let mut instructions: Vec<&dyn Encodable> = vec![];
+ // for node in self.mapping.current() {
+ // println!("{:#?}", node);
+ // match node {
+ // Node::Instruction(instr) => instructions.push(instr),
+ // Node::Literal(lit) => instructions.push(lit),
+ // _ => {}
+ // }
+ // }
+
+ // Ok(instructions)
+ // }
- match self.parse_identifier(line) {
- Some(identifier) => self.parse_from_identifier(identifier, line),
- None => Ok(()),
+ fn evaluate_assignment(&mut self, node: Box<PNode>) -> Result<()> {
+ if self
+ .context
+ .current_mut()
+ .unwrap()
+ .contains_key(&node.value.value)
+ {
+ return Err(ParseError {
+ line: self.line,
+ message: format!(
+ "variable '{}' is being re-assigned: it was previously defined in line {}",
+ node.value.value, node.value.line,
+ ),
+ parse: false,
+ });
}
- }
- pub fn evaluate(&mut self) -> Result<()> {
- for segment in &self.mapping.segments {
- for node in self.mapping.nodes.get_mut(&segment.name).unwrap() {
- match node {
- Node::Instruction(instr) => {
- Self::update_instruction_with_context(instr, &self.context)?;
- instr.address = segment.start;
-
- self.offsets
- .entry(segment.name.clone())
- .and_modify(|value| {
- instr.address += *value as u16;
- *value += usize::from(instr.size())
- })
- .or_insert(instr.size().into());
- }
- Node::Scoped(scope) => {
- if scope.start {
- self.context.push(&scope.identifier.value);
- } else {
- _ = self.context.pop();
- }
- }
- Node::Literal(literal) => {
- Self::update_literal_with_context(literal, &self.context)?;
- self.offsets
- .entry(segment.name.clone())
- .and_modify(|value| *value += usize::from(literal.size()))
- .or_insert(literal.size().into());
- }
- Node::Label(label) => {
- let address =
- usize::from(segment.start) + self.offsets.get(&segment.name).unwrap();
-
- self.context
- .current_mut()
- .unwrap()
- .entry(label.value.clone())
- .and_modify(|e| e.value = address);
- }
- _ => {}
- }
- }
+ if let Some(value_node) = node.left {
+ self.force_decimal = true;
+ println!("{:#?}", value_node);
+ let val = self.evaluate_node(value_node.clone())?;
+ println!("{:#?}", val);
+ self.force_decimal = false;
+
+ self.context.current_mut().unwrap().insert(
+ node.value.value.to_owned(),
+ PValue {
+ node: *value_node,
+ value: val,
+ label: false,
+ },
+ );
}
Ok(())
}
- // TODO: oh boy...
- pub fn resolve_labels(&mut self) -> Result<()> {
- for segment in &self.mapping.segments {
- for node in self.mapping.nodes.get_mut(&segment.name).unwrap() {
- match node {
- Node::Instruction(instr) => {
- if !instr.resolved {
- match &instr.left {
- Some(pstring) => {
- match self.context.current().unwrap().get(&pstring.value) {
- Some(entry) => {
- if instr.mode == AddressingMode::Absolute {
- let bytes = entry.value.to_le_bytes();
- instr.bytes = [bytes[0], bytes[1]];
- } else {
- let diff: isize = entry.value as isize
- - (instr.address as isize + 2);
- if diff < -128 || diff > 127 {
- return Err(instr.mnemonic.parser_error(
- format!("relative addressing out of range")
- .as_str(),
- ));
- }
- let bytes = diff.to_le_bytes();
- instr.bytes = [bytes[0], 0];
- }
- }
- None => {
- return Err(instr.mnemonic.parser_error(
- format!("label '{}' not found", pstring.value)
- .as_str(),
- ))
- }
- }
- }
- None => {
- return Err(instr.mnemonic.parser_error(
- format!("there is no label for the given jump instruction")
- .as_str(),
- ))
- }
- }
- }
- }
- Node::Literal(literal) => {
- if !literal.resolved {
- match self
- .context
- .current()
- .unwrap()
- .get(&literal.identifier.value)
- {
- Some(entry) => {
- let bytes = entry.value.to_le_bytes();
- literal.bytes = [bytes[0], bytes[1]];
- }
- None => {
- return Err(literal.identifier.parser_error(
- format!(
- "'{}' is neither a known variable or label at this scope",
- literal.identifier.value
- )
- .as_str(),
- ))
- }
- }
- }
+ fn evaluate_node(&mut self, node: Box<PNode>) -> Result<Bundle> {
+ match node.node_type {
+ NodeType::Control => self.evaluate_control(node),
+ NodeType::Literal => self.evaluate_literal(node),
+ NodeType::Instruction => self.evaluate_instruction(node),
+ NodeType::Value => match self.literal_mode {
+ Some(LiteralMode::Hexadecimal) => self.evaluate_hexadecimal(node),
+ Some(LiteralMode::Binary) => self.evaluate_binary(node),
+ Some(LiteralMode::Plain) => self.evaluate_decimal(node),
+ None => {
+ if self.force_decimal {
+ self.evaluate_decimal(node)
+ } else {
+ Err(self.parser_error("no prefix was given to operand"))
}
- _ => {}
}
- }
+ },
+ // TODO
+ _ => Ok(Bundle::new()),
}
-
- Ok(())
}
- fn update_instruction_with_context(instr: &mut Instruction, context: &Context) -> Result<()> {
- // To keep things simple, we remove out the `implied` case and we parse
- // further with a known `Some` value for the base algorithm implemented
- // in `update_instruction_and_bytes`.
- if instr.left.is_some() {
- Self::update_addressing_and_bytes(instr, context)?;
+ fn evaluate_instruction(&mut self, node: Box<PNode>) -> Result<Bundle> {
+ let mnemonic = node.value.value.to_lowercase();
+
+ let (mode, mut bundle) = if node.left.is_some() {
+ self.get_addressing_mode_and_bytes(node)?
} else {
- instr.mode = AddressingMode::Implied;
- }
+ (AddressingMode::Implied, Bundle::new())
+ };
- // Now that we have the addressing mode and the bytes, we can fill out
- // the rest of it by fetching the values on `INSTRUCTIONS`.
- match INSTRUCTIONS.get(&instr.mnemonic.value.to_lowercase()) {
- Some(entries) => match entries.get(&instr.mode) {
+ match INSTRUCTIONS.get(&mnemonic) {
+ Some(entries) => match entries.get(&mode) {
Some(values) => {
- instr.cycles = values.cycles;
- instr.opcode = values.opcode;
- instr.size = values.size;
- instr.affected_on_page = values.affected_on_page;
+ bundle.cycles = values.cycles;
+ bundle.size = values.size;
+ bundle.affected_on_page = values.affected_on_page;
+ bundle.bytes[2] = bundle.bytes[1];
+ bundle.bytes[1] = bundle.bytes[0];
+ bundle.bytes[0] = values.opcode.to_le_bytes()[0];
}
None => {
- return Err(instr.mnemonic.parser_error(
+ return Err(self.parser_error(
format!(
- "bad addressing mode '{}' for the instruction '{}'",
- &instr.mode, &instr.mnemonic.value
+ "cannot use {} addressing mode for the instruction '{}'",
+ mode, mnemonic
)
.as_str(),
- ));
+ ))
}
},
None => {
- return Err(instr.mnemonic.parser_error(
- format!("unknown instruction '{}'", &instr.mnemonic.value).as_str(),
- ));
+ return Err(self.parser_error(format!("unknown instruction {}", mnemonic).as_str()))
}
}
-
- Ok(())
+ Ok(bundle)
}
- fn update_addressing_and_bytes(instr: &mut Instruction, context: &Context) -> Result<()> {
- // `unwrap()` is guaranteed to work by the caller.
- let left = instr.left.as_ref().unwrap();
-
- // We will first try to check if there's any variable involved on the
- // left arm and replace the string if so. This will greatly simplify
- // things down the line. That being said, there is a special reserved
- // case, which is the implied addressing by using "a". In this case, we
- // want to ensure that we assume an implied addressing and not a
- // variable named "a".
- if left.value.to_lowercase() == "a" {
- instr.mode = AddressingMode::Implied;
+ fn get_addressing_mode_and_bytes(
+ &mut self,
+ node: Box<PNode>,
+ ) -> Result<(AddressingMode, Bundle)> {
+ if node.clone().left.unwrap().node_type == NodeType::Indirection {
+ self.get_from_indirect(node)
+ } else if node.right.is_some() {
+ self.get_from_indexed(node)
} else {
- let (nleft, resolved) = Self::replace_variable(left, context)?;
- // TODO
- instr.resolved = resolved;
- if !resolved {
- if instr.mnemonic.value == "jmp" {
- instr.mode = AddressingMode::Absolute;
- } else {
- instr.mode = AddressingMode::RelativeOrZeropage;
- }
- }
+ self.get_from_left(node)
+ }
+ }
- if nleft.value.starts_with('$') {
- // This is an address. At this point we should assume that the
- // left node contains the address itself, and that the right one
- // will contain whether there is indexing.
-
- let string = nleft.value.chars().as_str();
- instr.bytes = Self::parse_hex_from(string, &nleft, true, false, true)?;
-
- match &instr.right {
- Some(xy) => match xy.value.to_lowercase().as_str() {
- "x" => {
- if string.len() == 3 {
- instr.mode = AddressingMode::ZeropageIndexedX;
- } else {
- instr.mode = AddressingMode::IndexedX;
- }
- }
- "y" => {
- if string.len() == 3 {
- instr.mode = AddressingMode::ZeropageIndexedY;
- } else {
- instr.mode = AddressingMode::IndexedY;
- }
- }
- _ => return Err(xy.parser_error("index is neither X nor Y")),
- },
- None => {
- if string.len() == 3 {
- instr.mode = AddressingMode::RelativeOrZeropage;
- } else {
- instr.mode = AddressingMode::Absolute;
- }
+ fn get_from_indirect(&mut self, node: Box<PNode>) -> Result<(AddressingMode, Bundle)> {
+ let left = node.left.unwrap();
+
+ match node.right {
+ Some(right) => {
+ if right.value.value.trim().to_lowercase() == "y" {
+ if left.right.is_some() {
+ return Err(self.parser_error(
+ "it has to be either X addressing or Y addressing, not all at once",
+ ));
+ }
+
+ let val = self.evaluate_node(left.left.unwrap())?;
+ if val.size != 1 {
+ return Err(self.parser_error(
+ "address can only be one byte long on indirect Y addressing",
+ ));
}
+ return Ok((AddressingMode::IndirectY, val));
}
- } else if nleft.value.starts_with('#') {
- // Immediate addressing in any case: hexadecimal, binary or
- // decimal. Hence, just figure out the character being used and
- // call the right function for it.
-
- let mut chars = nleft.value.chars();
- chars.next();
- let string = chars.as_str();
-
- instr.bytes = Self::parse_numeric(string, &nleft, false)?;
- instr.mode = AddressingMode::Immediate;
- } else if nleft.value.starts_with('(') {
- // Indirect addressing. In this case the left arm can be further
- // subdivided. That is, indirect X-indexing is represented like
- // so: `instr ($NN, x)`. Hence, first of all we have to figure
- // out whether there is a subdivision.
-
- let (left1, oleft2) = Self::split_left_arm(&nleft)?;
- match oleft2 {
- Some(left2) => {
- // There is subdivision. Thus, we have to assume
- // indirect X-indexing, which means that the right arm
- // should be None and that the right side of the left
- // node must match the X register. Other than that, the
- // address being referenced must be zero page.
- if instr.right.is_some() {
- return Err(instr.right.as_ref().unwrap().parser_error(
- "bad indirect mode, expecting an indirect X-indexed addressing mode"
- ));
- }
- if left2.value.to_lowercase() != "x" {
- return Err(left2.parser_error(
- "the index in indirect X-indexed addressing must be X",
+ return Err(
+ self.parser_error("only the Y index is allowed on indirect Y addressing")
+ );
+ }
+ None => match left.right {
+ Some(right) => {
+ if right.value.value.trim().to_lowercase() == "x" {
+ let val = self.evaluate_node(left.left.unwrap())?;
+ if val.size != 1 {
+ return Err(self.parser_error(
+ "address can only be one byte long on indirect X addressing",
));
}
- match Self::parse_hex_from(&left1.value, &left1, false, false, true) {
- Ok(bytes) => instr.bytes = bytes,
- Err(e) => {
- let msg = String::from(
- "when parsing an instruction with indirect X-indexed addressing: ",
- ) + &e.message;
- return Err(left1.parser_error(msg.as_str()));
- }
- }
- instr.mode = AddressingMode::IndirectX;
- }
- None => {
- // There is no subdivision on the left arm. Hence, if
- // there is something on the right arm then we must
- // assume indirect Y-index addressing, and if not then
- // it's indirect addressing with no indices involvved.
- if instr.right.is_some() {
- if instr.right.as_ref().unwrap().value.to_lowercase() != "y" {
- return Err(instr.right.as_ref().unwrap().parser_error(
- "the index in indirect Y-indexed addressing must be Y",
- ));
- }
- match Self::parse_hex_from(&left1.value, &left1, false, false, true) {
- Ok(bytes) => instr.bytes = bytes,
- Err(e) => {
- let msg = String::from(
- "when parsing an instruction with indirect Y-indexed addressing: ",
- ) + &e.message;
- return Err(left1.parser_error(msg.as_str()));
- }
- }
- instr.mode = AddressingMode::IndirectY;
- } else {
- instr.bytes =
- Self::parse_hex_from(&left1.value, &left1, true, true, true)?;
- instr.mode = AddressingMode::Indirect;
- }
+ return Ok((AddressingMode::IndirectX, val));
}
+ return Err(
+ self.parser_error("only the X index is allowed on indirect X addressing")
+ );
}
- } else {
- // At this point all of the syntax cases have been exhausted:
- // the programmer messed up. From this point on we try to figure
- // out how they messed up.
-
- if nleft.value.starts_with('=') {
- return Err(instr.mnemonic.parser_error(
- format!(
- "cannot use '{}' in an assignment because it's a word reserved for an instruction mnemonic",
- instr.mnemonic.value
- ).as_str(),
- ));
+ None => {
+ let val = self.evaluate_node(left.left.unwrap())?;
+ if val.size != 2 {
+ return Err(self.parser_error("expecting a full 16-bit address"));
+ }
+ return Ok((AddressingMode::Indirect, val));
}
- // TODO:
- // instr.mode = AddressingMode::Absolute;
- // return Err(instr.mnemonic.parser_error(
- // format!(
- // "unknown addressing mode for instruction '{}'",
- // instr.mnemonic.value
- // )
- // .as_str(),
- // ));
- }
+ },
}
-
- Ok(())
}
- fn update_literal_with_context(literal: &mut Literal, context: &Context) -> Result<()> {
- // If it has already been set, skip it.
- // TODO: add a proper `is_set` thingie to it instead of this hack.
- if literal.bytes[0] != 0 || literal.bytes[1] != 0 {
- return Ok(());
- }
-
- // Evaluate any possible variable being used inside of this literal.
- let (evaled, resolved) = Self::replace_variable(&literal.identifier, context)?;
+ fn get_from_indexed(&mut self, node: Box<PNode>) -> Result<(AddressingMode, Bundle)> {
+ self.literal_mode = None; // TODO: needed?
+ let val = self.evaluate_node(node.left.unwrap())?;
- // It may happen that the literal is just a label that is to be resolved
- // in the future. If so, let's leave early.
- literal.resolved = resolved;
- if !resolved {
- return Ok(());
+ if let Some(lm) = &self.literal_mode {
+ if *lm != LiteralMode::Hexadecimal {
+ return Err(self.parser_error("indexed addressing only works with addresses"));
+ }
}
- // Parse the numeric value after a possible variable has been replaced.
- let two_bytes_allowed = literal.size == 2;
- let res = Self::parse_numeric(
- evaled.value.as_str(),
- &literal.identifier,
- two_bytes_allowed,
- );
-
- // And finally assign the computed bytes.
- match res {
- Ok(bytes) => {
- literal.bytes = bytes;
- Ok(())
+ match node.right.unwrap().value.value.to_lowercase().trim() {
+ "x" => {
+ if val.size == 1 {
+ Ok((AddressingMode::ZeropageIndexedX, val))
+ } else {
+ Ok((AddressingMode::IndexedX, val))
+ }
}
- Err(e) => {
- let msg = String::from("when parsing a data literal: ") + &e.message;
- Err(literal.identifier.parser_error(msg.as_str()))
+ "y" => {
+ if val.size == 1 {
+ Ok((AddressingMode::ZeropageIndexedY, val))
+ } else {
+ Ok((AddressingMode::IndexedY, val))
+ }
}
+ _ => Err(self.parser_error("can only use X and Y as indices")),
}
}
- fn parse_numeric(string: &str, node: &PString, two_bytes_allowed: bool) -> Result<[u8; 2]> {
- if string.starts_with('$') {
- Ok(Self::parse_hex_from(
- string,
- node,
- two_bytes_allowed,
- false,
- true,
- )?)
- } else if string.starts_with('%') {
- Ok([Self::parse_binary_from(string, node)?, 0])
- } else {
- Ok([Self::parse_decimal_from(string, node)?, 0])
- }
- }
+ fn get_from_left(&mut self, node: Box<PNode>) -> Result<(AddressingMode, Bundle)> {
+ let left = node.left.unwrap();
- fn split_left_arm(node: &PString) -> Result<(PString, Option<PString>)> {
- let mut chars = node.value.chars();
- chars.next();
- let string = chars.as_str();
-
- match string.find(|c: char| c == ',') {
- Some(idx) => {
- let left1 = string.get(..idx).unwrap_or("").trim();
- let left2 = string.get(idx + 1..).unwrap_or("").trim();
-
- Ok((
- PString {
- value: left1.to_string(),
- line: node.line,
- range: Range {
- start: node.range.start + 1,
- end: node.range.start + 1 + left1.len(),
- },
- },
- Some(PString {
- value: left2.to_string(),
- line: node.line,
- range: Range {
- start: node.range.start + 1 + idx,
- end: node.range.start + 1 + idx + left2.len(),
- },
- }),
- ))
- }
- None => Ok((
- PString {
- value: string.to_string(),
- line: node.line,
- range: Range {
- start: node.range.start + 1,
- end: node.range.end,
- },
- },
- None,
- )),
+ if left.value.value.to_lowercase().trim() == "a" {
+ return Ok((AddressingMode::Implied, Bundle::new()));
}
- }
- fn parse_binary_from(string: &str, node: &PString) -> Result<u8> {
- let mut value = 0;
- let mut shift = 0;
+ self.literal_mode = None; // TODO: needed?
+ let val = self.evaluate_node(left)?;
- for c in string.get(1..).unwrap_or("").chars().rev() {
- if c == '1' {
- let val = 1 << shift;
- value += val;
- } else if c != '0' {
- return Err(
- node.parser_error(format!("bad binary format for '{}'", string).as_str())
- );
+ match self.literal_mode {
+ Some(LiteralMode::Hexadecimal) => {
+ if val.size == 1 {
+ Ok((AddressingMode::RelativeOrZeropage, val))
+ } else {
+ Ok((AddressingMode::Absolute, val))
+ }
+ }
+ Some(LiteralMode::Plain) => {
+ if val.size > 1 {
+ Err(self.parser_error("immediate is too big"))
+ } else {
+ Ok((AddressingMode::Immediate, val))
+ }
+ }
+ _ => {
+ Err(self
+ .parser_error("left arm of instruction is neither an address nor an immediate"))
}
-
- shift += 1;
- }
-
- if shift < 8 {
- Err(node.parser_error("missing binary digits to get a full byte"))
- } else if shift > 8 {
- Err(node.parser_error("too many binary digits for a single byte"))
- } else {
- Ok(value)
}
}
- // TODO: returns if resolved
- fn replace_variable(node: &PString, context: &Context) -> Result<(PString, bool)> {
- match node
- .value
- .find(|c: char| c.is_alphabetic() || c == '_' || c == '@')
- {
- Some(idx) => {
- // Before doing any replacement, let's check the character
- // before the one that was found. In this case, if it was a
- // proper ASCII digit, then it cannot be a variable but it's
- // part of a numeric literal (e.g. '1A'): then just let the
- // different numeric parsing functions do their job.
- if idx > 0 {
- let prev = node.value.chars().nth(idx - 1).unwrap_or(' ');
- if prev.is_ascii_digit() {
- return Ok((node.clone(), true));
- }
+ fn evaluate_control(&mut self, node: Box<PNode>) -> Result<Bundle> {
+ let id = node.value.value.to_lowercase();
+ let id_str = id.as_str();
+
+ // If we are just dealing with context resolution/assignment and the
+ // current control statement does not matter on that regard, just skip
+ // it.
+ // if self.only_context && !TOUCH_CONTEXT.contains(&id_str) {
+ // return Ok(Bundle::new());
+ // }
+
+ match id_str {
+ ".hibyte" => self.evaluate_hilo_byte(node.args.unwrap_or(vec![]), true),
+ ".lobyte" => self.evaluate_hilo_byte(node.args.unwrap_or(vec![]), false),
+ ".scope" => self.evaluate_scope_definition(node),
+ ".endscope" => self.evaluate_scope_end(),
+ // ".segment" => self.parse_segment_definition(&id, line),
+ // ".byte" | ".db" => self.parse_literal_bytes(&id, line, false),
+ // ".word" | ".dw" | ".addr" => self.parse_literal_bytes(&id, line, true),
+ // ".proc" => self.parse_proc_definition(&id, line),
+ // ".endproc" => self.parse_proc_end(&id),
+ // ".macro" => self.parse_macro_definition(&id, line),
+ // ".endmacro" => self.parse_macro_end(&id),
+ _ => Err(self.parser_error(format!("unknown control statement '{}'", id).as_str())),
+ }
+ }
+
+ fn evaluate_literal(&mut self, node: Box<PNode>) -> Result<Bundle> {
+ let mut prev = None;
+ self.literal_mode = None;
+
+ let ret = match node.value.value.chars().nth(0) {
+ Some(prefix) => {
+ if prefix == '$' {
+ prev = Some(LiteralMode::Hexadecimal);
+ } else if prefix == '%' {
+ prev = Some(LiteralMode::Binary);
+ } else {
+ prev = Some(LiteralMode::Plain);
}
+ self.literal_mode = prev.clone();
+ self.evaluate_node(node.left.unwrap())
+ }
+ None => Err(self.parser_error("no prefix was given to operand")),
+ };
- // The variable might still be before an inner comma (e.g.
- // sta ($20, x)). We will assume that variables can happen
- // only before that.
- let end = node.value.find(',').unwrap_or(node.value.len());
- let mut string = node.value.get(idx..end).unwrap_or("");
- let tail = node.value.get(end..).unwrap_or("");
-
- // Get the context that might be being referenced.
- let ctxt = match string.find("::") {
- Some(_) => {
- let tctxt = string.rsplit_once("::").unwrap_or(("", ""));
- if tctxt.0.is_empty() {
- context.current()
- } else {
- string = tctxt.1;
- context.find(tctxt.0)
- }
- }
- None => context.current(),
- };
-
- match ctxt {
- Some(hash) => {
- // If there was a comma before the "variable" (i.e. idx >
- // end and hence string == ""), or this is just the regular
- // X or Y index, just return early.
- match string.to_lowercase().as_str() {
- "x" | "y" | "" => return Ok((node.clone(), true)),
- _ => {}
- }
+ self.literal_mode = prev;
- // It's not any of the indices, let's look for a match on
- // the current scope.
- match hash.get(string) {
- Some(var) => {
- // If this is just a memory address (e.g.
- // label), then just return it as is.
- if var.label {
- return Ok((node.clone(), false));
- }
-
- let value = String::from(node.value.get(..idx).unwrap_or(""))
- + var.node.value.as_str();
- Ok((
- PString {
- value: value.clone() + tail,
- line: node.line,
- range: Range {
- start: node.range.start,
- end: node.range.start + value.len(),
- },
- },
- true,
- ))
- }
- None => {
- // If a variable could not be found, check that
- // this is not a purely hexadecimal number (e.g.
- // 'AA'). If that's the case, then just return
- // its value.
- if Self::parse_hex_from(string, node, true, false, false).is_ok() {
- return Ok((node.clone(), true));
- }
-
- // We've tried hard to not assume the programmer
- // messing up, but there's no other way around
- // it: it's an "unknown variable" error.
- return Err(node.parser_error(
- format!("unknown variable '{}'", string).as_str(),
- ));
- }
- }
- }
- None => {
- Err(node.parser_error(format!("unknown scope '{}'", "Global").as_str()))
- }
- }
- }
- None => Ok((node.clone(), true)),
- }
+ ret
}
- // Parse an hexadecimal value from the given `string`. A `node` must also be
- // supplied so a ParseError can be pushed inside of it in case anything goes
- // wrong. Other than that, there are three boolean parameters that have to
- // be passed:
- // - `two_bytes_allowed`: the value can be 8-bit or 16-bit long.
- // - `exactly_two_bytes`: the value has to be exactly 16-bit long.
- // - `char_given`: whether the string starts with a '$' character or not.
- fn parse_hex_from(
- string: &str,
- node: &PString,
- two_bytes_allowed: bool,
- exactly_two_bytes: bool,
- char_given: bool,
- ) -> Result<[u8; 2]> {
- let mut chars = string.chars();
- let len = if char_given {
- chars.next();
- string.len() - 1
- } else {
- string.len()
- };
+ fn evaluate_hexadecimal(&mut self, node: Box<PNode>) -> Result<Bundle> {
+ let mut chars = node.value.value.chars();
+ let mut bytes = [0, 0, 0];
+ let size: u8;
- // TODO: re-visit this "exactly_two_byes bs"
- match len {
+ match node.value.value.len() {
1 => {
- if exactly_two_bytes {
- return Err(node.parser_error("expecting a full 16-bit address"));
- }
- let i = Self::char_to_hex(node, chars.next())? * 16;
-
- Ok([i, 0])
+ bytes[0] = self.char_to_hex(chars.next())?;
+ size = 1;
}
2 => {
- if exactly_two_bytes {
- return Err(node.parser_error("expecting a full 16-bit address"));
- }
- let mut i = Self::char_to_hex(node, chars.next())? * 16;
- i += Self::char_to_hex(node, chars.next())?;
-
- Ok([i, 0])
+ bytes[0] = self.char_to_hex(chars.next())? * 16;
+ bytes[0] += self.char_to_hex(chars.next())?;
+ size = 1;
}
3 => {
- if !two_bytes_allowed {
- return Err(node.parser_error("only one byte of data is allowed here"));
- }
-
- let hi = Self::char_to_hex(node, chars.next())? * 16;
-
- let mut lo = Self::char_to_hex(node, chars.next())? * 16;
- lo += Self::char_to_hex(node, chars.next())?;
-
- Ok([lo, hi])
+ bytes[1] = self.char_to_hex(chars.next())?;
+ bytes[0] = self.char_to_hex(chars.next())? * 16;
+ bytes[0] += self.char_to_hex(chars.next())?;
+ size = 2;
}
4 => {
- if !two_bytes_allowed {
- return Err(node.parser_error("only one byte of data is allowed here"));
- }
-
- let mut hi = Self::char_to_hex(node, chars.next())? * 16;
- hi += Self::char_to_hex(node, chars.next())?;
-
- let mut lo = Self::char_to_hex(node, chars.next())? * 16;
- lo += Self::char_to_hex(node, chars.next())?;
-
- Ok([lo, hi])
- }
- _ => {
- if two_bytes_allowed {
- Err(node.parser_error("expecting a number of 1 to 4 hexadecimal digits"))
- } else if exactly_two_bytes {
- Err(node.parser_error("expecting a number of 4 hexadecimal digits"))
- } else {
- Err(node.parser_error("expecting a number of 2 hexadecimal digits"))
- }
+ bytes[1] = self.char_to_hex(chars.next())? * 16;
+ bytes[1] += self.char_to_hex(chars.next())?;
+ bytes[0] = self.char_to_hex(chars.next())? * 16;
+ bytes[0] += self.char_to_hex(chars.next())?;
+ size = 2;
}
+ _ => return Err(self.parser_error("expecting a number of 1 to 4 hexadecimal digits")),
}
+
+ Ok(Bundle {
+ bytes,
+ size,
+ address: 0,
+ cycles: 0,
+ affected_on_page: false,
+ })
}
- fn char_to_hex(node: &PString, oc: Option<char>) -> Result<u8> {
+ fn char_to_hex(&mut self, oc: Option<char>) -> Result<u8> {
match oc {
Some(c) => match c.to_digit(16) {
Some(c) => Ok(c as u8),
- None => Err(node.parser_error("could not convert digit to hexadecimal")),
+ None => Err(self.parser_error("could not convert digit to hexadecimal")),
},
- None => Err(node.parser_error("digit out of bounds")),
+ None => Err(self.parser_error("digit out of bounds")),
}
}
- fn parse_decimal_from(string: &str, node: &PString) -> Result<u8> {
+ fn evaluate_binary(&mut self, node: Box<PNode>) -> Result<Bundle> {
+ let string = node.value.value.as_str();
let mut value = 0;
- let mut shift = 1;
-
- if string.is_empty() {
- return Err(node.parser_error("empty decimal literal"));
- }
+ let mut shift = 0;
for c in string.chars().rev() {
- if shift > 100 {
- return Err(node.parser_error("decimal value is too big"));
- }
- if c != '0' {
- match c.to_digit(10) {
- Some(digit) => {
- value += digit * shift;
- }
- None => {
- return Err(
- node.parser_error(format!("'{}' is not a decimal value", c).as_str())
- )
- }
- }
- }
-
- shift *= 10;
- }
- if value > 255 {
- return Err(node.parser_error("decimal value is too big"));
- }
-
- Ok(value as u8)
- }
-
- // Advances `self.column` until a non-whitespace character is found. Returns
- // false if the line can be skipped entirely, true otherwise.
- fn skip_whitespace(&mut self, line: &str) -> bool {
- for c in line.get(self.column..).unwrap_or("").chars() {
- if !c.is_whitespace() {
- if c == ';' {
- return false;
- }
- return true;
- }
-
- self.column += 1;
- }
-
- true
- }
-
- // Returns a PString object which holds the information for an identifier.
- //
- // NOTE: this function assumes that `self.column` points to a non-whitespace
- // character.
- fn parse_identifier(&mut self, line: &str) -> Option<PString> {
- let column = self.column;
-
- // For the general case we just need to iterate until a whitespace
- // character or an inline comment is found. Then our PString object
- // is merely whatever is on the column..self.column range.
- for c in line.get(column..).unwrap_or("").chars() {
- if c.is_whitespace() || c == ';' {
- let range = Range {
- start: column,
- end: self.column,
- };
-
- return Some(PString {
- value: String::from(line.get(range.clone()).unwrap_or("").trim()),
- line: self.line,
- range,
- });
+ if c == '1' {
+ let val = 1 << shift;
+ value += val;
+ } else if c != '0' {
+ return Err(
+ self.parser_error(format!("bad binary format for '{}'", string).as_str())
+ );
}
- self.column += 1;
+ shift += 1;
}
- // Otherwise, we might be at a point whether there is nothing (e.g. an
- // empty line), or the line is merely the identifier (e.g. instruction
- // with implied addressing).
- let range = Range {
- start: column,
- end: line.len(),
- };
- let id = String::from(line.get(range.clone()).unwrap_or("").trim());
- if id.is_empty() {
- None
+ if shift < 8 {
+ Err(self.parser_error("missing binary digits to get a full byte"))
+ } else if shift > 8 {
+ Err(self.parser_error("too many binary digits for a single byte"))
} else {
- Some(PString {
- value: id,
- line: self.line,
- range,
+ Ok(Bundle {
+ bytes: [value as u8, 0, 0],
+ size: 1,
+ address: 0,
+ cycles: 0,
+ affected_on_page: false,
})
}
}
- // Given a PString object which acts as the identifier, try to parse the
- // rest of the line depending on whether it's an instruction (e.g. `adc
- // $20`), a control statement (e.g. `.macro whatever`) or a general
- // statement (e.g. `Var = $10`).
- fn parse_from_identifier(&mut self, id: PString, line: &str) -> Result<()> {
- if id.value.starts_with('.') {
- // If the statement starts with a '.', it's guaranteed to be a
- // control statement.
- self.parse_control(id, line)
- } else {
- // Otherwise, we will parse it either as an instruction or a general
- // statement depending on whether the parsed identifier is a valid
- // instruction mnemonic or not.
- match INSTRUCTIONS.get(&id.value) {
- Some(_instr) => self.parse_instruction(id, line),
- None => self.parse_statement(id, line),
- }
+ fn evaluate_decimal(&mut self, node: Box<PNode>) -> Result<Bundle> {
+ let string = node.value.value.as_str();
+ if string.is_empty() {
+ return Err(self.parser_error("empty decimal literal"));
}
- }
- fn parse_control(&mut self, mut id: PString, line: &str) -> Result<()> {
- self.skip_whitespace(line);
-
- // Try to handle arguments passed to the control statement.
- let mut args: Vec<String> = vec![];
- if let Some(open) = line.find(|c: char| c == '(') {
- match line.find(|c: char| c == ')') {
- Some(close) => {
- id.value = id
- .value
- .get(..open)
- .unwrap_or(id.value.as_str())
- .to_string();
- id.range.end = open;
-
- args = line
- .get(open + 1..close)
- .unwrap_or(" ")
- .split(',')
- .map(|w| w.trim().to_string())
- .collect::<Vec<_>>();
- }
- None => {
- return Err(id.parser_error(format!("open parenthesis on macro call").as_str()))
+ match self.do_evaluate_decimal(string) {
+ Ok(val) => Ok(val),
+ Err(e) => {
+ if e.parse {
+ Err(e)
+ } else {
+ self.fetch_variable(string)
}
}
}
-
- match id.value.to_lowercase().as_str() {
- ".scope" => self.parse_scope_definition(&id, line),
- ".endscope" => self.parse_scope_end(&id),
- ".segment" => self.parse_segment_definition(&id, line),
- ".byte" | ".db" => self.parse_literal_bytes(&id, line, false),
- ".word" | ".dw" | ".addr" => self.parse_literal_bytes(&id, line, true),
- ".proc" => self.parse_proc_definition(&id, line),
- ".endproc" => self.parse_proc_end(&id),
- ".macro" => self.parse_macro_definition(&id, line),
- ".endmacro" => self.parse_macro_end(&id),
- ".hibyte" => self.parse_hi_lo_byte(&id, &args, true),
- ".lobyte" => self.parse_hi_lo_byte(&id, &args, false),
- _ => {
- return Err(
- id.parser_error(format!("unknown control statement '{}'", id.value).as_str())
- )
- }
- }
}
- fn parse_hi_lo_byte(&mut self, id: &PString, args: &Vec<String>, hi: bool) -> Result<()> {
- if args.len() > 1 {
- return Err(id.parser_error(
- format!(
- "only 1 argument was expected, but {} were passed",
- args.len()
- )
- .as_str(),
- ));
- }
-
- println!("{:#?} -- {:#?} -- {}", id, args, hi);
-
- Ok(())
- }
-
- fn parse_macro_definition(&mut self, id: &PString, line: &str) -> Result<()> {
- self.skip_whitespace(line);
-
- let identifier = self.fetch_identifier(id, line)?;
- if identifier.is_reserved() {
- return Err(identifier.parser_error(
- format!(
- "cannot use reserved name '{}' for proc name",
- identifier.value
- )
- .as_str(),
- ));
- }
-
- self.mapping.current_macro = Some(identifier.value.clone());
- self.mapping.macros.entry(identifier.value).or_default();
- Ok(())
- }
-
- fn parse_macro_end(&mut self, id: &PString) -> Result<()> {
- match self.mapping.current_macro {
- Some(_) => self.mapping.current_macro = None,
- None => {
- return Err(id.parser_error(
- format!("bad `.endmacro`: we are not inside of a macro definition").as_str(),
- ))
- }
- }
-
- Ok(())
- }
-
- fn parse_proc_definition(&mut self, id: &PString, line: &str) -> Result<()> {
- self.skip_whitespace(line);
-
- let identifier = self.fetch_identifier(id, line)?;
- if identifier.is_reserved() {
- return Err(identifier.parser_error(
- format!(
- "cannot use reserved name '{}' for proc name",
- identifier.value
- )
- .as_str(),
- ));
- }
-
- // Insert the given identifier into the context.
- if let Some(entry) = self.context.current_mut() {
- match entry.entry(identifier.value.clone()) {
- Entry::Occupied(e) => {
- return Err(ParseError {
- line: self.line,
- message: format!(
- "proc '{}' already exists for this context: it was previously defined in line {}",
- id.value, e.get().node.line),
- })
+ fn fetch_variable(&mut self, mut string: &str) -> Result<Bundle> {
+ // Get the context that might be being referenced.
+ let ctxt = match string.find("::") {
+ Some(_) => {
+ let tctxt = string.rsplit_once("::").unwrap_or(("", ""));
+ if tctxt.0.is_empty() {
+ self.context.current()
+ } else {
+ string = tctxt.1;
+ self.context.find(tctxt.0)
}
- Entry::Vacant(e) => e.insert(PValue {
- node: PString {
- value: identifier.value.clone(),
- line: self.line,
- range: Range {
- start: id.range.start,
- end: id.range.end,
- },
- },
- value: 0,
- label: true,
- }),
- };
- }
-
- // And add the node so it's picked up later.
- self.mapping.push(Node::Label(Label {
- value: identifier.value.to_string(),
- }));
-
- // TODO: lol
- self.context.push_stack(&identifier.value);
-
- self.mapping.push(Node::Scoped(Scoped {
- identifier: identifier.clone(),
- start: true,
- }));
-
- Ok(())
- }
-
- fn parse_proc_end(&mut self, id: &PString) -> Result<()> {
- if !self.context.pop() {
- return Err(id.parser_error("missmatched '.endproc': there is no proc to end"));
- }
- self.mapping.push(Node::Scoped(Scoped {
- identifier: PString::new(),
- start: false,
- }));
-
- Ok(())
- }
-
- fn parse_segment_definition(&mut self, id: &PString, line: &str) -> Result<()> {
- self.skip_whitespace(line);
-
- let identifier = self.fetch_possibly_quoted_identifier(id, line)?;
- self.mapping.switch(&identifier)?;
-
- Ok(())
- }
-
- fn parse_scope_definition(&mut self, id: &PString, line: &str) -> Result<()> {
- self.skip_whitespace(line);
-
- let identifier = self.fetch_identifier(id, line)?;
- if identifier.is_reserved() {
- return Err(identifier.parser_error(
- format!("cannot use reserved name '{}'", identifier.value).as_str(),
- ));
- }
- self.context.push(&identifier.value);
- self.mapping.push(Node::Scoped(Scoped {
- identifier,
- start: true,
- }));
-
- Ok(())
- }
-
- fn parse_scope_end(&mut self, id: &PString) -> Result<()> {
- if !self.context.pop() {
- return Err(id.parser_error("missmatched '.endscope': there is no scope to end"));
- }
- self.mapping.push(Node::Scoped(Scoped {
- identifier: PString::new(),
- start: false,
- }));
-
- Ok(())
- }
-
- fn parse_literal_bytes(
- &mut self,
- node: &PString,
- line: &str,
- two_bytes_allowed: bool,
- ) -> Result<()> {
- loop {
- self.skip_whitespace(line);
-
- match line.chars().nth(self.column) {
- Some(byte) => {
- let needle = if byte == '\'' {
- self.column += 1;
- self.skip_whitespace(line);
- '\''
- } else if byte == '"' {
- self.column += 1;
- self.skip_whitespace(line);
- '"'
- } else {
- ','
- };
-
- // Find the index of the needle. If it cannot be found, try
- // to find the first whitespace (e.g. to ditch out inline
- // comments or other artifacts). If neither of these are
- // found, it will simply return the end of the string.
- //
- // TODO: instead of ditching out what's right of the first
- // whitespace, try to error out on weird scenarios.
- let needle_idx = line
- .get(self.column..)
- .unwrap_or("")
- .find(|c: char| c == needle);
- let idx = match needle_idx {
- Some(v) => v,
- None => line
- .get(self.column..)
- .unwrap_or("")
- .find(|c: char| c.is_whitespace())
- .unwrap_or(line.len() - self.column),
- };
-
- // If this is the last character, the needle was a quote and
- // the last char is not the needle, then it means that the
- // quote was left open. Complain about this as well.
- if idx == line.len() - self.column {
- if line.chars().nth(idx).unwrap_or(' ') != needle
- && (needle == '"' || needle == '\'')
- {
- return Err(node.parser_error("non-terminated quote for byte literal"));
- }
- }
-
- // Now we have our string. Before pushing it, though, there
- // is a special case for alphabetic literals that need to be
- // translated.
- let string = line.get(self.column..self.column + idx).unwrap_or(" ");
- let mut bytes: [u8; 2] = [0, 0];
- if string.len() == 1 && string.chars().nth(0).unwrap().is_ascii_alphabetic() {
- let v = Vec::from(string);
- bytes[0] = v[0];
- }
+ }
+ None => self.context.current(),
+ };
- // NOTE: for now we push an incomplete literal. We need the
- // first pass to fill the context and then a second pass
- // will evaluate each literal as needed (e.g. replacing
- // values from variables being used in this literal).
- self.mapping.push(Node::Literal(Literal {
- identifier: PString {
- value: string.to_owned(),
- line: self.line,
- range: Range {
- start: self.column,
- end: self.column + idx,
- },
- },
- size: if two_bytes_allowed { 2 } else { 1 },
- bytes,
- resolved: true,
- }));
-
- self.column += idx;
- for c in line.get(self.column..).unwrap_or(" ").chars() {
- if c == ',' {
- break;
- }
- if c == ';' {
- return Ok(());
- }
- self.column += 1;
+ // println!("{:#?}", self.context);
+ // println!("{:#?}", ctxt);
+
+ match ctxt {
+ Some(hash) => {
+ match hash.get(string) {
+ Some(var) => {
+ // TODO
+ // If this is just a memory address (e.g.
+ // label), then just return it as is.
+ // if var.label {
+ // return Ok((node.clone(), false));
+ // }
+ Ok(var.value.clone())
}
- self.column += 1;
- self.skip_whitespace(line);
- }
- None => break,
- };
- }
-
- Ok(())
- }
-
- fn fetch_identifier(&mut self, id: &PString, line: &str) -> Result<PString> {
- let idx = line
- .get(self.column..)
- .unwrap_or(" ")
- .find(|c: char| c.is_whitespace());
-
- match idx {
- Some(offset) => {
- let end = self.column + offset;
- let rest = line.get(end..).unwrap_or("").trim();
- if !rest.is_empty() {
- if rest.chars().nth(0).unwrap_or(' ') != ';' {
- return Err(id.parser_error(
- "there should not be any further content besides the identifier",
- ));
+ None => {
+ Err(self.parser_error(format!("unknown variable '{}'", string).as_str()))
}
}
- Ok(PString {
- value: line.get(self.column..end).unwrap_or(" ").trim().to_string(),
- line: self.line,
- range: Range {
- start: self.column,
- end,
- },
- })
}
- None => Ok(PString {
- value: line.get(self.column..).unwrap_or(" ").trim().to_string(),
- line: self.line,
- range: Range {
- start: self.column,
- end: line.len(),
- },
- }),
+ None => Err(self.parser_error(format!("unknown scope '{}'", "Global").as_str())),
}
}
- fn fetch_possibly_quoted_identifier(&mut self, id: &PString, line: &str) -> Result<PString> {
- let mut identifier = self.fetch_identifier(id, line)?;
-
- if identifier.value.starts_with('\'') || identifier.value.starts_with('`') {
- return Err(id.parser_error("use double quotes for the segment identifier instead"));
- } else if identifier.value.starts_with('"') {
- identifier.value = match identifier
- .value
- .get(1..(identifier.range.end - identifier.range.start - 1))
- {
- Some(v) => v.to_string(),
- None => return Err(id.parser_error("could not fetch quoted identifier")),
- };
- if identifier.value.contains('"') {
- return Err(id.parser_error("do not use double quotes inside of the identifier"));
- }
- identifier.range.start += 1;
- identifier.range.end -= 1;
- }
+ fn do_evaluate_decimal(&mut self, string: &str) -> Result<Bundle> {
+ let mut value = 0;
+ let mut shift = 1;
- Ok(identifier)
- }
+ if string.is_empty() {
+ return Err(self.parser_error("empty decimal literal"));
+ }
- fn parse_statement(&mut self, id: PString, line: &str) -> Result<()> {
- if id.value.chars().nth(self.column - 1).unwrap_or(' ') == ':' {
- self.parse_label(id, line)
- } else {
- match self.mapping.macros.get_mut(&id.value) {
- Some(nodes) => {
- for node in nodes {
- self.mapping
- .nodes
- .get_mut(&self.mapping.current)
- .unwrap()
- .push(node.clone());
+ for c in string.chars().rev() {
+ if shift > 100 {
+ return Err(self.parser_error("decimal value is too big"));
+ }
+ if c != '0' {
+ match c.to_digit(10) {
+ Some(digit) => {
+ value += digit * shift;
+ }
+ None => {
+ return Err(ParseError {
+ line: self.line,
+ message: format!("'{}' is not a decimal value", c),
+ parse: false,
+ });
}
- Ok(())
}
- None => self.parse_assignment(id, line),
}
- }
- }
-
- fn parse_label(&mut self, id: PString, _line: &str) -> Result<()> {
- let name = &id.value.as_str()[..id.value.len() - 1].to_string();
-
- // Forbid weird scenarios.
- if name.contains("::") {
- return Err(id.parser_error(
- format!(
- "the label '{}' is scoped: do not declare variables this way",
- id.value
- )
- .as_str(),
- ));
- }
-
- // Insert the given label into the context.
- if let Some(entry) = self.context.current_mut() {
- match entry.entry(name.clone()) {
- Entry::Occupied(e) => {
- return Err(ParseError {
- line: self.line,
- message: format!(
- "label '{}' already exists for this context: it was previously defined in line {}",
- id.value, e.get().node.line),
- })
- }
- Entry::Vacant(e) => e.insert(PValue {
- node: PString {
- value: name.clone(),
- line: self.line,
- range: Range {
- start: id.range.start,
- end: id.range.end,
- },
- },
- value: 0,
- label: true,
- }),
- };
- }
-
- // And add the node so it's picked up later.
- self.mapping.push(Node::Label(Label {
- value: name.to_string(),
- }));
- Ok(())
- }
- fn parse_assignment(&mut self, id: PString, line: &str) -> Result<()> {
- // You cannot assign into a name which is reserved.
- if id.is_reserved() {
- return Err(
- id.parser_error(format!("cannot use reserved name '{}'", id.value).as_str())
- );
- }
-
- // To avoid problems down the line, you cannot assign into names which
- // are proper hexadecimal values.
- if Self::parse_hex_from(&id.value, &id, true, false, false).is_ok() {
- return Err(id.parser_error(
- format!(
- "cannot use names which are valid hexadecimal values such as '{}'",
- id.value
- )
- .as_str(),
- ));
- }
-
- // You cannot assign into scoped names: declare them into their
- // respective scopes instead.
- if id.value.contains("::") {
- return Err(id.parser_error(
- format!(
- "the name '{}' is scoped: do not declare variables this way",
- id.value
- )
- .as_str(),
- ));
- }
-
- // Skip whitespaces and make sure that we have a '=' sign.
- self.skip_whitespace(line);
- if line.chars().nth(self.column).unwrap_or(' ') != '=' {
- return Err(self.parser_error(format!("unknown instruction '{}'", id.value).as_str()));
- }
-
- // Skip the '=' sign and any possible whitespaces.
- self.column += 1;
- if !self.skip_whitespace(line) {
- return Err(self.parser_error("incomplete assignment"));
- }
-
- let l = String::from(line.get(self.column..).unwrap_or("").trim());
- if l.is_empty() {
- return Err(self.parser_error("incomplete assignment"));
+ shift *= 10;
}
-
- // The `Context` struct pretty much guarantees that `current` and
- // `current_mut` will return something, so it's safe to ignore a
- // `None`.
- if let Some(entry) = self.context.current_mut() {
- match entry.entry(id.value.clone()) {
- Entry::Occupied(e) => {
- return Err(ParseError {
- line: self.line,
- message: format!(
- "variable '{}' is being re-assigned: it was previously defined in line {}",
- id.value, e.get().node.line),
- })
- }
- Entry::Vacant(e) => e.insert(PValue {
- node: PString {
- value: l,
- line: self.line,
- range: Range {
- start: id.range.start,
- end: line.len(),
- },
- },
- value: 0,
- label: false,
- }),
- };
+ if value > 255 {
+ return Err(self.parser_error("decimal value is too big"));
}
- Ok(())
+ Ok(Bundle {
+ bytes: [value as u8, 0, 0],
+ size: 1,
+ address: 0,
+ cycles: 0,
+ affected_on_page: false,
+ })
}
- fn parse_instruction(&mut self, id: PString, line: &str) -> Result<()> {
- // Parse the instruction into a `Generic` node.
- let node = self.get_generic_instruction_node(id, line)?;
-
- // Make sure that there is no dangling content.
- for c in line.get(self.column..).unwrap_or("").chars() {
- if c == ';' {
- break;
- }
- if !c.is_whitespace() {
- return Err(self.parser_error("only one statement is allowed per line"));
- }
- self.column += 1;
+ fn evaluate_hilo_byte(&mut self, args: Vec<Box<PNode>>, hi: bool) -> Result<Bundle> {
+ if args.len() != 1 {
+ return Err(self.parser_error("wrong number of arguments: expecting exactly one"));
}
- // And push a new `Instruction` object based on the parsed node. Note
- // that this instruction will be incomplete: we first need to evaluate
- // variables first in this context to be able to fully parse this
- // object.
- self.push_incomplete_instruction_from(node)
- }
-
- // Returns a `Generic` node with the contents that can be parsed with the
- // rest of the `line` and assuming that this is an assembly instruction
- // which is identified by `id`.
- fn get_generic_instruction_node(&mut self, id: PString, line: &str) -> Result<Generic> {
- // First of all, make sure that we are at a non-whitespace character.
- self.skip_whitespace(line);
-
- // Instructions have a character which split the left and the right arms
- // of the instruction. This is the character that we will use in order
- // to stop on the first loop.
- let needle_char = match line.chars().nth(self.column) {
- Some(c) => {
- if c == '(' {
- ')'
- } else {
- ','
- }
- }
- None => ',',
- };
-
- let mut column = self.column;
- let mut left = None;
- let mut right = None;
- let mut found = false;
-
- // First of the two loops: fetch the left arm. It iterates
- // until the needle_char is found and then initializes the `left`
- // variable with the fetched contents.
- for c in line.get(column..).unwrap_or("").chars() {
- if c == ';' {
- break;
- }
- if c == needle_char {
- self.init_positioned_maybe(&mut left, line, column, self.column);
-
- for inner in line.get(self.column..).unwrap_or("").chars() {
- if inner.is_whitespace() || inner == ';' {
- break;
- }
- self.column += 1;
- }
-
- found = true;
- break;
- }
-
- self.column += 1;
+ let val = self.evaluate_node(args.first().unwrap().clone())?;
+ if val.size < 1 {
+ let s = if hi { ".hibyte" } else { ".lobyte" };
+ return Err(self.parser_error(format!("empty value for {}", s).as_str()));
}
- // If the previous loop found the needle, the we might have a right arm.
- // Otherwise, if the needle was not found but the end of the line was
- // reached, we might still need to pick up the contents that were not
- // saved in the previous loop.
- if found {
- // Ready the `column` for the right arm.
- self.skip_whitespace(line);
- column = self.column;
-
- // Second loop: fetch the right arm. This time it iterates until a
- // whitespace character or ';' is found.
- for c in line.get(column..).unwrap_or("").chars() {
- if c == ';' {
- self.init_positioned_maybe(&mut right, line, column, self.column);
- break;
- }
- self.column += 1;
+ let b = if hi {
+ if val.size == 1 {
+ val.bytes[0]
+ } else {
+ val.bytes[1]
}
-
- // Similarly to the first loop, if this second one reached the end
- // without finding a whitespace or a ';' character, make sure that this
- // content is not ignored.
- self.init_positioned_maybe(&mut right, line, column, self.column);
} else {
- self.init_positioned_maybe(&mut left, line, column, self.column);
- }
+ val.bytes[0]
+ };
- Ok(Generic {
- identifier: id,
- left,
- right,
+ Ok(Bundle {
+ bytes: [b, 0, 0],
+ size: 1,
+ address: 0,
+ cycles: 0,
+ affected_on_page: false,
})
}
- // Initialize the PString object `p` with the contents of
- // `line.get(left..right)` unless it has already a `Some` value or the
- // fetched contents would result in an empty string.
- fn init_positioned_maybe(
- &mut self,
- p: &mut Option<PString>,
- line: &str,
- left: usize,
- right: usize,
- ) {
- if !p.is_none() || left == right {
- return;
- }
+ fn evaluate_scope_definition(&mut self, node: Box<PNode>) -> Result<Bundle> {
+ println!("{:#?}", node);
+ match node.left {
+ Some(identifier) => {
+ self.context.push(&identifier.value.value);
+ // TODO: mapping?
- let string = String::from(line.get(left..right).unwrap_or("").trim());
- if !string.is_empty() {
- *p = Some(PString {
- value: string,
- line: self.line,
- range: Range {
- start: left,
- end: right,
- },
- });
+ Ok(Bundle::new())
+ }
+ None => return Err(self.parser_error("scope definition with no identifier")),
}
}
- fn push_incomplete_instruction_from(&mut self, node: Generic) -> Result<()> {
- let mut instr = Instruction::from(&node.identifier.value);
- instr.left = node.left;
- instr.right = node.right;
-
- self.mapping.push(Node::Instruction(instr));
- Ok(())
- }
+ fn evaluate_scope_end(&mut self) -> Result<Bundle> {
+ if !self.context.pop() {
+ return Err(self.parser_error("missmatched '.endscope': there is no scope to end"));
+ }
+
+ // TODO: mapping?
+
+ Ok(Bundle::new())
+ }
+
+ // pub fn assemble(&mut self, reader: impl Read) -> Result<Vec<&dyn Encodable>> {
+ // let mut instructions: Vec<&dyn Encodable> = vec![];
+
+ // self.assemble_nodes(reader)?;
+
+ // let mut idx: usize = 0;
+ // for segment in &self.mapping.segments {
+ // let mut size: usize = 0;
+
+ // while idx < segment.start.into() {
+ // match &segment.fill {
+ // Some(fill) => instructions.push(fill),
+ // None => instructions.push(&Fill { value: 0x00 }),
+ // }
+ // idx += 1;
+ // }
+
+ // for node in &self.mapping.nodes[&segment.name] {
+ // match node {
+ // Node::Instruction(instr) => {
+ // instructions.push(instr);
+ // size += usize::from(instr.size());
+ // }
+ // Node::Literal(lit) => {
+ // instructions.push(lit);
+ // size += usize::from(lit.size());
+ // }
+ // _ => {}
+ // }
+ // }
+
+ // if size > segment.size {
+ // return Err(ParseError {
+ // line: 0,
+ // message: format!(
+ // "segment '{}' expected a size of '{}' bytes but '{}' bytes were produced instead",
+ // segment.name, size, segment.size
+ // ),
+ // });
+ // }
+ // idx += size;
+ // if segment.fill.is_none() {
+ // continue;
+ // }
+
+ // while size < segment.size {
+ // instructions.push(segment.fill.as_ref().unwrap());
+ // size += 1;
+ // idx += 1;
+ // }
+ // }
+
+ // Ok(instructions)
+ // }
+
+ // pub fn evaluate(&mut self) -> Result<()> {
+ // for segment in &self.mapping.segments {
+ // for node in self.mapping.nodes.get_mut(&segment.name).unwrap() {
+ // match node {
+ // Node::Instruction(instr) => {
+ // Self::update_instruction_with_context(instr, &self.context)?;
+ // instr.address = segment.start;
+
+ // self.offsets
+ // .entry(segment.name.clone())
+ // .and_modify(|value| {
+ // instr.address += *value as u16;
+ // *value += usize::from(instr.size())
+ // })
+ // .or_insert(instr.size().into());
+ // }
+ // Node::Scoped(scope) => {
+ // if scope.start {
+ // self.context.push(&scope.identifier.value);
+ // } else {
+ // _ = self.context.pop();
+ // }
+ // }
+ // Node::Literal(literal) => {
+ // Self::update_literal_with_context(literal, &self.context)?;
+ // self.offsets
+ // .entry(segment.name.clone())
+ // .and_modify(|value| *value += usize::from(literal.size()))
+ // .or_insert(literal.size().into());
+ // }
+ // Node::Label(label) => {
+ // let address =
+ // usize::from(segment.start) + self.offsets.get(&segment.name).unwrap();
+
+ // self.context
+ // .current_mut()
+ // .unwrap()
+ // .entry(label.value.clone())
+ // .and_modify(|e| e.value = address);
+ // }
+ // _ => {}
+ // }
+ // }
+ // }
+
+ // Ok(())
+ // }
+
+ // // TODO: oh boy...
+ // pub fn resolve_labels(&mut self) -> Result<()> {
+ // for segment in &self.mapping.segments {
+ // for node in self.mapping.nodes.get_mut(&segment.name).unwrap() {
+ // match node {
+ // Node::Instruction(instr) => {
+ // if !instr.resolved {
+ // match &instr.left {
+ // Some(pstring) => {
+ // match self.context.current().unwrap().get(&pstring.value) {
+ // Some(entry) => {
+ // if instr.mode == AddressingMode::Absolute {
+ // let bytes = entry.value.to_le_bytes();
+ // instr.bytes = [bytes[0], bytes[1]];
+ // } else {
+ // let diff: isize = entry.value as isize
+ // - (instr.address as isize + 2);
+ // if diff < -128 || diff > 127 {
+ // return Err(instr.mnemonic.parser_error(
+ // format!("relative addressing out of range")
+ // .as_str(),
+ // ));
+ // }
+ // let bytes = diff.to_le_bytes();
+ // instr.bytes = [bytes[0], 0];
+ // }
+ // }
+ // None => {
+ // return Err(instr.mnemonic.parser_error(
+ // format!("label '{}' not found", pstring.value)
+ // .as_str(),
+ // ))
+ // }
+ // }
+ // }
+ // None => {
+ // return Err(instr.mnemonic.parser_error(
+ // format!("there is no label for the given jump instruction")
+ // .as_str(),
+ // ))
+ // }
+ // }
+ // }
+ // }
+ // Node::Literal(literal) => {
+ // if !literal.resolved {
+ // match self
+ // .context
+ // .current()
+ // .unwrap()
+ // .get(&literal.identifier.value)
+ // {
+ // Some(entry) => {
+ // let bytes = entry.value.to_le_bytes();
+ // literal.bytes = [bytes[0], bytes[1]];
+ // }
+ // None => {
+ // return Err(literal.identifier.parser_error(
+ // format!(
+ // "'{}' is neither a known variable or label at this scope",
+ // literal.identifier.value
+ // )
+ // .as_str(),
+ // ))
+ // }
+ // }
+ // }
+ // }
+ // _ => {}
+ // }
+ // }
+ // }
+
+ // Ok(())
+ // }
+
+ // fn update_instruction_with_context(instr: &mut Instruction, context: &Context) -> Result<()> {
+ // // To keep things simple, we remove out the `implied` case and we parse
+ // // further with a known `Some` value for the base algorithm implemented
+ // // in `update_instruction_and_bytes`.
+ // if instr.left.is_some() {
+ // Self::update_addressing_and_bytes(instr, context)?;
+ // } else {
+ // instr.mode = AddressingMode::Implied;
+ // }
+
+ // // Now that we have the addressing mode and the bytes, we can fill out
+ // // the rest of it by fetching the values on `INSTRUCTIONS`.
+ // match INSTRUCTIONS.get(&instr.mnemonic.value.to_lowercase()) {
+ // Some(entries) => match entries.get(&instr.mode) {
+ // Some(values) => {
+ // instr.cycles = values.cycles;
+ // instr.opcode = values.opcode;
+ // instr.size = values.size;
+ // instr.affected_on_page = values.affected_on_page;
+ // }
+ // None => {
+ // return Err(instr.mnemonic.parser_error(
+ // format!(
+ // "bad addressing mode '{}' for the instruction '{}'",
+ // &instr.mode, &instr.mnemonic.value
+ // )
+ // .as_str(),
+ // ));
+ // }
+ // },
+ // None => {
+ // return Err(instr.mnemonic.parser_error(
+ // format!("unknown instruction '{}'", &instr.mnemonic.value).as_str(),
+ // ));
+ // }
+ // }
+
+ // Ok(())
+ // }
+
+ // fn update_addressing_and_bytes(instr: &mut Instruction, context: &Context) -> Result<()> {
+ // // `unwrap()` is guaranteed to work by the caller.
+ // let left = instr.left.as_ref().unwrap();
+
+ // // We will first try to check if there's any variable involved on the
+ // // left arm and replace the string if so. This will greatly simplify
+ // // things down the line. That being said, there is a special reserved
+ // // case, which is the implied addressing by using "a". In this case, we
+ // // want to ensure that we assume an implied addressing and not a
+ // // variable named "a".
+ // if left.value.to_lowercase() == "a" {
+ // instr.mode = AddressingMode::Implied;
+ // } else {
+ // let (nleft, resolved) = Self::replace_variable(left, context)?;
+ // // TODO
+ // instr.resolved = resolved;
+ // if !resolved {
+ // if instr.mnemonic.value == "jmp" {
+ // instr.mode = AddressingMode::Absolute;
+ // } else {
+ // instr.mode = AddressingMode::RelativeOrZeropage;
+ // }
+ // }
+
+ // if nleft.value.starts_with('$') {
+ // // This is an address. At this point we should assume that the
+ // // left node contains the address itself, and that the right one
+ // // will contain whether there is indexing.
+
+ // let string = nleft.value.chars().as_str();
+ // instr.bytes = Self::parse_hex_from(string, &nleft, true, false, true)?;
+
+ // match &instr.right {
+ // Some(xy) => match xy.value.to_lowercase().as_str() {
+ // "x" => {
+ // if string.len() == 3 {
+ // instr.mode = AddressingMode::ZeropageIndexedX;
+ // } else {
+ // instr.mode = AddressingMode::IndexedX;
+ // }
+ // }
+ // "y" => {
+ // if string.len() == 3 {
+ // instr.mode = AddressingMode::ZeropageIndexedY;
+ // } else {
+ // instr.mode = AddressingMode::IndexedY;
+ // }
+ // }
+ // _ => return Err(xy.parser_error("index is neither X nor Y")),
+ // },
+ // None => {
+ // if string.len() == 3 {
+ // instr.mode = AddressingMode::RelativeOrZeropage;
+ // } else {
+ // instr.mode = AddressingMode::Absolute;
+ // }
+ // }
+ // }
+ // } else if nleft.value.starts_with('#') {
+ // // Immediate addressing in any case: hexadecimal, binary or
+ // // decimal. Hence, just figure out the character being used and
+ // // call the right function for it.
+
+ // let mut chars = nleft.value.chars();
+ // chars.next();
+ // let string = chars.as_str();
+
+ // instr.bytes = Self::parse_numeric(string, &nleft, false)?;
+ // instr.mode = AddressingMode::Immediate;
+ // } else if nleft.value.starts_with('(') {
+ // // Indirect addressing. In this case the left arm can be further
+ // // subdivided. That is, indirect X-indexing is represented like
+ // // so: `instr ($NN, x)`. Hence, first of all we have to figure
+ // // out whether there is a subdivision.
+
+ // let (left1, oleft2) = Self::split_left_arm(&nleft)?;
+ // match oleft2 {
+ // Some(left2) => {
+ // // There is subdivision. Thus, we have to assume
+ // // indirect X-indexing, which means that the right arm
+ // // should be None and that the right side of the left
+ // // node must match the X register. Other than that, the
+ // // address being referenced must be zero page.
+ // if instr.right.is_some() {
+ // return Err(instr.right.as_ref().unwrap().parser_error(
+ // "bad indirect mode, expecting an indirect X-indexed addressing mode"
+ // ));
+ // }
+ // if left2.value.to_lowercase() != "x" {
+ // return Err(left2.parser_error(
+ // "the index in indirect X-indexed addressing must be X",
+ // ));
+ // }
+ // match Self::parse_hex_from(&left1.value, &left1, false, false, true) {
+ // Ok(bytes) => instr.bytes = bytes,
+ // Err(e) => {
+ // let msg = String::from(
+ // "when parsing an instruction with indirect X-indexed addressing: ",
+ // ) + &e.message;
+ // return Err(left1.parser_error(msg.as_str()));
+ // }
+ // }
+ // instr.mode = AddressingMode::IndirectX;
+ // }
+ // None => {
+ // // There is no subdivision on the left arm. Hence, if
+ // // there is something on the right arm then we must
+ // // assume indirect Y-index addressing, and if not then
+ // // it's indirect addressing with no indices involvved.
+ // if instr.right.is_some() {
+ // if instr.right.as_ref().unwrap().value.to_lowercase() != "y" {
+ // return Err(instr.right.as_ref().unwrap().parser_error(
+ // "the index in indirect Y-indexed addressing must be Y",
+ // ));
+ // }
+ // match Self::parse_hex_from(&left1.value, &left1, false, false, true) {
+ // Ok(bytes) => instr.bytes = bytes,
+ // Err(e) => {
+ // let msg = String::from(
+ // "when parsing an instruction with indirect Y-indexed addressing: ",
+ // ) + &e.message;
+ // return Err(left1.parser_error(msg.as_str()));
+ // }
+ // }
+ // instr.mode = AddressingMode::IndirectY;
+ // } else {
+ // instr.bytes =
+ // Self::parse_hex_from(&left1.value, &left1, true, true, true)?;
+ // instr.mode = AddressingMode::Indirect;
+ // }
+ // }
+ // }
+ // } else {
+ // // At this point all of the syntax cases have been exhausted:
+ // // the programmer messed up. From this point on we try to figure
+ // // out how they messed up.
+
+ // if nleft.value.starts_with('=') {
+ // return Err(instr.mnemonic.parser_error(
+ // format!(
+ // "cannot use '{}' in an assignment because it's a word reserved for an instruction mnemonic",
+ // instr.mnemonic.value
+ // ).as_str(),
+ // ));
+ // }
+ // // TODO:
+ // // instr.mode = AddressingMode::Absolute;
+ // // return Err(instr.mnemonic.parser_error(
+ // // format!(
+ // // "unknown addressing mode for instruction '{}'",
+ // // instr.mnemonic.value
+ // // )
+ // // .as_str(),
+ // // ));
+ // }
+ // }
+
+ // Ok(())
+ // }
+
+ // fn update_literal_with_context(literal: &mut Literal, context: &Context) -> Result<()> {
+ // // If it has already been set, skip it.
+ // // TODO: add a proper `is_set` thingie to it instead of this hack.
+ // if literal.bytes[0] != 0 || literal.bytes[1] != 0 {
+ // return Ok(());
+ // }
+
+ // // Evaluate any possible variable being used inside of this literal.
+ // let (evaled, resolved) = Self::replace_variable(&literal.identifier, context)?;
+
+ // // It may happen that the literal is just a label that is to be resolved
+ // // in the future. If so, let's leave early.
+ // literal.resolved = resolved;
+ // if !resolved {
+ // return Ok(());
+ // }
+
+ // // Parse the numeric value after a possible variable has been replaced.
+ // let two_bytes_allowed = literal.size == 2;
+ // let res = Self::parse_numeric(
+ // evaled.value.as_str(),
+ // &literal.identifier,
+ // two_bytes_allowed,
+ // );
+
+ // // And finally assign the computed bytes.
+ // match res {
+ // Ok(bytes) => {
+ // literal.bytes = bytes;
+ // Ok(())
+ // }
+ // Err(e) => {
+ // let msg = String::from("when parsing a data literal: ") + &e.message;
+ // Err(literal.identifier.parser_error(msg.as_str()))
+ // }
+ // }
+ // }
+
+ // fn parse_numeric(string: &str, node: &PString, two_bytes_allowed: bool) -> Result<[u8; 2]> {
+ // if string.starts_with('$') {
+ // Ok(Self::parse_hex_from(
+ // string,
+ // node,
+ // two_bytes_allowed,
+ // false,
+ // true,
+ // )?)
+ // } else if string.starts_with('%') {
+ // Ok([Self::parse_binary_from(string, node)?, 0])
+ // } else {
+ // Ok([Self::parse_decimal_from(string, node)?, 0])
+ // }
+ // }
+
+ // fn split_left_arm(node: &PString) -> Result<(PString, Option<PString>)> {
+ // let mut chars = node.value.chars();
+ // chars.next();
+ // let string = chars.as_str();
+
+ // match string.find(|c: char| c == ',') {
+ // Some(idx) => {
+ // let left1 = string.get(..idx).unwrap_or("").trim();
+ // let left2 = string.get(idx + 1..).unwrap_or("").trim();
+
+ // Ok((
+ // PString {
+ // value: left1.to_string(),
+ // line: node.line,
+ // range: Range {
+ // start: node.range.start + 1,
+ // end: node.range.start + 1 + left1.len(),
+ // },
+ // },
+ // Some(PString {
+ // value: left2.to_string(),
+ // line: node.line,
+ // range: Range {
+ // start: node.range.start + 1 + idx,
+ // end: node.range.start + 1 + idx + left2.len(),
+ // },
+ // }),
+ // ))
+ // }
+ // None => Ok((
+ // PString {
+ // value: string.to_string(),
+ // line: node.line,
+ // range: Range {
+ // start: node.range.start + 1,
+ // end: node.range.end,
+ // },
+ // },
+ // None,
+ // )),
+ // }
+ // }
+
+ // fn parse_binary_from(string: &str, node: &PString) -> Result<u8> {
+ // let mut value = 0;
+ // let mut shift = 0;
+
+ // for c in string.get(1..).unwrap_or("").chars().rev() {
+ // if c == '1' {
+ // let val = 1 << shift;
+ // value += val;
+ // } else if c != '0' {
+ // return Err(
+ // node.parser_error(format!("bad binary format for '{}'", string).as_str())
+ // );
+ // }
+
+ // shift += 1;
+ // }
+
+ // if shift < 8 {
+ // Err(node.parser_error("missing binary digits to get a full byte"))
+ // } else if shift > 8 {
+ // Err(node.parser_error("too many binary digits for a single byte"))
+ // } else {
+ // Ok(value)
+ // }
+ // }
+
+ // // TODO: returns if resolved
+ // fn replace_variable(node: &PString, context: &Context) -> Result<(PString, bool)> {
+ // match node
+ // .value
+ // .find(|c: char| c.is_alphabetic() || c == '_' || c == '@')
+ // {
+ // Some(idx) => {
+ // // Before doing any replacement, let's check the character
+ // // before the one that was found. In this case, if it was a
+ // // proper ASCII digit, then it cannot be a variable but it's
+ // // part of a numeric literal (e.g. '1A'): then just let the
+ // // different numeric parsing functions do their job.
+ // if idx > 0 {
+ // let prev = node.value.chars().nth(idx - 1).unwrap_or(' ');
+ // if prev.is_ascii_digit() {
+ // return Ok((node.clone(), true));
+ // }
+ // }
+
+ // // The variable might still be before an inner comma (e.g.
+ // // sta ($20, x)). We will assume that variables can happen
+ // // only before that.
+ // let end = node.value.find(',').unwrap_or(node.value.len());
+ // let mut string = node.value.get(idx..end).unwrap_or("");
+ // let tail = node.value.get(end..).unwrap_or("");
+
+ // // Get the context that might be being referenced.
+ // let ctxt = match string.find("::") {
+ // Some(_) => {
+ // let tctxt = string.rsplit_once("::").unwrap_or(("", ""));
+ // if tctxt.0.is_empty() {
+ // context.current()
+ // } else {
+ // string = tctxt.1;
+ // context.find(tctxt.0)
+ // }
+ // }
+ // None => context.current(),
+ // };
+
+ // match ctxt {
+ // Some(hash) => {
+ // // If there was a comma before the "variable" (i.e. idx >
+ // // end and hence string == ""), or this is just the regular
+ // // X or Y index, just return early.
+ // match string.to_lowercase().as_str() {
+ // "x" | "y" | "" => return Ok((node.clone(), true)),
+ // _ => {}
+ // }
+
+ // // It's not any of the indices, let's look for a match on
+ // // the current scope.
+ // match hash.get(string) {
+ // Some(var) => {
+ // // If this is just a memory address (e.g.
+ // // label), then just return it as is.
+ // if var.label {
+ // return Ok((node.clone(), false));
+ // }
+
+ // let value = String::from(node.value.get(..idx).unwrap_or(""))
+ // + var.node.value.as_str();
+ // Ok((
+ // PString {
+ // value: value.clone() + tail,
+ // line: node.line,
+ // range: Range {
+ // start: node.range.start,
+ // end: node.range.start + value.len(),
+ // },
+ // },
+ // true,
+ // ))
+ // }
+ // None => {
+ // // If a variable could not be found, check that
+ // // this is not a purely hexadecimal number (e.g.
+ // // 'AA'). If that's the case, then just return
+ // // its value.
+ // if Self::parse_hex_from(string, node, true, false, false).is_ok() {
+ // return Ok((node.clone(), true));
+ // }
+
+ // // We've tried hard to not assume the programmer
+ // // messing up, but there's no other way around
+ // // it: it's an "unknown variable" error.
+ // return Err(node.parser_error(
+ // format!("unknown variable '{}'", string).as_str(),
+ // ));
+ // }
+ // }
+ // }
+ // None => {
+ // Err(node.parser_error(format!("unknown scope '{}'", "Global").as_str()))
+ // }
+ // }
+ // }
+ // None => Ok((node.clone(), true)),
+ // }
+ // }
+
+ // fn parse_macro_definition(&mut self, id: &PString, line: &str) -> Result<()> {
+ // self.skip_whitespace(line);
+
+ // let identifier = self.fetch_identifier(id, line)?;
+ // if identifier.is_reserved() {
+ // return Err(identifier.parser_error(
+ // format!(
+ // "cannot use reserved name '{}' for proc name",
+ // identifier.value
+ // )
+ // .as_str(),
+ // ));
+ // }
+
+ // self.mapping.current_macro = Some(identifier.value.clone());
+ // self.mapping.macros.entry(identifier.value).or_default();
+ // Ok(())
+ // }
+
+ // fn parse_macro_end(&mut self, id: &PString) -> Result<()> {
+ // match self.mapping.current_macro {
+ // Some(_) => self.mapping.current_macro = None,
+ // None => {
+ // return Err(id.parser_error(
+ // format!("bad `.endmacro`: we are not inside of a macro definition").as_str(),
+ // ))
+ // }
+ // }
+
+ // Ok(())
+ // }
+
+ // fn parse_proc_definition(&mut self, id: &PString, line: &str) -> Result<()> {
+ // self.skip_whitespace(line);
+
+ // let identifier = self.fetch_identifier(id, line)?;
+ // if identifier.is_reserved() {
+ // return Err(identifier.parser_error(
+ // format!(
+ // "cannot use reserved name '{}' for proc name",
+ // identifier.value
+ // )
+ // .as_str(),
+ // ));
+ // }
+
+ // // Insert the given identifier into the context.
+ // if let Some(entry) = self.context.current_mut() {
+ // match entry.entry(identifier.value.clone()) {
+ // Entry::Occupied(e) => {
+ // return Err(ParseError {
+ // line: self.line,
+ // message: format!(
+ // "proc '{}' already exists for this context: it was previously defined in line {}",
+ // id.value, e.get().node.line),
+ // })
+ // }
+ // Entry::Vacant(e) => e.insert(PValue {
+ // node: PString {
+ // value: identifier.value.clone(),
+ // line: self.line,
+ // range: Range {
+ // start: id.range.start,
+ // end: id.range.end,
+ // },
+ // },
+ // value: 0,
+ // label: true,
+ // }),
+ // };
+ // }
+
+ // // And add the node so it's picked up later.
+ // self.mapping.push(Node::Label(Label {
+ // value: identifier.value.to_string(),
+ // }));
+
+ // // TODO: lol
+ // self.context.push_stack(&identifier.value);
+
+ // self.mapping.push(Node::Scoped(Scoped {
+ // identifier: identifier.clone(),
+ // start: true,
+ // }));
+
+ // Ok(())
+ // }
+
+ // fn parse_proc_end(&mut self, id: &PString) -> Result<()> {
+ // if !self.context.pop() {
+ // return Err(id.parser_error("missmatched '.endproc': there is no proc to end"));
+ // }
+ // self.mapping.push(Node::Scoped(Scoped {
+ // identifier: PString::new(),
+ // start: false,
+ // }));
+
+ // Ok(())
+ // }
+
+ // fn parse_segment_definition(&mut self, id: &PString, line: &str) -> Result<()> {
+ // self.skip_whitespace(line);
+
+ // let identifier = self.fetch_possibly_quoted_identifier(id, line)?;
+ // self.mapping.switch(&identifier)?;
+
+ // Ok(())
+ // }
+
+ // fn parse_scope_definition(&mut self, id: &PString, line: &str) -> Result<()> {
+ // self.skip_whitespace(line);
+
+ // let identifier = self.fetch_identifier(id, line)?;
+ // if identifier.is_reserved() {
+ // return Err(identifier.parser_error(
+ // format!("cannot use reserved name '{}'", identifier.value).as_str(),
+ // ));
+ // }
+ // self.context.push(&identifier.value);
+ // self.mapping.push(Node::Scoped(Scoped {
+ // identifier,
+ // start: true,
+ // }));
+
+ // Ok(())
+ // }
+
+ // fn parse_scope_end(&mut self, id: &PString) -> Result<()> {
+ // if !self.context.pop() {
+ // return Err(id.parser_error("missmatched '.endscope': there is no scope to end"));
+ // }
+ // self.mapping.push(Node::Scoped(Scoped {
+ // identifier: PString::new(),
+ // start: false,
+ // }));
+
+ // Ok(())
+ // }
+
+ // fn parse_literal_bytes(
+ // &mut self,
+ // node: &PString,
+ // line: &str,
+ // two_bytes_allowed: bool,
+ // ) -> Result<()> {
+ // loop {
+ // self.skip_whitespace(line);
+
+ // match line.chars().nth(self.column) {
+ // Some(byte) => {
+ // let needle = if byte == '\'' {
+ // self.column += 1;
+ // self.skip_whitespace(line);
+ // '\''
+ // } else if byte == '"' {
+ // self.column += 1;
+ // self.skip_whitespace(line);
+ // '"'
+ // } else {
+ // ','
+ // };
+
+ // // Find the index of the needle. If it cannot be found, try
+ // // to find the first whitespace (e.g. to ditch out inline
+ // // comments or other artifacts). If neither of these are
+ // // found, it will simply return the end of the string.
+ // //
+ // // TODO: instead of ditching out what's right of the first
+ // // whitespace, try to error out on weird scenarios.
+ // let needle_idx = line
+ // .get(self.column..)
+ // .unwrap_or("")
+ // .find(|c: char| c == needle);
+ // let idx = match needle_idx {
+ // Some(v) => v,
+ // None => line
+ // .get(self.column..)
+ // .unwrap_or("")
+ // .find(|c: char| c.is_whitespace())
+ // .unwrap_or(line.len() - self.column),
+ // };
+
+ // // If this is the last character, the needle was a quote and
+ // // the last char is not the needle, then it means that the
+ // // quote was left open. Complain about this as well.
+ // if idx == line.len() - self.column {
+ // if line.chars().nth(idx).unwrap_or(' ') != needle
+ // && (needle == '"' || needle == '\'')
+ // {
+ // return Err(node.parser_error("non-terminated quote for byte literal"));
+ // }
+ // }
+
+ // // Now we have our string. Before pushing it, though, there
+ // // is a special case for alphabetic literals that need to be
+ // // translated.
+ // let string = line.get(self.column..self.column + idx).unwrap_or(" ");
+ // let mut bytes: [u8; 2] = [0, 0];
+ // if string.len() == 1 && string.chars().nth(0).unwrap().is_ascii_alphabetic() {
+ // let v = Vec::from(string);
+ // bytes[0] = v[0];
+ // }
+
+ // // NOTE: for now we push an incomplete literal. We need the
+ // // first pass to fill the context and then a second pass
+ // // will evaluate each literal as needed (e.g. replacing
+ // // values from variables being used in this literal).
+ // self.mapping.push(Node::Literal(Literal {
+ // identifier: PString {
+ // value: string.to_owned(),
+ // line: self.line,
+ // range: Range {
+ // start: self.column,
+ // end: self.column + idx,
+ // },
+ // },
+ // size: if two_bytes_allowed { 2 } else { 1 },
+ // bytes,
+ // resolved: true,
+ // }));
+
+ // self.column += idx;
+ // for c in line.get(self.column..).unwrap_or(" ").chars() {
+ // if c == ',' {
+ // break;
+ // }
+ // if c == ';' {
+ // return Ok(());
+ // }
+ // self.column += 1;
+ // }
+ // self.column += 1;
+ // self.skip_whitespace(line);
+ // }
+ // None => break,
+ // };
+ // }
+
+ // Ok(())
+ // }
+
+ // fn fetch_identifier(&mut self, id: &PString, line: &str) -> Result<PString> {
+ // let idx = line
+ // .get(self.column..)
+ // .unwrap_or(" ")
+ // .find(|c: char| c.is_whitespace());
+
+ // match idx {
+ // Some(offset) => {
+ // let end = self.column + offset;
+ // let rest = line.get(end..).unwrap_or("").trim();
+ // if !rest.is_empty() {
+ // if rest.chars().nth(0).unwrap_or(' ') != ';' {
+ // return Err(id.parser_error(
+ // "there should not be any further content besides the identifier",
+ // ));
+ // }
+ // }
+ // Ok(PString {
+ // value: line.get(self.column..end).unwrap_or(" ").trim().to_string(),
+ // line: self.line,
+ // range: Range {
+ // start: self.column,
+ // end,
+ // },
+ // })
+ // }
+ // None => Ok(PString {
+ // value: line.get(self.column..).unwrap_or(" ").trim().to_string(),
+ // line: self.line,
+ // range: Range {
+ // start: self.column,
+ // end: line.len(),
+ // },
+ // }),
+ // }
+ // }
+
+ // fn fetch_possibly_quoted_identifier(&mut self, id: &PString, line: &str) -> Result<PString> {
+ // let mut identifier = self.fetch_identifier(id, line)?;
+
+ // if identifier.value.starts_with('\'') || identifier.value.starts_with('`') {
+ // return Err(id.parser_error("use double quotes for the segment identifier instead"));
+ // } else if identifier.value.starts_with('"') {
+ // identifier.value = match identifier
+ // .value
+ // .get(1..(identifier.range.end - identifier.range.start - 1))
+ // {
+ // Some(v) => v.to_string(),
+ // None => return Err(id.parser_error("could not fetch quoted identifier")),
+ // };
+ // if identifier.value.contains('"') {
+ // return Err(id.parser_error("do not use double quotes inside of the identifier"));
+ // }
+ // identifier.range.start += 1;
+ // identifier.range.end -= 1;
+ // }
+
+ // Ok(identifier)
+ // }
+
+ // fn parse_label(&mut self, id: PString, _line: &str) -> Result<()> {
+ // let name = &id.value.as_str()[..id.value.len() - 1].to_string();
+
+ // // Forbid weird scenarios.
+ // if name.contains("::") {
+ // return Err(id.parser_error(
+ // format!(
+ // "the label '{}' is scoped: do not declare variables this way",
+ // id.value
+ // )
+ // .as_str(),
+ // ));
+ // }
+
+ // // Insert the given label into the context.
+ // if let Some(entry) = self.context.current_mut() {
+ // match entry.entry(name.clone()) {
+ // Entry::Occupied(e) => {
+ // return Err(ParseError {
+ // line: self.line,
+ // message: format!(
+ // "label '{}' already exists for this context: it was previously defined in line {}",
+ // id.value, e.get().node.line),
+ // })
+ // }
+ // Entry::Vacant(e) => e.insert(PValue {
+ // node: PString {
+ // value: name.clone(),
+ // line: self.line,
+ // range: Range {
+ // start: id.range.start,
+ // end: id.range.end,
+ // },
+ // },
+ // value: 0,
+ // label: true,
+ // }),
+ // };
+ // }
+
+ // // And add the node so it's picked up later.
+ // self.mapping.push(Node::Label(Label {
+ // value: name.to_string(),
+ // }));
+ // Ok(())
+ // }
fn parser_error(&self, msg: &str) -> ParseError {
ParseError {
message: String::from(msg),
line: self.line,
- }
- }
-
- pub fn from_byte_reader<R: Read>(&mut self, mut reader: R) -> Result<()> {
- loop {
- let mut buf = [0; 1];
- let n = reader.read(&mut buf)?;
- if n == 0 {
- break;
- }
-
- match OPCODES.get(&buf[0]) {
- Some(v) => {
- let mut bs = [0; 2];
- for i in 0..v.size - 1 {
- let nn = reader.read(&mut buf)?;
- if nn == 0 {
- break;
- }
- bs[i as usize] = buf[0];
- }
- self.mapping.push(Node::Instruction(Instruction {
- mnemonic: PString::from(&v.mnemonic),
- opcode: v.opcode,
- size: v.size,
- bytes: bs,
- left: None,
- right: None,
- mode: v.mode.to_owned(),
- cycles: v.cycles,
- affected_on_page: v.affected_on_page,
- address: 0, // TODO
- resolved: true,
- }))
- }
-
- None => {
- return Err(
- self.parser_error(format!("unknown byte '0x{:02X}'", buf[0]).as_str())
- )
- }
- }
- }
-
- Ok(())
- }
+ parse: true,
+ }
+ }
+
+ // fn from_byte_reader<R: Read>(&mut self, mut reader: R) -> Result<()> {
+ // loop {
+ // let mut buf = [0; 1];
+ // let n = reader.read(&mut buf)?;
+ // if n == 0 {
+ // break;
+ // }
+
+ // match OPCODES.get(&buf[0]) {
+ // Some(v) => {
+ // let mut bs = [0; 2];
+ // for i in 0..v.size - 1 {
+ // let nn = reader.read(&mut buf)?;
+ // if nn == 0 {
+ // break;
+ // }
+ // bs[i as usize] = buf[0];
+ // }
+ // self.mapping.push(Node::Instruction(Instruction {
+ // mnemonic: PString::from(&v.mnemonic),
+ // opcode: v.opcode,
+ // size: v.size,
+ // bytes: bs,
+ // left: None,
+ // right: None,
+ // mode: v.mode.to_owned(),
+ // cycles: v.cycles,
+ // affected_on_page: v.affected_on_page,
+ // address: 0, // TODO
+ // resolved: true,
+ // }))
+ // }
+
+ // None => {
+ // return Err(
+ // self.parser_error(format!("unknown byte '0x{:02X}'", buf[0]).as_str())
+ // )
+ // }
+ // }
+ // }
+
+ // Ok(())
+ // }
}
#[cfg(test)]
@@ -1625,57 +1668,25 @@ mod tests {
use super::*;
use crate::mapping::EMPTY;
- fn assert_hex(one: &dyn Encodable, expected: &[u8]) {
- assert_eq!(
- one.to_hex(),
- expected
- .iter()
- .map(|x| format!("{:02X}", x))
- .collect::<Vec<_>>()
- );
- }
-
fn instruction_test(line: &str, hex: &[u8], skip_disassemble: bool) {
- let mut parser = Assembler::new(EMPTY.to_vec());
- let res = parser.assemble(line.as_bytes());
-
- if res.is_err() {
- if let Err(e) = res.clone() {
- assert_eq!(
- e,
- ParseError {
- line: 0,
- message: String::from("")
- }
- )
- }
- }
-
- let vec = res.unwrap();
+ let mut asm = Assembler::new(EMPTY.to_vec());
+ let res = asm.assemble(line.as_bytes()).unwrap();
- assert_eq!(vec.len(), 1);
- assert_hex(vec[0], hex);
+ assert_eq!(res.len(), 1);
- // Now disassemble.
+ for i in 0..res[0].size {
+ assert_eq!(hex[i as usize], res[0].bytes[i as usize]);
+ }
if skip_disassemble {
return;
}
-
- parser.reset();
- let dis = parser.disassemble(hex);
- assert!(dis.is_ok());
-
- let dvec = dis.unwrap();
- assert_eq!(dvec.len(), 1);
-
- let dinstr = dvec[0];
- assert_eq!(dinstr.to_human(), line);
+ // TODO
}
fn instruction_err(line: &str, message: &str) {
- let mut parser = Assembler::new(EMPTY.to_vec());
- let err = parser.assemble(line.as_bytes());
+ let mut asm = Assembler::new(EMPTY.to_vec());
+ let err = asm.assemble(line.as_bytes());
assert!(err.is_err());
if let Err(e) = err {
@@ -1683,37 +1694,36 @@ mod tests {
}
}
- // Mainly errors.
-
#[test]
fn bad_addressing() {
instruction_err("unknown #$20", "unknown instruction 'unknown'");
instruction_err(
"adc ($2002, x)",
- "when parsing an instruction with indirect X-indexed addressing: only one byte of data is allowed here",
+ "address can only be one byte long on indirect X addressing",
);
instruction_err(
- "adc ($2002, x), y",
- "bad indirect mode, expecting an indirect X-indexed addressing mode",
+ "adc ($20, x), y",
+ "it has to be either X addressing or Y addressing, not all at once",
);
instruction_err(
"adc ($2002), y",
- "when parsing an instruction with indirect Y-indexed addressing: only one byte of data is allowed here",
+ "address can only be one byte long on indirect Y addressing",
);
instruction_err(
"adc ($20, y)",
- "the index in indirect X-indexed addressing must be X",
+ "only the X index is allowed on indirect X addressing",
);
instruction_err(
"adc ($20), x",
- "the index in indirect Y-indexed addressing must be Y",
+ "only the Y index is allowed on indirect Y addressing",
);
instruction_err("jmp ($20)", "expecting a full 16-bit address");
- instruction_err("adc $20, z", "index is neither X nor Y");
+ instruction_err("adc $20, z", "can only use X and Y as indices");
instruction_err(
"adc ($2000)",
- "bad addressing mode 'indirect' for the instruction 'adc'",
+ "cannot use indirect addressing mode for the instruction 'adc'",
);
+ instruction_err("lda 12", "no prefix was given to operand")
}
#[test]
@@ -1731,8 +1741,9 @@ mod tests {
#[test]
fn parse_hexadecimal() {
instruction_err("adc $", "expecting a number of 1 to 4 hexadecimal digits");
- instruction_err("adc #$", "expecting a number of 2 hexadecimal digits");
- instruction_err("adc $AW", "unknown variable 'AW'");
+ // TODO: see comment on literal_mode being a stack.
+ instruction_err("adc #$", "expecting a number of 1 to 4 hexadecimal digits");
+ instruction_err("adc $AW", "could not convert digit to hexadecimal");
instruction_test("adc $AA", &[0x65, 0xAA], false);
instruction_test("adc $10", &[0x65, 0x10], false);
instruction_test("adc $10AB", &[0x6D, 0xAB, 0x10], false);
@@ -1743,7 +1754,7 @@ mod tests {
instruction_err("adc #", "empty decimal literal");
instruction_err("adc #256", "decimal value is too big");
instruction_err("adc #2000", "decimal value is too big");
- instruction_err("adc #2A", "'A' is not a decimal value");
+ instruction_err("adc #2A", "unknown variable '2A'"); // TODO: not sure about this
instruction_test("adc #1", &[0x69, 0x01], true);
}
@@ -1848,6 +1859,33 @@ mod tests {
}
#[test]
+ fn load() {
+ // lda
+ instruction_test("lda #$20", &[0xA9, 0x20], false);
+ instruction_test("lda $20", &[0xA5, 0x20], false);
+ instruction_test("lda $20, x", &[0xB5, 0x20], false);
+ instruction_test("lda $2002", &[0xAD, 0x02, 0x20], false);
+ instruction_test("lda $2002, x", &[0xBD, 0x02, 0x20], false);
+ instruction_test("lda $2002, y", &[0xB9, 0x02, 0x20], false);
+ instruction_test("lda ($20, x)", &[0xA1, 0x20], false);
+ instruction_test("lda ($20), y", &[0xB1, 0x20], false);
+
+ // ldx
+ instruction_test("ldx #$20", &[0xA2, 0x20], false);
+ instruction_test("ldx $20", &[0xA6, 0x20], false);
+ instruction_test("ldx $20, y", &[0xB6, 0x20], false);
+ instruction_test("ldx $2002", &[0xAE, 0x02, 0x20], false);
+ instruction_test("ldx $2002, y", &[0xBE, 0x02, 0x20], false);
+
+ // ldy
+ instruction_test("ldy #$20", &[0xA0, 0x20], false);
+ instruction_test("ldy $20", &[0xA4, 0x20], false);
+ instruction_test("ldy $20, x", &[0xB4, 0x20], false);
+ instruction_test("ldy $2002", &[0xAC, 0x02, 0x20], false);
+ instruction_test("ldy $2002, x", &[0xBC, 0x02, 0x20], false);
+ }
+
+ #[test]
fn jump() {
instruction_test("jsr $2002", &[0x20, 0x02, 0x20], false);
@@ -1944,33 +1982,6 @@ mod tests {
}
#[test]
- fn load() {
- // lda
- instruction_test("lda #$20", &[0xA9, 0x20], false);
- instruction_test("lda $20", &[0xA5, 0x20], false);
- instruction_test("lda $20, x", &[0xB5, 0x20], false);
- instruction_test("lda $2002", &[0xAD, 0x02, 0x20], false);
- instruction_test("lda $2002, x", &[0xBD, 0x02, 0x20], false);
- instruction_test("lda $2002, y", &[0xB9, 0x02, 0x20], false);
- instruction_test("lda ($20, x)", &[0xA1, 0x20], false);
- instruction_test("lda ($20), y", &[0xB1, 0x20], false);
-
- // ldx
- instruction_test("ldx #$20", &[0xA2, 0x20], false);
- instruction_test("ldx $20", &[0xA6, 0x20], false);
- instruction_test("ldx $20, y", &[0xB6, 0x20], false);
- instruction_test("ldx $2002", &[0xAE, 0x02, 0x20], false);
- instruction_test("ldx $2002, y", &[0xBE, 0x02, 0x20], false);
-
- // ldy
- instruction_test("ldy #$20", &[0xA0, 0x20], false);
- instruction_test("ldy $20", &[0xA4, 0x20], false);
- instruction_test("ldy $20, x", &[0xB4, 0x20], false);
- instruction_test("ldy $2002", &[0xAC, 0x02, 0x20], false);
- instruction_test("ldy $2002, x", &[0xBC, 0x02, 0x20], false);
- }
-
- #[test]
fn store_instructions() {
//sta
instruction_test("sta $20", &[0x85, 0x20], false);
@@ -2001,9 +2012,15 @@ mod tests {
// Variables & scopes.
#[test]
+ fn using_variables() {
+ // TODO
+ // todo!()
+ }
+
+ #[test]
fn scoped_variable() {
- let mut parser = Assembler::new(EMPTY.to_vec());
- let res = parser
+ let mut asm = Assembler::new(EMPTY.to_vec());
+ let res = asm
.assemble(
r#"
.scope One ; This is a comment
@@ -2030,13 +2047,9 @@ adc #Another::Variable
let instrs: Vec<[u8; 2]> = vec![[0x69, 0x20], [0x69, 0x30], [0x69, 0x20], [0x69, 0x40]];
for i in 0..4 {
- assert_eq!(
- res[i].to_hex(),
- instrs[i]
- .iter()
- .map(|x| format!("{:02X}", x))
- .collect::<Vec<_>>()
- );
+ assert_eq!(res[i].size, 2);
+ assert_eq!(res[i].bytes[0], instrs[i][0]);
+ assert_eq!(res[i].bytes[1], instrs[i][1]);
}
}
@@ -2060,117 +2073,101 @@ Yet = 4
if let Err(e) = res {
assert_eq!(
e.message,
- "variable 'Yet' is being re-assigned: it was previously defined in line 6"
+ "variable 'Yet' is being re-assigned: it was previously defined in line 7"
);
}
}
#[test]
- fn bad_variable_names() {
- instruction_err("a = 2", "cannot use reserved name 'a'");
- instruction_err("X = 2", "cannot use reserved name 'X'");
-
- instruction_err(
- "AA = 2",
- "cannot use names which are valid hexadecimal values such as 'AA'",
- );
-
- instruction_err(
- "Scope::Variable = 2",
- "the name 'Scope::Variable' is scoped: do not declare variables this way",
- );
- }
-
- #[test]
fn bad_assignment() {
instruction_err("Variable =", "incomplete assignment");
instruction_err("Variable = ; comment", "incomplete assignment");
- instruction_err("adc = $12", "cannot use 'adc' in an assignment because it's a word reserved for an instruction mnemonic");
- }
-
- // Literals
-
- #[test]
- fn byte_literals_errors() {
- // TODO
- // instruction_err(
- // ".byte $0102",
- // "when parsing a data literal: only one byte of data is allowed here",
- // );
- // instruction_err(".byte '$01", "non-terminated quote for byte literal");
- // instruction_err(".byte '$01, $02", "non-terminated quote for byte literal");
- }
-
- #[test]
- fn byte_literals() {
- let mut asm = Assembler::new(EMPTY.to_vec());
-
- let mut res = asm.assemble(".byte $01".as_bytes()).unwrap();
- assert_eq!(res.len(), 1);
- assert_hex(res[0], &[0x01]);
-
- asm.reset();
- res = asm.assemble(".db $01, $02".as_bytes()).unwrap();
- assert_eq!(res.len(), 2);
- assert_hex(res[0], &[0x01]);
- assert_hex(res[1], &[0x02]);
-
- asm.reset();
- res = asm
- .assemble(".byte $01, 2, '%00000011', \"$04\"".as_bytes())
- .unwrap();
- assert_eq!(res.len(), 4);
- assert_hex(res[0], &[0x01]);
- assert_hex(res[1], &[0x02]);
- assert_hex(res[2], &[0x03]);
- assert_hex(res[3], &[0x04]);
- }
-
- #[test]
- fn word_literals() {
- let mut asm = Assembler::new(EMPTY.to_vec());
-
- let mut res = asm.assemble(".word $01".as_bytes()).unwrap();
- assert_eq!(res.len(), 1);
- assert_hex(res[0], &[0x01, 0x00]);
-
- asm.reset();
- res = asm.assemble(".dw $0102, $02".as_bytes()).unwrap();
- assert_eq!(res.len(), 2);
- assert_hex(res[0], &[0x02, 0x01]);
- assert_hex(res[1], &[0x02, 0x00]);
-
- asm.reset();
- res = asm
- .assemble(".word $0102, $0204, '$0308', \"$0410\"".as_bytes())
- .unwrap();
- assert_eq!(res.len(), 4);
- assert_hex(res[0], &[0x02, 0x01]);
- assert_hex(res[1], &[0x04, 0x02]);
- assert_hex(res[2], &[0x08, 0x03]);
- assert_hex(res[3], &[0x10, 0x04]);
- }
-
- #[test]
- fn variables_in_literals() {
- let mut asm = Assembler::new(EMPTY.to_vec());
- let res = asm
- .assemble(
- r#"
-.scope One
- Variable = $01
-.endscope
-
-Variable = $02
-.byte One::Variable, Variable, $03
-"#
- .as_bytes(),
- )
- .unwrap();
-
- assert_eq!(res.len(), 3);
- assert_hex(res[0], &[0x01]);
- assert_hex(res[1], &[0x02]);
- assert_hex(res[2], &[0x03]);
}
}
+
+// // Literals
+
+// #[test]
+// fn byte_literals_errors() {
+// // TODO
+// // instruction_err(
+// // ".byte $0102",
+// // "when parsing a data literal: only one byte of data is allowed here",
+// // );
+// // instruction_err(".byte '$01", "non-terminated quote for byte literal");
+// // instruction_err(".byte '$01, $02", "non-terminated quote for byte literal");
+// }
+
+// #[test]
+// fn byte_literals() {
+// let mut asm = Assembler::new(EMPTY.to_vec());
+
+// let mut res = asm.assemble(".byte $01".as_bytes()).unwrap();
+// assert_eq!(res.len(), 1);
+// assert_hex(res[0], &[0x01]);
+
+// asm.reset();
+// res = asm.assemble(".db $01, $02".as_bytes()).unwrap();
+// assert_eq!(res.len(), 2);
+// assert_hex(res[0], &[0x01]);
+// assert_hex(res[1], &[0x02]);
+
+// asm.reset();
+// res = asm
+// .assemble(".byte $01, 2, '%00000011', \"$04\"".as_bytes())
+// .unwrap();
+// assert_eq!(res.len(), 4);
+// assert_hex(res[0], &[0x01]);
+// assert_hex(res[1], &[0x02]);
+// assert_hex(res[2], &[0x03]);
+// assert_hex(res[3], &[0x04]);
+// }
+
+// #[test]
+// fn word_literals() {
+// let mut asm = Assembler::new(EMPTY.to_vec());
+
+// let mut res = asm.assemble(".word $01".as_bytes()).unwrap();
+// assert_eq!(res.len(), 1);
+// assert_hex(res[0], &[0x01, 0x00]);
+
+// asm.reset();
+// res = asm.assemble(".dw $0102, $02".as_bytes()).unwrap();
+// assert_eq!(res.len(), 2);
+// assert_hex(res[0], &[0x02, 0x01]);
+// assert_hex(res[1], &[0x02, 0x00]);
+
+// asm.reset();
+// res = asm
+// .assemble(".word $0102, $0204, '$0308', \"$0410\"".as_bytes())
+// .unwrap();
+// assert_eq!(res.len(), 4);
+// assert_hex(res[0], &[0x02, 0x01]);
+// assert_hex(res[1], &[0x04, 0x02]);
+// assert_hex(res[2], &[0x08, 0x03]);
+// assert_hex(res[3], &[0x10, 0x04]);
+// }
+
+// #[test]
+// fn variables_in_literals() {
+// let mut asm = Assembler::new(EMPTY.to_vec());
+// let res = asm
+// .assemble(
+// r#"
+// .scope One
+// Variable = $01
+// .endscope
+
+// Variable = $02
+// .byte One::Variable, Variable, $03
+// "#
+// .as_bytes(),
+// )
+// .unwrap();
+
+// assert_eq!(res.len(), 3);
+// assert_hex(res[0], &[0x01]);
+// assert_hex(res[1], &[0x02]);
+// assert_hex(res[2], &[0x03]);
+// }
+// }