ref, add tests

This commit is contained in:
2020-05-05 23:10:36 +08:00
parent f62f966eaf
commit 57e91ff32e
3 changed files with 179 additions and 98 deletions

7
src/err.rs Normal file
View File

@@ -0,0 +1,7 @@
use err_derive::Error;
#[derive(Debug, Error)]
pub enum ParseError {
#[error(display = "error syntax: {:?}", _0)]
ErrorSyntax(String),
}

104
src/ins.rs Normal file
View File

@@ -0,0 +1,104 @@
#[derive(Debug)]
pub enum InsType {
Path(InsColl),
Node(Instruction),
}
#[derive(Debug)]
pub struct Ins {
pub c: char,
pub ins_type: InsType,
}
#[derive(Debug)]
pub struct InsColl {
pub inses: Vec<Ins>,
}
pub fn make_instruction_tree() -> InsColl {
InsColl{ inses: vec![
Ins{ c: '', ins_type: InsType::Path(InsColl{ inses: vec![
Ins{ c: '', ins_type: InsType::Node(Instruction::Push) },
Ins{ c: '', ins_type: InsType::Path(InsColl{ inses: vec![
Ins{ c: '', ins_type: InsType::Node(Instruction::Dup) },
Ins{ c: '', ins_type: InsType::Node(Instruction::Swap) },
Ins{ c: '', ins_type: InsType::Node(Instruction::Pop) }
]})},
Ins{ c: '', ins_type: InsType::Path(InsColl{ inses: vec![
Ins{ c: '', ins_type: InsType::Node(Instruction::CopyN) },
Ins{ c: '', ins_type: InsType::Node(Instruction::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(Instruction::Add) },
Ins{ c: '', ins_type: InsType::Node(Instruction::Sub) },
Ins{ c: '', ins_type: InsType::Node(Instruction::Mul) },
]})},
Ins{ c: '', ins_type: InsType::Path(InsColl{ inses: vec![
Ins{ c: '', ins_type: InsType::Node(Instruction::Div) },
Ins{ c: '', ins_type: InsType::Node(Instruction::Mod) },
]})},
]})},
Ins{ c: '', ins_type: InsType::Path(InsColl{ inses: vec![
Ins{ c: '', ins_type: InsType::Node(Instruction::Store) },
Ins{ c: '', ins_type: InsType::Node(Instruction::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(Instruction::StdOutChar) },
Ins{ c: '', ins_type: InsType::Node(Instruction::StdOutNum) },
]})},
Ins{ c: '', ins_type: InsType::Path(InsColl{ inses: vec![
Ins{ c: '', ins_type: InsType::Node(Instruction::StdInChar) },
Ins{ c: '', ins_type: InsType::Node(Instruction::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(Instruction::DefineLabel) },
Ins{ c: '', ins_type: InsType::Node(Instruction::CallAtLabel) },
Ins{ c: '', ins_type: InsType::Node(Instruction::GotoLabel) },
]})},
Ins{ c: '', ins_type: InsType::Path(InsColl{ inses: vec![
Ins{ c: '', ins_type: InsType::Node(Instruction::GotoLabelE0) },
Ins{ c: '', ins_type: InsType::Node(Instruction::GotoLabelL0) },
Ins{ c: '', ins_type: InsType::Node(Instruction::ReturnCallAt) },
]})},
Ins{ c: '', ins_type: InsType::Path(InsColl{ inses: vec![
Ins{ c: '', ins_type: InsType::Node(Instruction::End) },
]})},
]})},
]}
}
#[derive(Debug, Clone)]
pub enum Instruction {
Push,
Dup,
CopyN,
Swap,
Pop,
PopN,
Add,
Sub,
Mul,
Div,
Mod,
Store,
Retrieve,
DefineLabel,
CallAtLabel,
GotoLabel,
GotoLabelE0,
GotoLabelL0,
ReturnCallAt,
End,
StdOutChar,
StdOutNum,
StdInChar,
StdInNum,
}

View File

@@ -1,112 +1,82 @@
use err_derive::Error;
pub mod err;// TODO ...
mod ins;
#[derive(Debug, Error)]
pub enum ParseError {
#[error(display = "error syntax: {:?}", _0)]
ErrorSyntax(String),
}
#[derive(Debug)]
enum Instruction {
Push,
Dup,
CopyN,
Swap,
Pop,
PopN,
Add,
Sub,
Mul,
Div,
Mod,
Store,
Retrieve,
DefineLabel,
CallAtLabel,
GotoLabel,
GotoLabelE0,
GotoLabelL0,
ReturnCallAt,
End,
StdOutChar,
StdOutNum,
StdInChar,
StdInNum,
}
struct ParseChars {
index: usize,
chars: Vec<char>,
}
impl ParseChars {
fn next(&mut self) -> Option<char> {
let c = self.try_next();
if c.is_some() { self.index += 1; }
c
}
fn try_next(&self) -> Option<char> {
self.try_next_n(0_usize)
}
fn try_next_n(&self, nn: usize) -> Option<char> {
let i = self.index + nn;
if i >= self.chars.len() {
None
} else {
let c = self.chars[i];
Some(c)
}
}
}
use ins::*;
const VALID_INSTRUCTION_CHARS: &str = "草泥马河蟹";
// https://playsecurity.org/rawfile/grass_mud_horse_language_specification.md
// https://p.rogram.me/grassmudhorse.js/grassmudhorse.js
fn main() {
let input = "草草草泥马 马草草草泥草草草草泥泥马 草马草 泥马草泥 草草草泥草泥草马 泥马草草 草草草泥马 泥草草草 草马草 草草草泥草泥泥马 泥草草泥 马泥草草泥草草草泥草泥马 马草马草泥草草草草泥泥马 马草草草泥草草草泥草泥马 草马马 马马马";
let mut cs = "草草草泥草泥泥马".chars();
println!("{}", read_number(&mut cs));
println!("{:?}", parse_lang(input));
}
fn parse_lang(lang: &str) -> Result<Vec<Instruction>, ParseError> {
let mut r = vec![];
let mut cs = ParseChars{ index: 0_usize, chars: lang.chars().filter(|c| {
VALID_INSTRUCTION_CHARS.chars().any(|vic| vic == *c)
}).collect(), };
while let Some(c) = cs.next() {
let nc = match cs.try_next() {
Some(nc) => nc, None => {
return Err(ParseError::ErrorSyntax(
format!("Syntax error: after {} has no valid char", c)
));
},
};
if c == '草' && nc == '草' { // Push
cs.next();
// todo parse
continue;
fn read_number(cs: &mut dyn Iterator<Item=char>) -> isize {
let mut is_positive = 1_isize;
if let Some(c) = cs.next() {
if c == '草' {
is_positive = 1;
} else if c == '泥' {
is_positive = -1;
} else {
// Syntax ERROR!
}
let nnc = match cs.try_next_n(1) {
Some(nc) => nc, None => {
return Err(ParseError::ErrorSyntax(
format!("Syntax error: after {}{} has no valid char", c, nc)
));
},
};
if c == '草' && nc == '马' || nnc == '草' { // Dup
}
let nnnc = match cs.try_next_n(2) {
Some(nc) => nc, None => {
return Err(ParseError::ErrorSyntax(
format!("Syntax error: after {}{}{} has no valid char", c, nc, nnc)
));
},
};
} else {
// Syntax ERROR!
}
Ok(r)
let mut num = 0_isize;
while let Some(c) = cs.next() {
if c == '马' {
break;
} else {
num <<= 1;
num += if c == '草' { 0 } else { 1 };
}
}
is_positive * num
}
fn match_instruction(ins_coll: &InsColl, cs: &mut dyn Iterator<Item=char>) -> Option<Instruction> {
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) => {
println!("{:?}", ins);
return Some(ins.clone());
},
}
}
}
println!("ERROR!!!! syntax error!");
}
None
}
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)
});
while let Some(ins) = match_instruction(&instruction_tree_root, &mut cs) {
println!("{:?}", ins);
}
}
#[test]
fn test_read_number() {
assert_eq!(read_number(&mut "草泥马".chars()), 1);
assert_eq!(read_number(&mut "草泥草草草草泥泥马".chars()), 67);
assert_eq!(read_number(&mut "草泥草泥泥马".chars()), 11);
assert_eq!(read_number(&mut "草泥草草草泥草泥马".chars()), 69);
}