ref, add tests
This commit is contained in:
7
src/err.rs
Normal file
7
src/err.rs
Normal 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
104
src/ins.rs
Normal 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,
|
||||||
|
}
|
||||||
166
src/main.rs
166
src/main.rs
@@ -1,112 +1,82 @@
|
|||||||
use err_derive::Error;
|
pub mod err;// TODO ...
|
||||||
|
mod ins;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
use ins::*;
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const VALID_INSTRUCTION_CHARS: &str = "草泥马河蟹";
|
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() {
|
fn main() {
|
||||||
let input = "草草草泥马 马草草草泥草草草草泥泥马 草马草 泥马草泥 草草草泥草泥草马 泥马草草 草草草泥马 泥草草草 草马草 草草草泥草泥泥马 泥草草泥 马泥草草泥草草草泥草泥马 马草马草泥草草草草泥泥马 马草草草泥草草草泥草泥马 草马马 马马马";
|
let input = "草草草泥马 马草草草泥草草草草泥泥马 草马草 泥马草泥 草草草泥草泥草马 泥马草草 草草草泥马 泥草草草 草马草 草草草泥草泥泥马 泥草草泥 马泥草草泥草草草泥草泥马 马草马草泥草草草草泥泥马 马草草草泥草草草泥草泥马 草马马 马马马";
|
||||||
|
|
||||||
|
|
||||||
|
let mut cs = "草草草泥草泥泥马".chars();
|
||||||
|
println!("{}", read_number(&mut cs));
|
||||||
println!("{:?}", parse_lang(input));
|
println!("{:?}", parse_lang(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_lang(lang: &str) -> Result<Vec<Instruction>, ParseError> {
|
fn read_number(cs: &mut dyn Iterator<Item=char>) -> isize {
|
||||||
let mut r = vec![];
|
let mut is_positive = 1_isize;
|
||||||
|
if let Some(c) = cs.next() {
|
||||||
let mut cs = ParseChars{ index: 0_usize, chars: lang.chars().filter(|c| {
|
if c == '草' {
|
||||||
VALID_INSTRUCTION_CHARS.chars().any(|vic| vic == *c)
|
is_positive = 1;
|
||||||
}).collect(), };
|
} else if c == '泥' {
|
||||||
while let Some(c) = cs.next() {
|
is_positive = -1;
|
||||||
let nc = match cs.try_next() {
|
} else {
|
||||||
Some(nc) => nc, None => {
|
// Syntax ERROR!
|
||||||
return Err(ParseError::ErrorSyntax(
|
|
||||||
format!("Syntax error: after {} has no valid char", c)
|
|
||||||
));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if c == '草' && nc == '草' { // Push
|
|
||||||
cs.next();
|
|
||||||
// todo parse
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
let nnc = match cs.try_next_n(1) {
|
} else {
|
||||||
Some(nc) => nc, None => {
|
// Syntax ERROR!
|
||||||
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)
|
|
||||||
));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
let mut num = 0_isize;
|
||||||
Ok(r)
|
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);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user