メモ > 技術 > プログラミング言語: Rust > プログラミング言語(rustack)
プログラミング言語(rustack)
Rustで作るプログラミング言語 ―コンパイラ/インタプリタの基礎からプログラミング言語の新潮流まで:書籍案内|技術評論社
https://gihyo.jp/book/2024/978-4-297-14192-9
GitHub - msakuta/rustack: A very simple stack based language interpreter in Rust
https://github.com/msakuta/rustack
前述の「コンソールアプリケーション」でも作ったが、書籍の順番どおり、まずは逆ポーランド記法で計算できるプログラムを作成する
以下のとおり、プロジェクトを作成する
>cd C:\Users\refirio\Rust
>cargo new rustack --bin
>cd rustack
■ハードコーディングでの実装
src/main.rs の内容を以下のようにする
(「{stack:?}」という書き方は、前述の「基本の文法」内にある「変数」を参照)
fn main() {
let mut stack = vec![];
stack.push(42);
stack.push(36);
add(&mut stack);
println!("stack: {stack:?}");
}
fn add(stack: &mut Vec<i32>) {
let x = stack.pop().unwrap();
let y = stack.pop().unwrap();
stack.push(x + y);
}
以下のように、計算結果を表示できる
>cargo run
stack: [78]
■標準入力からの読み込み
src/main.rs の内容を以下のようにする
fn main() {
let mut stack = vec![];
stack.push(42);
stack.push(36);
add(&mut stack);
println!("stack: {stack:?}");
}
fn add(stack: &mut Vec<i32>) {
let x = stack.pop().unwrap();
let y = stack.pop().unwrap();
stack.push(x + y);
}
以下のように、入力内容に応じた結果が表示される
終了したい場合、Ctrl+Cで終了する
>cargo run
42 36 + … 入力
Line: ["42", "36", "+"] … 結果が表示される
■パースとコマンドの実行
src/main.rs の内容を以下のようにする
fn main() {
for line in std::io::stdin().lines() {
let mut stack = vec![];
if let Ok(line) = line {
let words: Vec<_> = line.split(" ").collect();
for word in words {
if let Ok(parsed) = word.parse::<i32>() {
stack.push(parsed);
} else {
match word {
"+" => add(&mut stack),
_ => panic!("{word:?} could not be parsed"),
}
}
}
println!("stack: {stack:?}");
}
}
}
fn add(stack: &mut Vec<i32>) {
let x = stack.pop().unwrap();
let y = stack.pop().unwrap();
stack.push(x + y);
}
以下のように、入力内容に応じた計算結果が表示される
終了したい場合、Ctrl+Cで終了する
>cargo run
42 36 + … 入力
stack: [78] … 計算結果が表示される
42 36 + 22 + … 入力
stack: [100] … 計算結果が表示される
■四則計算
src/main.rs の内容を以下のようにする
(直感的な計算結果にするため、各計算の関数内でxとyの順番を逆にしているので注意)
fn main() {
for line in std::io::stdin().lines() {
let mut stack = vec![];
if let Ok(line) = line {
let words: Vec<_> = line.split(" ").collect();
for word in words {
if let Ok(parsed) = word.parse::<i32>() {
stack.push(parsed);
} else {
match word {
"+" => add(&mut stack),
"-" => sub(&mut stack),
"*" => mul(&mut stack),
"/" => div(&mut stack),
"%" => rem(&mut stack),
_ => panic!("{word:?} could not be parsed"),
}
}
}
println!("stack: {stack:?}");
}
}
}
fn add(stack: &mut Vec<i32>) {
let y = stack.pop().unwrap();
let x = stack.pop().unwrap();
stack.push(x + y);
}
fn sub(stack: &mut Vec<i32>) {
let y = stack.pop().unwrap();
let x = stack.pop().unwrap();
stack.push(x - y);
}
fn mul(stack: &mut Vec<i32>) {
let y = stack.pop().unwrap();
let x = stack.pop().unwrap();
stack.push(x * y);
}
fn div(stack: &mut Vec<i32>) {
let y = stack.pop().unwrap();
let x = stack.pop().unwrap();
stack.push(x / y);
}
fn rem(stack: &mut Vec<i32>) {
let y = stack.pop().unwrap();
let x = stack.pop().unwrap();
stack.push(x % y);
}
以下のように、入力内容に応じた計算結果が表示される
終了したい場合、Ctrl+Cで終了する
>cargo run
2 3 + … 入力
stack: [5] … 計算結果が表示される
2 3 - … 入力
stack: [-1] … 計算結果が表示される
2 3 * … 入力
stack: [6] … 計算結果が表示される
2 3 / … 入力
stack: [0] … 計算結果が表示される
2 3 % … 入力
stack: [2] … 計算結果が表示される
■ブロックとネスト構造
src/main.rs の内容を以下のようにする
#[derive(Debug, PartialEq, Eq)]
enum Value<'src> {
Num(i32),
Op(&'src str),
Block(Vec<Value<'src>>),
}
impl<'src> Value<'src> {
fn as_num(&self) -> i32 {
match self {
Self::Num(val) => *val,
_=> panic!("Value is not a number"),
}
}
}
fn main() {
for line in std::io::stdin().lines().flatten() {
parse(&line);
}
}
fn parse<'a>(line: &'a str) -> Vec<Value> {
let mut stack = vec![];
let input: Vec<_> = line.split(" ").collect();
let mut words = &input[..];
while let Some((&word, mut rest)) = words.split_first() {
if word.is_empty() {
break;
}
if word == "{" {
let value;
(value, rest) = parse_block(rest);
stack.push(value);
} else if let Ok(parsed) = word.parse::<i32>() {
stack.push(Value::Num(parsed));
} else {
match word {
"+" => add(&mut stack),
"-" => sub(&mut stack),
"*" => mul(&mut stack),
"/" => div(&mut stack),
"%" => rem(&mut stack),
_ => panic!("{word:?} could not be parsed"),
}
}
words = rest;
}
println!("stack: {stack:?}");
stack
}
fn parse_block<'src, 'a>(input: &'a[&'src str]) -> (Value<'src>, &'a[&'src str]) {
let mut tokens = vec![];
let mut words = input;
while let Some((&word, mut rest)) = words.split_first() {
if word.is_empty() {
break;
}
if word == "{" {
let value;
(value, rest) = parse_block(rest);
tokens.push(value);
} else if word == "}" {
return(Value::Block(tokens), rest);
} else if let Ok(value) = word.parse::<i32>() {
tokens.push(Value::Num(value));
} else {
tokens.push(Value::Op(word));
}
words = rest;
}
(Value::Block(tokens), words)
}
fn add(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num(x + y));
}
fn sub(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num(x - y));
}
fn mul(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num(x * y));
}
fn div(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num(x / y));
}
fn rem(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num(x % y));
}
#[cfg(test)]
mod tests {
use super::{parse, Value::*};
#[test]
fn test_group() {
assert_eq!(
parse("1 2 + { 3 4 }"),
vec![Num(3), Block(vec![Num(3), Num(4)])]
);
}
}
以下のように、ブロックの処理内容が表示される
終了したい場合、Ctrl+Cで終了する
>cargo run
1 1 + … 入力
stack: [Num(2)] … 計算結果が表示される
1 2 + { 3 4 + } 5 … 入力
stack: [Num(3), Block([Num(3), Num(4), Op("+")]), Num(5)] … 計算結果が表示される
1 2 + { 3 4 } … 入力
stack: [Num(3), Block([Num(3), Num(4)])] … 計算結果が表示される
■if制御構文
src/main.rs の内容を以下のようにする
#[derive(Debug, Clone, PartialEq, Eq)]
enum Value<'src> {
Num(i32),
Op(&'src str),
Block(Vec<Value<'src>>),
}
impl<'src> Value<'src> {
fn as_num(&self) -> i32 {
match self {
Self::Num(val) => *val,
_=> panic!("Value is not a number"),
}
}
fn to_block(self) -> Vec<Value<'src>> {
match self {
Self::Block(val) => val,
_=> panic!("Value is not a block"),
}
}
}
fn main() {
for line in std::io::stdin().lines().flatten() {
parse(&line);
}
}
fn parse<'a>(line: &'a str) -> Vec<Value> {
let mut stack = vec![];
let input: Vec<_> = line.split(" ").collect();
let mut words = &input[..];
while let Some((&word, mut rest)) = words.split_first() {
if word.is_empty() {
break;
}
if word == "{" {
let value;
(value, rest) = parse_block(rest);
stack.push(value);
} else {
let code = if let Ok(num) = word.parse::<i32>() {
Value::Num(num)
} else {
Value::Op(word)
};
eval(code, &mut stack);
}
words = rest;
}
println!("stack: {stack:?}");
stack
}
fn eval<'src>(code: Value<'src>, stack: &mut Vec<Value<'src>>) {
match code {
Value::Op(op) => match op {
"+" => add(stack),
"-" => sub(stack),
"*" => mul(stack),
"/" => div(stack),
"%" => rem(stack),
"if" => op_if(stack),
_ => panic!("{op:?} could not be parsed"),
},
_ => stack.push(code.clone()),
}
}
fn parse_block<'src, 'a>(input: &'a[&'src str]) -> (Value<'src>, &'a[&'src str]) {
let mut tokens = vec![];
let mut words = input;
while let Some((&word, mut rest)) = words.split_first() {
if word.is_empty() {
break;
}
if word == "{" {
let value;
(value, rest) = parse_block(rest);
tokens.push(value);
} else if word == "}" {
return(Value::Block(tokens), rest);
} else if let Ok(value) = word.parse::<i32>() {
tokens.push(Value::Num(value));
} else {
tokens.push(Value::Op(word));
}
words = rest;
}
(Value::Block(tokens), words)
}
fn add(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num(x + y));
}
fn sub(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num(x - y));
}
fn mul(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num(x * y));
}
fn div(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num(x / y));
}
fn rem(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num(x % y));
}
fn op_if(stack: &mut Vec<Value>) {
let false_branch = stack.pop().unwrap().to_block();
let true_branch = stack.pop().unwrap().to_block();
let cond = stack.pop().unwrap().to_block();
for code in cond {
eval(code, stack);
}
let cond_result = stack.pop().unwrap().as_num();
if cond_result != 0 {
for code in true_branch {
eval(code, stack);
}
} else {
for code in false_branch {
eval(code, stack);
}
}
}
#[cfg(test)]
mod tests {
use super::{parse, Value::*};
#[test]
fn test_group() {
assert_eq!(
parse("1 2 + { 3 4 }"),
vec![Num(3), Block(vec![Num(3), Num(4)])]
);
}
#[test]
fn test_if_false() {
assert_eq!(
parse("{ 1 -1 + } { 100 } { -100 } if"),
vec![Num(-100)]
);
}
#[test]
fn test_if_true() {
assert_eq!(
parse("{ 1 1 + } { 100 } { -100 } if"),
vec![Num(100)]
);
}
}
>cargo run
{ 1 -1 + } { 100 } { -100 } if … 入力
stack: [Num(-100)] … 処理結果が表示される
{ 1 1 + } { 100 } { -100 } if … 入力
stack: [Num(100)] … 処理結果が表示される
■変数の定義
src/main.rs の内容を以下のようにする
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq)]
enum Value<'src> {
Num(i32),
Op(&'src str),
Sym(&'src str),
Block(Vec<Value<'src>>),
}
impl<'src> Value<'src> {
fn as_num(&self) -> i32 {
match self {
Self::Num(val) => *val,
_=> panic!("Value is not a number"),
}
}
fn to_block(self) -> Vec<Value<'src>> {
match self {
Self::Block(val) => val,
_=> panic!("Value is not a block"),
}
}
fn as_sym(&self) -> &'src str {
if let Self::Sym(sym) = self {
*sym
} else {
panic!("Value is not a symbol")
}
}
}
struct Vm<'src> {
stack: Vec<Value<'src>>,
vars: HashMap<&'src str, Value<'src>>,
}
impl<'src> Vm<'src> {
fn new() -> Self {
Self {
stack: vec![],
vars: HashMap::new(),
}
}
}
fn main() {
for line in std::io::stdin().lines().flatten() {
parse(&line);
}
}
fn parse<'a>(line: &'a str) -> Vec<Value> {
let mut vm = Vm::new();
let input: Vec<_> = line.split(" ").collect();
let mut words = &input[..];
while let Some((&word, mut rest)) = words.split_first() {
if word.is_empty() {
break;
}
if word == "{" {
let value;
(value, rest) = parse_block(rest);
vm.stack.push(value);
} else {
let code = if let Ok(num) = word.parse::<i32>() {
Value::Num(num)
} else if word.starts_with("/") {
Value::Sym(&word[1..])
} else {
Value::Op(word)
};
eval(code, &mut vm);
}
words = rest;
}
println!("stack: {:?}", vm.stack);
vm.stack
}
fn eval<'src>(code: Value<'src>, vm: &mut Vm<'src>) {
match code {
Value::Op(op) => match op {
"+" => add(&mut vm.stack),
"-" => sub(&mut vm.stack),
"*" => mul(&mut vm.stack),
"/" => div(&mut vm.stack),
"%" => rem(&mut vm.stack),
"<" => lt(&mut vm.stack),
">" => gt(&mut vm.stack),
"if" => op_if(vm),
"def" => op_def(vm),
_ => {
let val = vm.vars.get(op).expect(&format!(
"{op:?} is not a defined operation"
));
vm.stack.push(val.clone());
}
},
_ => vm.stack.push(code.clone()),
}
}
fn parse_block<'src, 'a>(input: &'a[&'src str]) -> (Value<'src>, &'a[&'src str]) {
let mut tokens = vec![];
let mut words = input;
while let Some((&word, mut rest)) = words.split_first() {
if word.is_empty() {
break;
}
if word == "{" {
let value;
(value, rest) = parse_block(rest);
tokens.push(value);
} else if word == "}" {
return(Value::Block(tokens), rest);
} else if let Ok(value) = word.parse::<i32>() {
tokens.push(Value::Num(value));
} else {
tokens.push(Value::Op(word));
}
words = rest;
}
(Value::Block(tokens), words)
}
macro_rules! impl_op {
{$name:ident, $op:tt} => {
fn $name(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num((x $op y) as i32));
}
}
}
impl_op!(add, +);
impl_op!(sub, -);
impl_op!(mul, *);
impl_op!(div, /);
impl_op!(rem, %);
impl_op!(lt, <);
impl_op!(gt, >);
fn op_if(vm: &mut Vm) {
let false_branch = vm.stack.pop().unwrap().to_block();
let true_branch = vm.stack.pop().unwrap().to_block();
let cond = vm.stack.pop().unwrap().to_block();
for code in cond {
eval(code, vm);
}
let cond_result = vm.stack.pop().unwrap().as_num();
if cond_result != 0 {
for code in true_branch {
eval(code, vm);
}
} else {
for code in false_branch {
eval(code, vm);
}
}
}
fn op_def(vm: &mut Vm) {
let value = vm.stack.pop().unwrap();
eval(value, vm);
let value = vm.stack.pop().unwrap();
let sym = vm.stack.pop().unwrap().as_sym();
vm.vars.insert(sym, value);
}
#[cfg(test)]
mod tests {
use super::{parse, Value::*};
#[test]
fn test_group() {
assert_eq!(
parse("1 2 + { 3 4 }"),
vec![Num(3), Block(vec![Num(3), Num(4)])]
);
}
#[test]
fn test_if_false() {
assert_eq!(
parse("{ 1 -1 + } { 100 } { -100 } if"),
vec![Num(-100)]
);
}
#[test]
fn test_if_true() {
assert_eq!(
parse("{ 1 1 + } { 100 } { -100 } if"),
vec![Num(100)]
);
}
#[test]
fn test_var() {
assert_eq!(
parse("/x 10 def /y 20 def x y *"),
vec![Num(200)]
);
}
#[test]
fn test_var_if() {
assert_eq!(
parse("/x 10 def /y 20 def { x y < } { x } { y } if"),
vec![Num(10)]
);
}
}
>cargo run
/x 10 def /y 20 def x y * … 入力
stack: [Num(200)] … 処理結果が表示される
/x 10 def /y 20 def { x y < } { x } { y } if … 入力
stack: [Num(10)] … 処理結果が表示される
■複数行のソースコードへの対応
src/main.rs の内容を以下のようにする
use std::{
collections::HashMap,
io::{BufRead, BufReader}
};
#[derive(Debug, Clone, PartialEq, Eq)]
enum Value {
Num(i32),
Op(String),
Sym(String),
Block(Vec<Value>),
}
impl Value {
fn as_num(&self) -> i32 {
match self {
Self::Num(val) => *val,
_=> panic!("Value is not a number"),
}
}
fn to_string(&self) -> String {
match self {
Self::Num(i) => i.to_string(),
Self::Op(ref s) | Self::Sym(ref s) => s.clone(),
Self::Block(_) => "<Block>".to_string(),
}
}
fn as_sym(&self) -> &str {
if let Self::Sym(sym) = self {
sym
} else {
panic!("Value is not a symbol")
}
}
fn to_block(self) -> Vec<Value> {
match self {
Self::Block(val) => val,
_=> panic!("Value is not a block"),
}
}
}
struct Vm {
stack: Vec<Value>,
vars: HashMap<String, Value>,
blocks: Vec<Vec<Value>>,
}
impl Vm {
fn new() -> Self {
Self {
stack: vec![],
vars: HashMap::new(),
blocks: vec![],
}
}
}
fn main() {
if let Some(f) = std::env::args()
.nth(1)
.and_then(|f| std::fs::File::open(f).ok())
{
parse_batch(BufReader::new(f));
} else {
parse_interactive();
}
}
fn parse_batch(source: impl BufRead) -> Vec<Value> {
let mut vm = Vm::new();
for line in source.lines().flatten() {
for word in line.split(" ") {
parse_word(word, &mut vm);
}
}
vm.stack
}
fn parse_interactive() {
let mut vm = Vm::new();
for line in std::io::stdin().lines().flatten() {
for word in line.split(" ") {
parse_word(word, &mut vm);
}
println!("stack: {:?}", vm.stack);
}
}
fn parse_word(word: &str, vm: &mut Vm) {
if word.is_empty() {
return;
}
if word == "{" {
vm.blocks.push(vec![]);
} else if word == "}" {
let top_block = vm.blocks.pop().expect("Block stack underrun!");
eval(Value::Block(top_block), vm);
} else {
let code = if let Ok(num) = word.parse::<i32>() {
Value::Num(num)
} else if word.starts_with("/") {
Value::Sym(word[1..].to_string())
} else {
Value::Op(word.to_string())
};
eval(code, vm);
}
}
fn eval(code: Value, vm: &mut Vm) {
if let Some(top_block) = vm.blocks.last_mut() {
top_block.push(code);
return;
}
match code {
Value::Op(ref op) => match op as &str {
"+" => add(&mut vm.stack),
"-" => sub(&mut vm.stack),
"*" => mul(&mut vm.stack),
"/" => div(&mut vm.stack),
"%" => rem(&mut vm.stack),
"<" => lt(&mut vm.stack),
">" => gt(&mut vm.stack),
"if" => op_if(vm),
"def" => op_def(vm),
"puts" => puts(vm),
_ => {
let val = vm.vars.get(op).expect(&format!(
"{op:?} is not a defined operation"
));
vm.stack.push(val.clone());
}
},
_ => vm.stack.push(code.clone()),
}
}
macro_rules! impl_op {
{$name:ident, $op:tt} => {
fn $name(stack: &mut Vec<Value>) {
let y = stack.pop().unwrap().as_num();
let x = stack.pop().unwrap().as_num();
stack.push(Value::Num((x $op y) as i32));
}
}
}
impl_op!(add, +);
impl_op!(sub, -);
impl_op!(mul, *);
impl_op!(div, /);
impl_op!(rem, %);
impl_op!(lt, <);
impl_op!(gt, >);
fn op_if(vm: &mut Vm) {
let false_branch = vm.stack.pop().unwrap().to_block();
let true_branch = vm.stack.pop().unwrap().to_block();
let cond = vm.stack.pop().unwrap().to_block();
for code in cond {
eval(code, vm);
}
let cond_result = vm.stack.pop().unwrap().as_num();
if cond_result != 0 {
for code in true_branch {
eval(code, vm);
}
} else {
for code in false_branch {
eval(code, vm);
}
}
}
fn op_def(vm: &mut Vm) {
let value = vm.stack.pop().unwrap();
eval(value, vm);
let value = vm.stack.pop().unwrap();
let sym = vm.stack.pop().unwrap().as_sym().to_string();
vm.vars.insert(sym, value);
}
fn puts(vm: &mut Vm) {
let value = vm.stack.pop().unwrap();
println!("{}", value.to_string());
}
#[cfg(test)]
mod tests {
use super::{Value::*, *};
use std::io::Cursor;
fn parse(input: &str) -> Vec<Value> {
parse_batch(Cursor::new(input))
}
#[test]
fn test_group() {
assert_eq!(
parse("1 2 + { 3 4 }"),
vec![Num(3), Block(vec![Num(3), Num(4)])]
);
}
#[test]
fn test_if_false() {
assert_eq!(
parse("{ 1 -1 + } { 100 } { -100 } if"),
vec![Num(-100)]
);
}
#[test]
fn test_if_true() {
assert_eq!(
parse("{ 1 1 + } { 100 } { -100 } if"),
vec![Num(100)]
);
}
#[test]
fn test_var() {
assert_eq!(
parse("/x 10 def /y 20 def x y *"),
vec![Num(200)]
);
}
#[test]
fn test_var_if() {
assert_eq!(
parse("/x 10 def /y 20 def { x y < } { x } { y } if"),
vec![Num(10)]
);
}
#[test]
fn test_multiline() {
assert_eq!(
parse(
r#"
/x 10 def
/y 20 def
{
x y <
}
{
x
}
{
y
} if
"#
),
vec![Num(10)]
);
}
}
input.txt を作成し、以下の内容を記述する
/x 10 def
/y 20 def
{
x y <
}
{
x
}
{
y
} if
puts
>cargo run -- input.txt
10
なお、プログラム中にある「and_then」は、成功した場合のみ処理を実行するもの
「(|f| std::fs::File::open(f).ok())」については、前述の「クロージャ」を参照
■関数呼び出し
src/main.rs の内容を以下のようにする
use std::{
collections::HashMap,
io::{BufRead, BufReader}
};
#[derive(Debug, Clone, PartialEq, Eq)]
enum Value {
Num(i32),
Op(String),
Sym(String),
Block(Vec<Value>),
Native(NativeOp)
}
impl Value {
fn as_num(&self) -> i32 {
match self {
Self::Num(val) => *val,
_=> panic!("Value is not a number"),
}
}
fn to_string(&self) -> String {
match self {
Self::Num(i) => i.to_string(),
Self::Op(ref s) | Self::Sym(ref s) => s.clone(),
Self::Block(_) => "<Block>".to_string(),
Self::Native(_) => "<Native>".to_string(),
}
}
fn as_sym(&self) -> &str {
if let Self::Sym(sym) = self {
sym
} else {
panic!("Value is not a symbol")
}
}
fn to_block(self) -> Vec<Value> {
match self {
Self::Block(val) => val,
_=> panic!("Value is not a block"),
}
}
}
#[derive(Clone)]
struct NativeOp(fn(&mut Vm));
impl PartialEq for NativeOp {
fn eq(&self, other: &NativeOp) -> bool {
self.0 as *const fn() == other.0 as *const fn()
}
}
impl Eq for NativeOp {}
impl std::fmt::Debug for NativeOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<NativeOp>")
}
}
struct Vm {
stack: Vec<Value>,
vars: HashMap<String, Value>,
blocks: Vec<Vec<Value>>,
}
impl Vm {
fn new() -> Self {
let functions: [(&str, fn(&mut Vm)); 14] = [
("+", add),
("-", sub),
("*", mul),
("/", div),
("%", rem),
("<", lt),
(">", gt),
("if", op_if),
("def", op_def),
("puts", puts),
("pop", pop),
("dup", dup),
("exch", exch),
("index", index),
];
Self {
stack: vec![],
vars: functions
.into_iter()
.map(|(name, fun)| {
(name.to_owned(), Value::Native(NativeOp(fun)))
})
.collect(),
blocks: vec![],
}
}
}
fn main() {
if let Some(f) = std::env::args()
.nth(1)
.and_then(|f| std::fs::File::open(f).ok())
{
parse_batch(BufReader::new(f));
} else {
parse_interactive();
}
}
fn parse_batch(source: impl BufRead) -> Vec<Value> {
let mut vm = Vm::new();
for line in source.lines().flatten() {
for word in line.split(" ") {
parse_word(word, &mut vm);
}
}
vm.stack
}
fn parse_interactive() {
let mut vm = Vm::new();
for line in std::io::stdin().lines().flatten() {
for word in line.split(" ") {
parse_word(word, &mut vm);
}
println!("stack: {:?}", vm.stack);
}
}
fn parse_word(word: &str, vm: &mut Vm) {
if word.is_empty() {
return;
}
if word == "{" {
vm.blocks.push(vec![]);
} else if word == "}" {
let top_block = vm.blocks.pop().expect("Block stack underrun!");
eval(Value::Block(top_block), vm);
} else {
let code = if let Ok(num) = word.parse::<i32>() {
Value::Num(num)
} else if word.starts_with("/") {
Value::Sym(word[1..].to_string())
} else {
Value::Op(word.to_string())
};
eval(code, vm);
}
}
fn eval(code: Value, vm: &mut Vm) {
if let Some(top_block) = vm.blocks.last_mut() {
top_block.push(code);
return;
}
if let Value::Op(ref op) = code {
let val = vm.vars.get(op).expect(&format!(
"{op:?} is not a defined operation"
)).clone();
match val {
Value::Block(block) => {
for code in block {
eval(code, vm);
}
}
Value::Native(op) => op.0(vm),
_ => vm.stack.push(val),
}
} else {
vm.stack.push(code.clone());
}
}
macro_rules! impl_op {
{$name:ident, $op:tt} => {
fn $name(vm: &mut Vm) {
let y = vm.stack.pop().unwrap().as_num();
let x = vm.stack.pop().unwrap().as_num();
vm.stack.push(Value::Num((x $op y) as i32));
}
}
}
impl_op!(add, +);
impl_op!(sub, -);
impl_op!(mul, *);
impl_op!(div, /);
impl_op!(rem, %);
impl_op!(lt, <);
impl_op!(gt, >);
fn op_if(vm: &mut Vm) {
let false_branch = vm.stack.pop().unwrap().to_block();
let true_branch = vm.stack.pop().unwrap().to_block();
let cond = vm.stack.pop().unwrap().to_block();
for code in cond {
eval(code, vm);
}
let cond_result = vm.stack.pop().unwrap().as_num();
if cond_result != 0 {
for code in true_branch {
eval(code, vm);
}
} else {
for code in false_branch {
eval(code, vm);
}
}
}
fn op_def(vm: &mut Vm) {
let value = vm.stack.pop().unwrap();
eval(value, vm);
let value = vm.stack.pop().unwrap();
let sym = vm.stack.pop().unwrap().as_sym().to_string();
vm.vars.insert(sym, value);
}
fn puts(vm: &mut Vm) {
let value = vm.stack.pop().unwrap();
println!("{}", value.to_string());
}
fn pop(vm: &mut Vm) {
vm.stack.pop().unwrap();
}
fn dup(vm: &mut Vm) {
let value = vm.stack.last().unwrap();
vm.stack.push(value.clone());
}
fn exch(vm: &mut Vm) {
let last = vm.stack.pop().unwrap();
let second = vm.stack.pop().unwrap();
vm.stack.push(last);
vm.stack.push(second);
}
fn index(vm: &mut Vm) {
let index = vm.stack.pop().unwrap().as_num() as usize;
let value = vm.stack[vm.stack.len() - index - 1].clone();
vm.stack.push(value);
}
#[cfg(test)]
mod tests {
use super::{Value::*, *};
use std::io::Cursor;
fn parse(input: &str) -> Vec<Value> {
parse_batch(Cursor::new(input))
}
#[test]
fn test_group() {
assert_eq!(
parse("1 2 + { 3 4 }"),
vec![Num(3), Block(vec![Num(3), Num(4)])]
);
}
#[test]
fn test_if_false() {
assert_eq!(
parse("{ 1 -1 + } { 100 } { -100 } if"),
vec![Num(-100)]
);
}
#[test]
fn test_if_true() {
assert_eq!(
parse("{ 1 1 + } { 100 } { -100 } if"),
vec![Num(100)]
);
}
#[test]
fn test_var() {
assert_eq!(
parse("/x 10 def /y 20 def x y *"),
vec![Num(200)]
);
}
#[test]
fn test_var_if() {
assert_eq!(
parse("/x 10 def /y 20 def { x y < } { x } { y } if"),
vec![Num(10)]
);
}
#[test]
fn test_multiline() {
assert_eq!(
parse(
r#"
/x 10 def
/y 20 def
{
x y <
}
{
x
}
{
y
} if
"#
),
vec![Num(10)]
);
}
#[test]
fn test_function() {
assert_eq!(
parse(
r#"
/double { 2 * } def
10 double
"#
),
vec![Num(20)]
);
}
}
input.txt を作成し、以下の内容を記述する
/double { 2 * } def
/square { dup * } def
10 double puts
10 square puts
>cargo run -- input.txt
20
100
今度は input.txt の内容を以下のように変更する
これで10の階上を計算できる(関数の再帰呼び出しができていることを確認できる)
/factorial { 1 factorial_int } def
/factorial_int {
/acc exch def
/n exch def
{ n 2 < }
{ acc }
{
n 1 -
acc n *
factorial_int
}
if
} def
10 factorial puts
>cargo run -- input.txt
3628800
■ローカル変数
引き続き…