Rust 1.0 alpha がリリースされたのでRustを書いてみた。
FScheme - Scheme in F# - Code Monkey Have Fun - Site Home - MSDN Blogs
このサイトを参考にSchemeをさわりだけ実装。 減算・乗算・if式が実行可能。
今までGCある言語しか使ってこなかった身としては結構つらいところある。 C/C++の素養があればもっとスムーズに始められと思われる。
enumにclosure入れるのとかやっとの事で理解できた。
次はRust製のゲームエンジンpiston触ってみたい。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mod tokenizer { | |
#[derive(Eq, PartialEq, Show)] | |
pub enum Token { | |
Open, Close, | |
String(String), | |
Number(String), | |
Symbol(String), | |
} | |
/// LISPのS式を字句解析する | |
pub fn tokenize(input: &str) -> Vec<Token> { | |
let input_chars = input.clone().chars().collect::<Vec<char>>(); | |
let mut chars = input_chars.as_slice(); | |
let mut tokens = Vec::new(); | |
while chars.len() > 0 { | |
chars = match chars { | |
['(', rest..] => { | |
tokens.push(Token::Open); | |
rest | |
}, | |
[')', rest..] => { | |
tokens.push(Token::Close); | |
rest | |
}, | |
['"', rest..] => { | |
let (s, rest) = string(String::new(), rest); | |
tokens.push(Token::String(s)); | |
rest | |
}, | |
[c, rest..] if c.is_whitespace() => { | |
rest | |
}, | |
[c, rest..] if c == '+' || c.is_digit(10) || (c == '-' && rest[0].is_digit(10)) => { | |
let (s, rest) = if c == '+' { | |
token(String::new(), rest) | |
} else { | |
token(c.to_string(), rest) | |
}; | |
tokens.push(Token::Number(s)); | |
rest | |
}, | |
[c, rest..] => { | |
let (s, rest) = token(c.to_string(), rest); | |
tokens.push(Token::Symbol(s)); | |
rest | |
}, | |
[] => {chars}, | |
} | |
} | |
fn string(mut acc: String, mut chars: &[char]) -> (String, &[char]) { | |
loop { | |
match chars { | |
['"', rest..] => { | |
chars = rest; | |
break; | |
}, | |
[ c , rest..] => { | |
acc = acc + c.to_string().as_slice(); | |
chars = rest; | |
}, | |
[] => panic!("String quotation is not closed."), | |
} | |
} | |
(acc, chars) | |
} | |
fn token(mut acc: String, mut chars: &[char]) -> (String, &[char]) { | |
loop { | |
match chars { | |
[c, ..] if c.is_whitespace() || c == ')' => { | |
break; | |
}, | |
[c, rest..] => { | |
acc = acc + c.to_string().as_slice(); | |
chars = rest; | |
}, | |
[] => { break;} | |
} | |
} | |
(acc, chars) | |
} | |
tokens | |
} | |
} | |
mod printer { | |
use parser::Expression; | |
pub fn print(exp: &Expression) -> String { | |
let mut s = "".to_string(); | |
let mut nest_stack = Vec::new(); | |
let mut vecs = vec!(exp.clone()); | |
let mut slice = vecs.as_slice(); | |
match exp { | |
&Expression::List(ref lst) => slice = lst.as_slice(), | |
_ => {}, | |
}; | |
nest_stack.push(slice); | |
loop { | |
let nest_len = nest_stack.len() - 1; | |
match nest_stack[nest_len] { | |
[Expression::List(ref ls), rest..] => { | |
s = s + "("; | |
nest_stack.push(ls.as_slice()); | |
nest_stack[nest_len] = rest; | |
continue; | |
}, | |
[Expression::Number(ref num), rest..] => { | |
s = s + num.to_string().as_slice() + " "; | |
nest_stack[nest_len] = rest; | |
continue; | |
}, | |
[Expression::String(ref st), rest..] => { | |
s = s + st.as_slice() + " "; | |
nest_stack[nest_len] = rest; | |
continue; | |
}, | |
[Expression::Symbol(ref st), rest..] => { | |
s = s + st.as_slice() + " "; | |
nest_stack[nest_len] = rest; | |
continue; | |
}, | |
[] => { | |
if nest_stack.len() == 1 { | |
s.pop(); | |
break; | |
} else { | |
s = s + ")"; | |
nest_stack.pop(); | |
continue; | |
} | |
}, | |
} | |
} | |
s | |
} | |
} | |
mod parser { | |
use tokenizer::Token; | |
use std::fmt; | |
#[derive(Eq, PartialEq, Show, Clone)] | |
pub enum Expression { | |
Number(isize), | |
String(String), | |
Symbol(String), | |
List(Vec<Expression>), | |
} | |
pub fn parse(tokens: &Vec<Token>) -> Vec<Expression> { | |
fn map(token: &Token) -> Expression { | |
match token { | |
&Token::Number(ref st) => Expression::Number(st.parse::<isize>().unwrap()), | |
&Token::String(ref st) => Expression::String(st.clone()), | |
&Token::Symbol(ref st) => Expression::Symbol(st.clone()), | |
_ => panic!(), | |
} | |
} | |
let mut tokens_slice = tokens.as_slice(); | |
let mut nest_stack = Vec::new(); | |
nest_stack.push(Vec::new()); | |
let mut result = Vec::new(); | |
loop { | |
match tokens_slice { | |
[Token::Open, rest..] => { | |
nest_stack.push(Vec::new()); | |
tokens_slice = rest; | |
continue; | |
}, | |
[Token::Close, rest..] => { | |
let top = nest_stack.pop().unwrap(); | |
if nest_stack.is_empty() { | |
result.push(Expression::List(top)); | |
} else { | |
let last_index = nest_stack.len() - 1; | |
nest_stack[last_index].push(Expression::List(top)); | |
} | |
tokens_slice = rest; | |
continue; | |
}, | |
[ref head, rest..] => { | |
let last_index = nest_stack.len() - 1; | |
nest_stack[last_index].push(map(head)); | |
tokens_slice = rest; | |
continue; | |
}, | |
[] => { | |
if nest_stack.is_empty() == false { | |
let top = nest_stack.pop().unwrap(); | |
if nest_stack.is_empty() == false { panic!("invalid tokens") } | |
result.push(top[0].clone()); | |
} | |
break; | |
}, | |
} | |
} | |
result | |
} | |
} | |
mod pritimives { | |
use parser::Expression; | |
use super::evaler::eval; | |
fn unwrap_num(arg: &Expression) -> isize { | |
match arg { | |
&Expression::Number(num) => { num }, | |
_ => { panic!("Malformed multiplication arguments.") }, | |
} | |
} | |
pub fn multiply(args: &[Expression]) -> Expression { | |
let result = args.iter() | |
.map(unwrap_num) | |
.fold(1is, |a, b| a * b); | |
Expression::Number(result) | |
} | |
pub fn subtract(args: &[Expression]) -> Expression { | |
match args { | |
[] => Expression::Number(0is), | |
[Expression::Number(n)] => Expression::Number(- n), | |
[Expression::Number(n), rest..] => { | |
let result = rest.iter() | |
.map(unwrap_num) | |
.fold(n, |a, b| a - b); | |
Expression::Number(result) | |
}, | |
_ => panic!("Malformed sbtract."), | |
} | |
} | |
pub fn if_(args: &[Expression]) -> Expression { | |
match args { | |
[ref cond, ref t, ref f] => { | |
match eval(cond) { | |
Expression::List(ref lst) if lst.is_empty() => { eval(f) }, | |
Expression::String(ref st) if st.is_empty() => { eval(f) }, | |
Expression::Number(num) if num == 0 => { eval(f) }, | |
_ => { eval(t) }, | |
} | |
}, | |
[_..] => panic!("Malformed 'if'."), | |
} | |
} | |
} | |
mod evaler { | |
use parser::Expression; | |
use pritimives; | |
enum Form<'f> { | |
Function(Box<Fn(&[Expression]) -> Expression + 'f>), | |
Special(Box<Fn(&[Expression]) -> Expression + 'f>), | |
} | |
pub fn eval(exp: &Expression) -> Expression { | |
fn transform_symbol(exp: &Expression) -> Form { | |
match exp { | |
&Expression::Symbol(ref symbol) => { | |
match symbol.as_slice() { | |
"*" => { Form::Function(Box::new(pritimives::multiply)) }, | |
"-" => { Form::Function(Box::new(pritimives::subtract)) }, | |
"if" => { Form::Special(Box::new(pritimives::if_)) }, | |
_ => panic!("Malformed symbol"), | |
} | |
}, | |
_ => panic!("not symbol!"), | |
} | |
} | |
match exp { | |
&Expression::Number(_) => exp.clone(), | |
&Expression::String(_) => exp.clone(), | |
&Expression::List(ref lst) if lst.is_empty() => exp.clone(), | |
&Expression::List(ref lst) => { | |
match lst.as_slice() { | |
[ref h, t..] => { | |
match transform_symbol(h) { | |
Form::Function(f) => apply(f, t), | |
Form::Special(f) => f(t), | |
} | |
}, | |
[] => panic!("Malformed List expression."), | |
} | |
}, | |
_ => panic!("Malformed expression"), | |
} | |
} | |
fn apply(f: Box<for <'r> Fn(&'r [Expression]) -> Expression>, args: &[Expression]) -> Expression { | |
let temp_vec: Vec<Expression> = args.iter().map(|a| eval(a)).collect(); | |
let temp_args = temp_vec.as_slice(); | |
(*f)(temp_args) | |
} | |
} | |
fn rep(src: &str) -> String { | |
use tokenizer::tokenize; | |
use parser::{ parse, Expression }; | |
use evaler::eval; | |
use printer::print; | |
let tokens = tokenize(src); | |
let exps = parse(&tokens); | |
let result = eval(&exps[0]); | |
print(&result) | |
} | |
fn main() { | |
println!("Welcome to RScheme"); | |
loop { | |
print!("> "); | |
let input = std::io::stdin().read_line().ok().expect("failed to read line."); | |
let output = rep(input.as_slice()); | |
println!("{}", output.as_slice()); | |
} | |
} | |
#[test] | |
fn rep_test() { | |
fn case(src: &str, expected: &str) { | |
println!("source: {}, expected: {}", src, expected); | |
let output = rep(src); | |
assert_eq!(output, expected); | |
} | |
case("1", "1"); | |
case("+1", "1"); | |
case("-1", "-1"); | |
case(r##""hello""##, r##"hello"##); | |
case("(*)", "1"); | |
case("(* 2 3)", "6"); | |
case("(* 2 3 4)", "24"); | |
case("(-)", "0"); | |
case("(- 10)", "-10"); | |
case("(- 10 2)", "8"); | |
case("(if (* 0 1) 10 20)", "20"); | |
case("(if (* 1 1) 10 20)", "10"); | |
case("(if (* 1 1) 10 bomb)", "10"); | |
} |