aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2024-12-11 12:05:24 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2024-12-12 07:57:16 +0100
commitb1623996c76fe6089e6cb16d1e1a16d32d1c19c3 (patch)
tree4ac3c2c925c60d18e412e6ad30ead162396e15fd /lib
parentf0624b8929e6ead3f2d8b1939dc7f094c539bee1 (diff)
downloadtools.nes-b1623996c76f.tar.gz
tools.nes-b1623996c76f.zip
Add support for character literals
Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/xixanta/src/parser.rs78
1 files changed, 76 insertions, 2 deletions
diff --git a/lib/xixanta/src/parser.rs b/lib/xixanta/src/parser.rs
index 9e7b7d5..9c67587 100644
--- a/lib/xixanta/src/parser.rs
+++ b/lib/xixanta/src/parser.rs
@@ -4,8 +4,6 @@ use crate::opcodes::{CONTROL_FUNCTIONS, INSTRUCTIONS};
use std::cmp::Ordering;
use std::io::{self, BufRead, Read};
-// TODO: allow for labels a la ".Lwhatever:"
-
/// The Parser struct holds basic data for the current parsing session.
#[derive(Default)]
pub struct Parser {
@@ -665,6 +663,8 @@ impl Parser {
self.parse_control(id, line)
} else if line.starts_with('$') || line.starts_with('#') || line.starts_with('%') {
self.parse_literal(id, line)
+ } else if line.starts_with('\'') {
+ self.parse_char(id)
} else {
// If there is an indication that it might be a macro call, process
// it as such.
@@ -777,6 +777,47 @@ impl Parser {
})
}
+ // Returns a NodeType::Literal node with the given `id` parsed as a
+ // character literal. This literal will have on the left node a value which
+ // is the character transformed into a decimal value.
+ fn parse_char(&mut self, id: PString) -> Result<PNode, ParseError> {
+ if id.value.len() != 3 {
+ return Err(self.parser_error("bad character literal"));
+ }
+
+ let mut chars = id.value.chars();
+ let del = chars.next().unwrap();
+ let ch = chars.next().unwrap();
+
+ if del != '\'' || chars.next().unwrap() != '\'' {
+ return Err(self.parser_error("bad character literal"));
+ }
+ if !ch.is_ascii_alphanumeric() {
+ return Err(self.parser_error(
+ "only alphanumeric ASCII characters are allowed on character literals",
+ ));
+ }
+
+ Ok(PNode {
+ node_type: NodeType::Literal,
+ value: id.clone(),
+ left: Some(Box::new(PNode {
+ node_type: NodeType::Value,
+ value: PString {
+ value: (ch as u8).to_string(),
+ line: id.line,
+ start: id.start + 1,
+ end: id.end - 1,
+ },
+ left: None,
+ right: None,
+ args: None,
+ })),
+ right: None,
+ args: None,
+ })
+ }
+
// Returns a new ParseError by using the current line.
fn parser_error(&self, msg: &str) -> ParseError {
ParseError {
@@ -1428,6 +1469,39 @@ mod tests {
}
#[test]
+ fn parse_byte_with_character_literals() {
+ let line = ".byte 'N', 'E', 'S', $1A";
+ let mut parser = Parser::default();
+ assert!(parser.parse(line.as_bytes()).is_ok());
+
+ let args = parser.nodes.first().unwrap().args.clone().unwrap();
+ assert_eq!(args.len(), 4);
+
+ let mut it = args.into_iter();
+
+ let mut cur = it.next().unwrap();
+ let mut left = cur.left.as_ref().unwrap();
+ assert_node(&cur, NodeType::Literal, line, "'N'");
+ assert_eq!(&left.node_type, &NodeType::Value);
+ assert_eq!(&left.value.value, "78");
+ assert_eq!(line.get(left.value.start..left.value.end).unwrap(), "N");
+
+ cur = it.next().unwrap();
+ left = cur.left.as_ref().unwrap();
+ assert_node(&cur, NodeType::Literal, line, "'E'");
+ assert_eq!(&left.node_type, &NodeType::Value);
+ assert_eq!(&left.value.value, "69");
+ assert_eq!(line.get(left.value.start..left.value.end).unwrap(), "E");
+
+ cur = it.next().unwrap();
+ left = cur.left.as_ref().unwrap();
+ assert_node(&cur, NodeType::Literal, line, "'S'");
+ assert_eq!(&left.node_type, &NodeType::Value);
+ assert_eq!(&left.value.value, "83");
+ assert_eq!(line.get(left.value.start..left.value.end).unwrap(), "S");
+ }
+
+ #[test]
fn parse_control_id_no_args() {
for line in vec![
".scope Scope",