From 2b4e522b12a7eb87e91cd1cdc56064ee429a5212 Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Tue, 11 Feb 2025 14:00:20 +1300 Subject: Initial commit --- src/parsers/syntactic.rs | 108 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/parsers/syntactic.rs (limited to 'src/parsers/syntactic.rs') diff --git a/src/parsers/syntactic.rs b/src/parsers/syntactic.rs new file mode 100644 index 0000000..443e47e --- /dev/null +++ b/src/parsers/syntactic.rs @@ -0,0 +1,108 @@ +use crate::*; + + +pub struct SyntacticParser { + tokeniser: Tokeniser, + /// The name of the most recently parsed label. + label_name: String, + /// The name of the macro being parsed. + macro_name: Option, +} + +impl SyntacticParser { + pub fn from_source_code>(source_code: &str, path: Option

) -> Self { + let mut tokeniser = Tokeniser::new(source_code, path); + tokeniser.add_delimiters(&['@','&','%',';',':','{','}','(','[','#','~']); + Self { + tokeniser, + label_name: String::new(), + macro_name: None, + } + } +} + + +impl Iterator for SyntacticParser { + type Item = SyntacticToken; + + /// Sequentially parse tokens from the source code. + fn next(&mut self) -> Option { + use SyntacticTokenVariant as SynVar; + use SyntacticParseError as SynErr; + let t = &mut self.tokeniser; + + t.drop_whitespace(); + t.mark_start_position(); + + let variant = match t.eat_char()? { + '@' => { + self.label_name = t.eat_token(); + SynVar::LabelDefinition(self.label_name.clone()) + } + '&' => { + let token = t.eat_token(); + SynVar::LabelDefinition(format!("{}/{token}", self.label_name)) + } + '%' => { + let macro_name = t.eat_token(); + self.macro_name = Some(macro_name.clone()); + SynVar::MacroDefinition(macro_name) + } + ';' => { + self.macro_name = None; + SynVar::MacroDefinitionTerminator + } + '[' => match t.eat_to_delimiter(']') { + Some(string) => { + let constant = ConstantExpression::from_str(&string, t); + SynVar::ConstantExpression(constant) + } + None => SynVar::Error(SynErr::UnterminatedConstantExpression), + } + '{' => SynVar::BlockOpen, + '}' => SynVar::BlockClose, + '(' => match t.eat_to_delimiter(')') { + Some(string) => SynVar::Comment(string), + None => SynVar::Error(SynErr::UnterminatedComment), + } + '#' => { + let token = t.eat_token(); + let pbl = PackedBinaryLiteral::from_str(&token, t); + SynVar::PackedBinaryLiteral(pbl) + }, + '~' => { + let token = t.eat_token(); + SynVar::Symbol(format!("{}/{token}", self.label_name)) + } + ':' => SynVar::Separator, + c => { + let token = format!("{c}{}", t.eat_token()); + if let Some(hex_string) = token.strip_prefix("0x") { + match usize::from_str_radix(hex_string, 16) { + Ok(hex) => SynVar::HexadecimalLiteral(hex), + Err(_) => SynVar::Error(SynErr::InvalidHexadecimalLiteral(token)), + } + } else { + match usize::from_str_radix(&token, 10) { + Ok(value) => SynVar::DecimalLiteral(value), + Err(_) => SynVar::Symbol(token), + } + } + } + }; + + // Parse source path comments. + if let SynVar::Comment(comment) = &variant { + // Check if the comment fills the entire line. + if t.start_position.column == 0 && t.end_of_line() { + if let Some(path) = comment.strip_prefix(": ") { + t.source_path = Some(PathBuf::from(path.trim())); + t.embedded_first_line = t.start_position.line + 1; + } + } + } + + let source = t.mark_end_position(); + Some( SyntacticToken { source, variant } ) + } +} -- cgit v1.2.3-70-g09d2