216 lines
7.8 KiB
Rust
216 lines
7.8 KiB
Rust
use super::err::*;
|
|
use super::ins::*;
|
|
|
|
pub const VALID_INSTRUCTION_CHARS: &str = "草泥马河蟹";
|
|
|
|
#[derive(Debug)]
|
|
enum InsId {
|
|
Push, // has data
|
|
Dup,
|
|
CopyN, // has data
|
|
Swap,
|
|
Pop,
|
|
PopN, // has data
|
|
Add,
|
|
Sub,
|
|
Mul,
|
|
Div,
|
|
Mod,
|
|
Store,
|
|
Retrieve,
|
|
DefineLabel, // has data
|
|
CallAtLabel, // has data
|
|
GotoLabel, // has data
|
|
GotoLabelE0, // has data
|
|
GotoLabelL0, // has data
|
|
ReturnCallAt,
|
|
End,
|
|
StdOutChar,
|
|
StdOutNum,
|
|
StdInChar,
|
|
StdInNum,
|
|
}
|
|
|
|
impl InsId {
|
|
fn has_data(&self) -> bool {
|
|
match self {
|
|
InsId::Push | InsId::CopyN | InsId::PopN |
|
|
InsId::DefineLabel | InsId::CallAtLabel |
|
|
InsId::GotoLabel | InsId::GotoLabelE0 | InsId::GotoLabelL0 => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
fn new_instruction(&self, data: Option<isize>) -> Instruction {
|
|
match self {
|
|
InsId::Push => Instruction::Push(data.unwrap_or(0)),
|
|
InsId::Dup => Instruction::Dup,
|
|
InsId::CopyN => Instruction::CopyN(data.unwrap_or(0)),
|
|
InsId::Swap => Instruction::Swap,
|
|
InsId::Pop => Instruction::Pop,
|
|
InsId::PopN => Instruction::PopN(data.unwrap_or(0)),
|
|
InsId::Add => Instruction::Add,
|
|
InsId::Sub => Instruction::Sub,
|
|
InsId::Mul => Instruction::Mul,
|
|
InsId::Div => Instruction::Div,
|
|
InsId::Mod => Instruction::Mod,
|
|
InsId::Store => Instruction::Store,
|
|
InsId::Retrieve => Instruction::Retrieve,
|
|
InsId::DefineLabel => Instruction::DefineLabel(data.unwrap_or(0)),
|
|
InsId::CallAtLabel => Instruction::CallAtLabel(data.unwrap_or(0)),
|
|
InsId::GotoLabel => Instruction::GotoLabel(data.unwrap_or(0)),
|
|
InsId::GotoLabelE0 => Instruction::GotoLabelE0(data.unwrap_or(0)),
|
|
InsId::GotoLabelL0 => Instruction::GotoLabelL0(data.unwrap_or(0)),
|
|
InsId::ReturnCallAt => Instruction::ReturnCallAt,
|
|
InsId::End => Instruction::End,
|
|
InsId::StdOutChar => Instruction::StdOutChar,
|
|
InsId::StdOutNum => Instruction::StdOutNum,
|
|
InsId::StdInChar => Instruction::StdInChar,
|
|
InsId::StdInNum => Instruction::StdInNum,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum InsType {
|
|
Path(InsColl),
|
|
Node(InsId),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Ins {
|
|
c: char,
|
|
ins_type: InsType,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct InsColl {
|
|
inses: Vec<Ins>,
|
|
}
|
|
|
|
pub fn parse_lang(lang: &str) -> Result<Vec<Instruction>, ParseError> {
|
|
let mut r = vec![];
|
|
|
|
let instruction_tree_root = make_instruction_tree();
|
|
let mut cs = lang.chars().filter(|c| {
|
|
VALID_INSTRUCTION_CHARS.chars().any(|vic| vic == *c)
|
|
});
|
|
|
|
loop {
|
|
match match_instruction(&instruction_tree_root, &mut cs) {
|
|
Err(err) => return Err(err),
|
|
Ok(None) => break,
|
|
Ok(Some(ins)) => r.push(ins),
|
|
}
|
|
}
|
|
|
|
Ok(r)
|
|
}
|
|
|
|
fn make_instruction_tree() -> InsColl {
|
|
InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Node(InsId::Push) },
|
|
Ins{ c: '马', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Node(InsId::Dup) },
|
|
Ins{ c: '泥', ins_type: InsType::Node(InsId::Swap) },
|
|
Ins{ c: '马', ins_type: InsType::Node(InsId::Pop) }
|
|
]})},
|
|
Ins{ c: '泥', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Node(InsId::CopyN) },
|
|
Ins{ c: '马', ins_type: InsType::Node(InsId::PopN) }
|
|
]})},
|
|
]})},
|
|
Ins{ c: '泥', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Node(InsId::Add) },
|
|
Ins{ c: '泥', ins_type: InsType::Node(InsId::Sub) },
|
|
Ins{ c: '马', ins_type: InsType::Node(InsId::Mul) },
|
|
]})},
|
|
Ins{ c: '泥', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Node(InsId::Div) },
|
|
Ins{ c: '泥', ins_type: InsType::Node(InsId::Mod) },
|
|
]})},
|
|
]})},
|
|
Ins{ c: '泥', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Node(InsId::Store) },
|
|
Ins{ c: '泥', ins_type: InsType::Node(InsId::Retrieve) },
|
|
]})},
|
|
Ins{ c: '马', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Node(InsId::StdOutChar) },
|
|
Ins{ c: '泥', ins_type: InsType::Node(InsId::StdOutNum) },
|
|
]})},
|
|
Ins{ c: '泥', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Node(InsId::StdInChar) },
|
|
Ins{ c: '泥', ins_type: InsType::Node(InsId::StdInNum) },
|
|
]})},
|
|
]})},
|
|
]})},
|
|
Ins{ c: '马', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Node(InsId::DefineLabel) },
|
|
Ins{ c: '泥', ins_type: InsType::Node(InsId::CallAtLabel) },
|
|
Ins{ c: '马', ins_type: InsType::Node(InsId::GotoLabel) },
|
|
]})},
|
|
Ins{ c: '泥', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '草', ins_type: InsType::Node(InsId::GotoLabelE0) },
|
|
Ins{ c: '泥', ins_type: InsType::Node(InsId::GotoLabelL0) },
|
|
Ins{ c: '马', ins_type: InsType::Node(InsId::ReturnCallAt) },
|
|
]})},
|
|
Ins{ c: '马', ins_type: InsType::Path(InsColl{ inses: vec![
|
|
Ins{ c: '马', ins_type: InsType::Node(InsId::End) },
|
|
]})},
|
|
]})},
|
|
]}
|
|
}
|
|
|
|
fn match_instruction(ins_coll: &InsColl, cs: &mut dyn Iterator<Item=char>) -> Result<Option<Instruction>, ParseError> {
|
|
if let Some(c) = cs.next() {
|
|
for ins in &ins_coll.inses {
|
|
if ins.c == c {
|
|
match &ins.ins_type {
|
|
InsType::Path(ins_coll) => return match_instruction(&ins_coll, cs),
|
|
InsType::Node(ins) => {
|
|
let data = if ins.has_data() { Some(read_number(cs)?) } else { None };
|
|
return Ok(Some(ins.new_instruction(data)));
|
|
},
|
|
}
|
|
}
|
|
}
|
|
return Err(ParseError::ErrorSyntax("ERROR!!!! syntax error!".into()));
|
|
}
|
|
Ok(None)
|
|
}
|
|
|
|
fn read_number(cs: &mut dyn Iterator<Item=char>) -> Result<isize, ParseError> {
|
|
let is_positive = if let Some(c) = cs.next() {
|
|
if c == '草' { 1 } else if c == '泥' { -1 } else {
|
|
return Err(ParseError::ErrorSyntax("ERROR!!!! syntax error!".into()));
|
|
}
|
|
} else {
|
|
return Err(ParseError::ErrorSyntax("ERROR!!!! syntax error!".into()));
|
|
};
|
|
let mut num = 0_isize;
|
|
loop {
|
|
if let Some(c) = cs.next() {
|
|
if c == '马' {
|
|
break;
|
|
} else {
|
|
num <<= 1;
|
|
num += if c == '草' { 0 } else { 1 };
|
|
}
|
|
}
|
|
}
|
|
Ok(is_positive * num)
|
|
}
|
|
|
|
#[test]
|
|
fn test_read_number() {
|
|
assert_eq!(read_number(&mut "草泥马".chars()).unwrap(), 1);
|
|
assert_eq!(read_number(&mut "草泥草草草草泥泥马".chars()).unwrap(), 67);
|
|
assert_eq!(read_number(&mut "草泥草泥泥马".chars()).unwrap(), 11);
|
|
assert_eq!(read_number(&mut "草泥草草草泥草泥马".chars()).unwrap(), 69);
|
|
}
|