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) -> 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, } pub fn parse_lang(lang: &str) -> Result, 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) -> Result, 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) -> Result { 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); }