use crate::*;
pub struct SyntacticParser {
tokeniser: Tokeniser,
tokens: Vec<SyntacticToken>,
/// The name of the macro being parsed.
macro_name: Option<String>,
/// The name of the most recent label.
label_name: String,
}
impl SyntacticParser {
pub fn new<P: Into<PathBuf>>(source_code: &str, path: Option<P>) -> Self {
let mut tokeniser = Tokeniser::new(source_code, path);
tokeniser.add_delimiters(&['@','%',';',':','{','}','(','[','#','~']);
Self {
tokeniser,
tokens: Vec::new(),
macro_name: None,
label_name: String::new(),
}
}
pub fn parse(mut self) -> Vec<SyntacticToken> {
use SyntacticTokenVariant as SynVar;
use SyntacticParseError as SynErr;
let t = &mut self.tokeniser;
loop {
t.eat_whitespace();
t.mark_start();
let Some(c) = t.eat_char() else { break };
let variant = match c {
':' => SynVar::Separator,
'{' => SynVar::BlockOpen,
'}' => SynVar::BlockClose,
'@' => match &self.macro_name {
Some(_) => {
t.eat_token();
SynVar::Error(SynErr::LabelInMacroDefinition)
}
None => {
self.label_name = t.eat_token();
SynVar::LabelDefinition(self.label_name.clone())
}
}
'&' => match &self.macro_name {
Some(macro_name) => {
let label_name = format!("{macro_name}:{}", t.eat_token());
SynVar::LabelDefinition(label_name)
}
None => {
let label_name = &self.label_name;
let sublabel_name = format!("{label_name}/{}", t.eat_token());
SynVar::LabelDefinition(sublabel_name)
}
}
'%' => {
let macro_name = t.eat_token();
self.macro_name = Some(macro_name.clone());
SynVar::MacroDefinition(macro_name)
}
';' => {
self.macro_name = None;
SynVar::MacroDefinitionTerminator
}
'[' => {
t.mark_child();
match t.eat_to_delimiter(']') {
Some(_) => {
let child = t.subtokenise();
t.mark_end();
let expr = parse_constant_expression(child, t.get_source());
SynVar::Expression(expr)
}
None => SynVar::Error(SynErr::UnterminatedExpression),
}
}
'"' => {
t.mark_child();
match t.eat_to_delimiter('"') {
Some(string) => {
let child = t.subtokenise();
t.mark_end();
let chars = parse_tracked_chars(child);
let tracked_string = TrackedString {
source: t.get_source(), string, chars,
};
SynVar::String(tracked_string)
}
None => SynVar::Error(SynErr::UnterminatedString),
}
}
'(' => match t.eat_to_delimiter(')') {
Some(string) => {
// Check if the comment fills the entire line.
if t.start.position.column == 0 && t.end_of_line() {
if let Some(path) = string.strip_prefix(": ") {
t.embedded_path = Some(PathBuf::from(path.trim()));
t.embedded_first_line = t.start.position.line + 1;
}
}
continue;
},
None => SynVar::Error(SynErr::UnterminatedComment),
}
'|' => {
let token = t.eat_token();
if let Some(hex_string) = token.strip_prefix("0x") {
match usize::from_str_radix(hex_string, 16) {
Ok(addr) => SynVar::PinnedAddress(addr),
Err(_) => SynVar::Error(SynErr::InvalidHexadecimalLiteral(token)),
}
} else {
match usize::from_str_radix(&token, 10) {
Ok(addr) => SynVar::PinnedAddress(addr),
Err(_) => SynVar::Error(SynErr::InvalidDecimalLiteral(token)),
}
}
}
'#' => {
t.mark_child();
t.eat_token();
let pbl = parse_packed_binary_literal(t.subtokenise(), t.get_source());
SynVar::PackedBinaryLiteral(pbl)
},
'~' => match &self.macro_name {
Some(macro_name) => {
let symbol_name = format!("{macro_name}:{}", t.eat_token());
SynVar::Symbol(symbol_name)
}
None => {
let label_name = &self.label_name;
let symbol_name = format!("{label_name}/{}", t.eat_token());
SynVar::Symbol(symbol_name)
}
}
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(value) => SynVar::IntegerLiteral(value as isize),
Err(_) => SynVar::Error(SynErr::InvalidHexadecimalLiteral(token)),
}
} else {
match usize::from_str_radix(&token, 10) {
Ok(value) => SynVar::IntegerLiteral(value as isize),
Err(_) => SynVar::Symbol(token),
}
}
}
};
t.mark_end();
let source = t.get_source();
self.tokens.push(SyntacticToken { source, variant });
}
return self.tokens;
}
}
fn parse_tracked_chars(mut t: Tokeniser) -> Vec<Tracked<char>> {
let mut output = Vec::new();
while let Some(c) = t.eat_char() {
output.push(Tracked::from(c, t.get_source()));
t.mark_start();
}
return output;
}