Scheme in Rust

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触ってみたい。

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");
}
view raw rscheme.rs hosted with ❤ by GitHub
Show Comments