use std::collections::VecDeque;
#[derive(Clone, Debug)]
pub struct ParsedInstruction {
pub instr: String,
pub args: Option<Vec<String>>,
}
#[derive(Clone, Debug)]
pub struct ParsedMacro {
pub mcr: String,
pub args: Option<Vec<String>>,
}
#[derive(Clone, Debug)]
pub enum ParsedOperation {
Instruction(ParsedInstruction),
Macro(ParsedMacro),
}
pub struct Parser {
q: VecDeque<String>,
d: VecDeque<ParsedOperation>,
}
impl std::str::FromStr for Parser {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let split = s.lines();
Ok(Parser {
q: VecDeque::from_iter(split.map(|s| s.to_string())),
d: VecDeque::new(),
})
}
}
fn single_split(s: &str) -> (String, String) {
let mut flag: bool = true;
let mut res: (String, String) = (String::new(), String::new());
for c in s.chars() {
if flag {
if !c.is_whitespace() {
res.0.push(c);
} else {
flag = false;
res.1.push(c);
}
} else {
res.1.push(c);
}
}
res
}
impl Parser {
pub fn pop_vdq(&mut self) -> VecDeque<ParsedOperation> {
std::mem::replace(&mut self.d, VecDeque::new())
}
pub fn peek_queued(&self) -> Option<&String> {
self.q.front()
}
fn pop_queued(&mut self) -> Option<String> {
self.q.pop_front()
}
pub fn drop_queued(&mut self) -> () {
self.pop_queued();
}
pub fn peek_parsed(&self) -> Option<&ParsedOperation> {
self.d.front()
}
pub fn pop_parsed(&mut self) -> Option<ParsedOperation> {
self.d.pop_front()
}
pub fn parse_all(&mut self) -> () {
while self.parse_line() {}
}
pub fn parse_line(&mut self) -> bool {
let mut a: String = match self.pop_queued() {
Some(s) => s,
None => return false,
};
let mut b = a.trim_end().chars();
if b.next_back() == Some(':') {
self.d.push_back(ParsedOperation::Macro(ParsedMacro {
mcr: "label".to_string(),
args: Some(vec![b.collect::<String>()]),
}));
return true;
} else {
drop(b);
}
a = split_comments(a.trim_start());
if a == "" {
return true;
}
let b: (String, String) = single_split(&a);
let mut ar: Vec<String> = vec![];
if b.1.trim_start() != "" {
for n in acs_from_str(&b.1) {
ar.push(n.trim().to_string());
}
}
let oar: Option<Vec<String>> = {
if ar.len() != 0 {
Some(ar)
} else {
None
}
};
if b.0.chars().nth(0).unwrap() != '.' {
self.d
.push_back(ParsedOperation::Instruction(ParsedInstruction {
instr: b.0,
args: oar,
}));
} else {
let mut ns: std::str::Chars = b.0.chars();
ns.next();
self.d.push_back(ParsedOperation::Macro(ParsedMacro {
mcr: ns.collect::<String>(),
args: oar,
}));
}
true
}
}
struct ArgCommaSplitter<'a> {
p: bool,
c: core::iter::Peekable<std::str::Bytes<'a>>,
}
impl Iterator for ArgCommaSplitter<'_> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
if let None = self.c.peek() {
return None;
}
let mut pool: Vec<u8> = Vec::new();
loop {
match self.c.next() {
Some(b'(') => {
self.p = true;
pool.push(b'(');
}
Some(b')') => {
self.p = false;
pool.push(b')');
}
Some(b',') => {
if !self.p {
return Some(String::from_utf8(pool).unwrap());
} else {
pool.push(b',');
}
}
None => return Some(String::from_utf8(pool).unwrap()),
Some(v) => pool.push(v),
}
}
}
}
fn acs_from_str(s: &str) -> impl Iterator<Item = String> + '_ {
ArgCommaSplitter {
p: false,
c: s.bytes().peekable(),
}
}
fn split_comments(s: &str) -> String {
s.split("//")
.next()
.unwrap()
.split(';')
.next()
.unwrap()
.to_string()
}