BufReader
use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;
fn main() -> std::io::Result<()> {
let f = File::open("./src/address.rs")?;
let mut reader = BufReader::new(f);
let mut line = String::new();
let len = reader.read_line(&mut line)?;
println!("First line is {len} bytes long");
Ok(())
}
ファイルを読み込んで1行ずつ処理の際は同じになる。
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_block(self) -> Vec<Value> {
match self {
Self::Block(val) => val,
_ => panic!("Value is not a block"),
}
}
fn as_sym(&self) -> &str {
if let Self::Sym(sym) = self {
sym
} else {
panic!("Value is not a symbol");
}
}
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(),
}
}
}
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<'src>(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),
"<" => lt(&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 rhs = stack.pop().unwrap().as_num();
let lhs = stack.pop().unwrap().as_num();
stack.push(Value::Num((lhs $op rhs) as i32));
}
}
}
impl_op!(add, +);
impl_op!(sub, -);
impl_op!(mul, *);
impl_op!(div, /);
impl_op!(lt, <);
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 test {
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)]
);
}
}
$ cargo run — if.txt
Compiling stackmachine v0.1.0 (/home/vagrant/dev/rust/stackmachine)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.49s
Running `target/debug/stackmachine if.txt`
10