mod computing;
mod core;
mod mathematics;
mod mathml;
mod xml;
use std::fmt;
#[derive(Clone)]
pub enum Cmd {
Computing(computing::Computing),
Core(core::Core),
Mathematics(mathematics::Mathematics),
MathML(mathml::MathML),
XML(xml::XML),
}
impl fmt::Display for Cmd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let space: &str;
let name: &str;
let fmt_args: String;
match self {
Cmd::Computing(subcmd) => {
space = subcmd.space();
name = subcmd.name();
fmt_args = subcmd.fmt_args();
}
Cmd::Core(subcmd) => {
space = subcmd.space();
name = subcmd.name();
fmt_args = subcmd.fmt_args();
}
Cmd::Mathematics(subcmd) => {
space = subcmd.space();
name = subcmd.name();
fmt_args = subcmd.fmt_args();
}
Cmd::MathML(subcmd) => {
space = subcmd.space();
name = subcmd.name();
fmt_args = subcmd.fmt_args();
}
Cmd::XML(subcmd) => {
space = subcmd.space();
name = subcmd.name();
fmt_args = subcmd.fmt_args();
}
}
write!(f, "[{}/{} {}]", space, name, fmt_args)
}
}
impl Cmd {
pub fn execute(&self) -> Cmd {
match self {
Cmd::Computing(subcmd) => match subcmd.execute() {
Some(cmd) => cmd,
None => self.clone(),
},
Cmd::Core(subcmd) => match subcmd.execute() {
Some(cmd) => cmd,
None => self.clone(),
},
Cmd::Mathematics(subcmd) => match subcmd.execute() {
Some(cmd) => cmd,
None => self.clone(),
},
Cmd::MathML(subcmd) => match subcmd.execute() {
Some(cmd) => cmd,
None => self.clone(),
},
Cmd::XML(subcmd) => match subcmd.execute() {
Some(cmd) => cmd,
None => self.clone(),
},
}
}
}
#[cfg(test)]
mod tests {
use super::core::Core;
use super::mathematics::Mathematics;
use super::mathml::MathML;
use super::Cmd;
#[test]
fn smoke() {
assert_eq!(
Cmd::Mathematics(Mathematics::Addition(
Box::new(Cmd::Mathematics(Mathematics::NaturalU64(42))),
Box::new(Cmd::Mathematics(Mathematics::NaturalU64(22))),
))
.to_string(),
String::from(
"[Mathematics/Addition [Mathematics/NaturalU64 42] [Mathematics/NaturalU64 22]]"
)
);
let cmd = Cmd::Core(Core::Execute(Box::new(Cmd::Mathematics(
Mathematics::Addition(
Box::new(Cmd::Mathematics(Mathematics::NaturalU64(42))),
Box::new(Cmd::Mathematics(Mathematics::NaturalU64(22))),
),
))));
assert_eq!(
cmd.execute().to_string(),
String::from("[Mathematics/NaturalU64 64]")
);
let mml = Cmd::MathML(MathML::SerializeAsParallel(Box::new(Cmd::Mathematics(
Mathematics::Addition(
Box::new(Cmd::Mathematics(Mathematics::NaturalU64(42))),
Box::new(Cmd::Mathematics(Mathematics::NaturalU64(22))),
),
))));
assert_eq!(
mml.execute().to_string(),
String::from("[XML/MarkupUTF8 <math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mn>42</mn><mo>+</mo><mn>22</mn></mrow><annotation-xml encoding=\"MathML-Content\"><apply><plus/><cn type=\"integer\">42</cn><cn type=\"integer\">22</cn></apply></annotation-xml></semantics></math>]")
);
}
}
pub fn parser(s: String) -> Cmd {
let tokens = scan(s);
println!("Tokens={:?}", tokens);
let (cmd, _index) = parse_cmd_start(&tokens, 0);
cmd
}
fn new_error(msg: &str) -> Cmd {
Cmd::Core(core::Core::Error(msg.to_string()))
}
fn parse_cmd_start(tokens: &TokenList, index: usize) -> (Cmd, usize) {
match tokens.get(index) {
Some(Token::CmdStart) => parse_cmd_qualified_name(tokens, index + 1),
Some(_) => (new_error("Expected a Token::CmdStart"), index),
None => (new_error("Premature end of tokens"), index),
}
}
fn parse_cmd_qualified_name(tokens: &TokenList, index: usize) -> (Cmd, usize) {
match tokens.get(index) {
Some(Token::CmdSpace(space)) => match tokens.get(index + 1) {
Some(Token::CmdLocalName(local_name)) => match space.as_str() {
computing::SPACE => computing::parse(local_name, tokens, index + 2),
core::SPACE => core::parse(local_name, tokens, index + 2),
mathematics::SPACE => mathematics::parse(local_name, tokens, index + 2),
mathml::SPACE => mathml::parse(local_name, tokens, index + 2),
xml::SPACE => xml::parse(local_name, tokens, index + 2),
_ => (new_error("Premature end of tokens"), index + 1),
},
Some(_) => (new_error("Expected a Token::CmdLocalName"), index + 1),
None => (new_error("Premature end of tokens"), index + 1),
},
Some(_) => (new_error("Expected a Token::CmdSpace"), index),
None => (new_error("Premature end of tokens"), index),
}
}
fn parse_cmd_end(tokens: &TokenList, index: usize, cmd: Cmd) -> (Cmd, usize) {
match tokens.get(index) {
Some(Token::CmdEnd) => (cmd, index + 1),
Some(_) => (new_error("Expected a Token:CmdEnd"), index),
None => (new_error("Expected a Token:CmdEnd"), index),
}
}
enum Mode {
Start,
CmdQualifiedName,
CmdLocalName,
CmdArgs,
ValNatural,
ValString,
}
#[derive(PartialEq, Debug)]
enum Token {
CmdStart,
CmdSpace(String),
CmdLocalName(String),
ValNatural(String),
ValString(String),
CmdEnd,
}
use std::collections::VecDeque;
#[derive(Debug)]
pub struct TokenList {
list: VecDeque<Token>,
}
impl TokenList {
fn new() -> TokenList {
TokenList {
list: VecDeque::new(),
}
}
fn add(&mut self, token: Token) {
self.list.push_back(token);
}
fn get(&self, index: usize) -> Option<&Token> {
self.list.get(index)
}
}
fn scan(s: String) -> TokenList {
let mut mode_stack: Vec<Mode> = vec![Mode::Start];
let mut token_value: String = String::new();
let mut tokens: TokenList = TokenList::new();
let mut current_space: String = String::new();
for c in s.chars() {
match mode_stack.last().unwrap() {
Mode::Start => match c {
'[' => {
tokens.add(Token::CmdStart);
mode_stack.push(Mode::CmdQualifiedName);
}
_ => (),
},
Mode::CmdQualifiedName => match c {
'A'..='Z' | 'a'..='z' | '0'..='9' => token_value.push(c),
'/' => {
current_space = token_value.to_string();
tokens.add(Token::CmdSpace(token_value.to_string()));
token_value = String::new();
mode_stack.pop();
mode_stack.push(Mode::CmdLocalName);
}
' ' => {
tokens.add(Token::CmdSpace(current_space.to_string()));
tokens.add(Token::CmdLocalName(token_value.to_string()));
token_value = String::new();
mode_stack.pop();
mode_stack.push(Mode::CmdArgs);
}
'[' => {
tokens.add(Token::CmdSpace(current_space.to_string()));
tokens.add(Token::CmdLocalName(token_value.to_string()));
tokens.add(Token::CmdStart);
token_value = String::new();
mode_stack.pop();
mode_stack.push(Mode::CmdArgs);
mode_stack.push(Mode::CmdQualifiedName);
}
']' => {
tokens.add(Token::CmdSpace(current_space.to_string()));
tokens.add(Token::CmdLocalName(token_value.to_string()));
tokens.add(Token::CmdEnd);
token_value = String::new();
mode_stack.pop();
}
_ => (),
},
Mode::CmdLocalName => match c {
'A'..='Z' | 'a'..='z' | '0'..='9' => token_value.push(c),
'/' => {
panic!("Illegal / character detected in CmdLocalName.");
}
' ' => {
tokens.add(Token::CmdLocalName(token_value.to_string()));
token_value = String::new();
mode_stack.pop();
mode_stack.push(Mode::CmdArgs);
}
'[' => {
tokens.add(Token::CmdLocalName(token_value.to_string()));
tokens.add(Token::CmdStart);
token_value = String::new();
mode_stack.pop();
mode_stack.push(Mode::CmdArgs);
mode_stack.push(Mode::CmdQualifiedName);
}
']' => {
tokens.add(Token::CmdLocalName(token_value.to_string()));
tokens.add(Token::CmdEnd);
token_value = String::new();
mode_stack.pop();
}
_ => (),
},
Mode::CmdArgs => match c {
'0'..='9' => {
token_value.push(c);
mode_stack.push(Mode::ValNatural);
}
'\'' => {
mode_stack.push(Mode::ValString);
}
'[' => {
tokens.add(Token::CmdStart);
mode_stack.push(Mode::CmdQualifiedName);
}
']' => {
tokens.add(Token::CmdEnd);
mode_stack.pop();
}
_ => (),
},
Mode::ValNatural => match c {
'0'..='9' => {
token_value.push(c);
}
' ' => {
tokens.add(Token::ValNatural(token_value.to_string()));
token_value = String::new();
mode_stack.pop();
}
'[' => {
tokens.add(Token::ValNatural(token_value.to_string()));
tokens.add(Token::CmdStart);
token_value = String::new();
mode_stack.pop();
mode_stack.push(Mode::CmdQualifiedName);
}
']' => {
tokens.add(Token::ValNatural(token_value.to_string()));
tokens.add(Token::CmdEnd);
token_value = String::new();
mode_stack.pop();
mode_stack.pop();
}
_ => (),
},
Mode::ValString => match c {
'\'' => {
tokens.add(Token::ValString(token_value.to_string()));
token_value = String::new();
mode_stack.pop();
}
_ => {
token_value.push(c);
}
},
}
}
tokens
}