feat(read_write): WIP read and write impl

main
Flavien Henrion 2023-12-18 19:04:50 +01:00
parent 7567312055
commit c84414b23c
12 changed files with 254 additions and 80 deletions

BIN
.DS_Store vendored

Binary file not shown.

25
Cargo.lock generated
View File

@ -401,6 +401,7 @@ dependencies = [
"lazy_static",
"pest",
"pest_derive",
"termion",
"tracing-subscriber",
"tracing-wasm",
"wasm-bindgen-futures",
@ -1028,6 +1029,12 @@ dependencies = [
"syn 2.0.39",
]
[[package]]
name = "numtoa"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]]
name = "objc"
version = "0.2.7"
@ -1276,6 +1283,12 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_termios"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
[[package]]
name = "same-file"
version = "1.0.6"
@ -1401,6 +1414,18 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "termion"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4648c7def6f2043b2568617b9f9b75eae88ca185dbc1f1fda30e95a85d49d7d"
dependencies = [
"libc",
"libredox",
"numtoa",
"redox_termios",
]
[[package]]
name = "thiserror"
version = "1.0.37"

View File

@ -9,6 +9,7 @@ edition = "2021"
lazy_static = "1.4.0"
pest = "2.7.5"
pest_derive = "2.7.5"
termion = "2.0.3"
# web
egui = "0.24.0"
eframe = { version = "0.24.0", default-features = false, features = [

View File

@ -55,14 +55,17 @@ var: z => val: 8
- string / char => indexing OK / escaping OK
- local scope for { statements } OK
- array OK => len keyword OK
- read / write => file, standard io, network
- dict
- read / write => file, standard io
- sleep
- program arguments
- standard lib
- tuple
- dict
- standard lib ?
- types declaration
- type checking before runtime
- types inference
- structs => methods (list container?)
- read / write => network sockets
- for loop and iterators NOK
- pow / bitwise / bitshift operators
- range

View File

@ -42,11 +42,11 @@ impl ValueCompute for Value {
(Value::Char(l), Value::Char(r)) => Ok(Value::Char(l + r)),
(Value::Char(l), Value::Number(r)) => match l.checked_add(*r as u8) {
Some(n) => Ok(Value::Char(n)),
None => Err(format!("Char overflow: '{}' + {}", char::from(*l), r)),
None => Err(format!("Char overflow: {} + {}", char::from(*l), r)),
},
(Value::Number(l), Value::Char(r)) => match l.checked_add(*r as i64) {
Some(n) => Ok(Value::Number(n)),
None => Err(format!("Integer overflow: {} + '{}'", l, char::from(*r))),
None => Err(format!("Integer overflow: {} + {}", l, char::from(*r))),
},
(Value::String(l), Value::String(r)) => {
let new = l.iter().chain(r.iter()).copied().collect();
@ -61,7 +61,11 @@ impl ValueCompute for Value {
new.push(r.clone());
Ok(Value::Array(Rc::new(new)))
}
(l, r) => Err(format!("Cannot add {} to {}", l, r)),
(_, _) => Err(format!(
"Cannot add {} and {}",
self.get_type(),
right.get_type()
)),
}
}
@ -74,20 +78,24 @@ impl ValueCompute for Value {
(Value::Char(l), Value::Char(r)) => match l.checked_sub(*r) {
Some(n) => Ok(Value::Char(n)),
None => Err(format!(
"Char overflow: '{}' - '{}'",
"Char overflow: {} - {}",
char::from(*l),
char::from(*r)
)),
},
(Value::Char(l), Value::Number(r)) => match l.checked_sub(*r as u8) {
Some(n) => Ok(Value::Char(n)),
None => Err(format!("Char overflow: '{}' - {}", char::from(*l), r)),
None => Err(format!("Char overflow: {} - {}", char::from(*l), r)),
},
(Value::Number(l), Value::Char(r)) => match l.checked_sub(*r as i64) {
Some(n) => Ok(Value::Number(n)),
None => Err(format!("Integer overflow: {} - '{}'", l, char::from(*r))),
None => Err(format!("Integer overflow: {} - {}", l, char::from(*r))),
},
(l, r) => Err(format!("Cannot subtract {} from {}", r, l)),
(_, _) => Err(format!(
"Cannot subtract {} and {}",
self.get_type(),
right.get_type()
)),
}
}
@ -112,7 +120,11 @@ impl ValueCompute for Value {
.collect::<Vec<Value>>(),
)))
}
(l, r) => Err(format!("Cannot multiply {} by {}", l, r)),
(_, _) => Err(format!(
"Cannot multiply {} and {}",
self.get_type(),
right.get_type()
)),
}
}
@ -125,7 +137,11 @@ impl ValueCompute for Value {
Ok(Value::Number(l / r))
}
}
(l, r) => Err(format!("Cannot divide {} by {}", l, r)),
(_, _) => Err(format!(
"Cannot divide {} and {}",
self.get_type(),
right.get_type()
)),
}
}
@ -138,9 +154,10 @@ impl ValueCompute for Value {
Ok(Value::Number(l % r))
}
}
(l, r) => Err(format!(
"Cannot calculate the remainder with {} divisor of {}",
l, r
(_, _) => Err(format!(
"Cannot modulo {} and {}",
self.get_type(),
right.get_type()
)),
}
}

View File

@ -1,4 +1,4 @@
use std::rc::Rc;
use std::{io::Read, rc::Rc};
use super::{
compute::{BinaryCompute, UnaryCompute},
@ -142,6 +142,23 @@ impl EvaluateExpr for Expr {
.collect::<Result<Vec<Value>, String>>()?;
Ok(Value::Array(Rc::new(values)))
}
Expr::Read(ref name) => match name.evaluate(env)?.to_string().as_str() {
"stdin" => {
let mut buffer = String::new();
std::io::stdin()
.read_line(&mut buffer)
.expect("Error in reading from stdin");
Ok(Value::String(Rc::new(buffer.into_bytes())))
}
path => {
let mut f = std::fs::File::open(path)
.map_err(|_| format!("Error in opening file {}", path))?;
let mut content = String::new();
f.read_to_string(&mut content)
.map_err(|_| format!("Error in reading file {}", path))?;
Ok(Value::String(Rc::new(content.into_bytes())))
}
},
}
}
}

View File

@ -1,10 +1,13 @@
use std::rc::Rc;
use std::{
io::{stderr, stdout, Write},
rc::Rc,
};
use super::{
environment::{Environment, GlobalScope, LocalScope},
evaluate::{EvaluateExpr, EvaluateValue},
};
use crate::parser::syntax::{Stat, Value};
use crate::parser::syntax::{Stat, Value, WriteMode};
// can be used to add continue/exit
pub enum ControlFlow {
@ -123,6 +126,52 @@ impl Execution for Stat {
println!("{}", expr.evaluate(env)?);
Ok(ControlFlow::Next)
}
Stat::Write(ref expr, ref mode, ref name) => {
let value = expr.evaluate(env)?;
let name = name.evaluate(env)?.to_string();
match name.as_str() {
"stdout" => {
if let WriteMode::Overwrite = mode {
print!("\r{}", termion::clear::CurrentLine);
}
print!("{}", value);
stdout().flush().unwrap();
Ok(ControlFlow::Next)
}
"stderr" => {
if let WriteMode::Overwrite = mode {
eprint!("{}", termion::clear::CurrentLine);
}
eprint!("{}", value);
stderr().flush().unwrap();
Ok(ControlFlow::Next)
}
path => {
match mode {
WriteMode::Overwrite => {
let mut file = std::fs::File::create(path).map_err(|e| {
format!("Error in creating file {}: {}", path, e)
})?;
write!(file, "{}", value).map_err(|e| {
format!("Error in writing to file {}: {}", path, e)
})?;
}
WriteMode::Append => {
let mut file = std::fs::OpenOptions::new()
.append(true)
.open(path)
.map_err(|e| {
format!("Error in opening file {}: {}", path, e)
})?;
write!(file, "{}", value).map_err(|e| {
format!("Error in writing to file {}: {}", path, e)
})?;
}
}
Ok(ControlFlow::Next)
}
}
}
}
}
}

View File

@ -125,7 +125,7 @@ mod tests {
fn test_arithmetic_add_char_nb_overflow() {
let environment = run_string("var x = 'z' + 200;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Char overflow: 'z' + 200")
assert_eq!(environment.unwrap_err(), "Char overflow: z + 200")
}
#[test]
@ -144,7 +144,7 @@ mod tests {
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Integer overflow: 9223372036854775807 + 'z'"
"Integer overflow: 9223372036854775807 + z"
)
}
@ -192,28 +192,28 @@ mod tests {
fn test_arithmetic_add_nb_str() {
let environment = run_string("var x = 3 + \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot add 3 to hello")
assert_eq!(environment.unwrap_err(), "Cannot add number and string")
}
#[test]
fn test_arithmetic_add_bool_str() {
let environment = run_string("var x = true + \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot add true to hello")
assert_eq!(environment.unwrap_err(), "Cannot add boolean and string")
}
#[test]
fn test_arithmetic_add_nb_bool() {
let environment = run_string("var x = 3 + true;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot add 3 to true")
assert_eq!(environment.unwrap_err(), "Cannot add number and boolean")
}
#[test]
fn test_arithmetic_add_bool_bool() {
let environment = run_string("var x = true + false;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot add true to false")
assert_eq!(environment.unwrap_err(), "Cannot add boolean and boolean")
}
#[test]
@ -250,7 +250,7 @@ mod tests {
fn test_arithmetic_sub_char_nb_overflow() {
let environment = run_string("var x = 'a' - 200;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Char overflow: 'a' - 200")
assert_eq!(environment.unwrap_err(), "Char overflow: a - 200")
}
#[test]
@ -269,7 +269,7 @@ mod tests {
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Integer overflow: -9223372036854775807 - 'z'"
"Integer overflow: -9223372036854775807 - z"
)
}
@ -287,49 +287,64 @@ mod tests {
fn test_arithmetic_sub_char_char_overflow() {
let environment = run_string("var x = 'a' - 'z';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Char overflow: 'a' - 'z'")
assert_eq!(environment.unwrap_err(), "Char overflow: a - z")
}
#[test]
fn test_arithmetic_sub_str_str() {
let environment = run_string("var x = \"hello\" - \"world\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract world from hello")
assert_eq!(
environment.unwrap_err(),
"Cannot subtract string and string"
)
}
#[test]
fn test_arithmetic_sub_str_char() {
let environment = run_string("var x = \"hello\" - 'a';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract 'a' from hello")
assert_eq!(environment.unwrap_err(), "Cannot subtract string and char")
}
#[test]
fn test_arithmetic_sub_nb_str() {
let environment = run_string("var x = 3 - \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract hello from 3")
assert_eq!(
environment.unwrap_err(),
"Cannot subtract number and string"
)
}
#[test]
fn test_arithmetic_sub_bool_str() {
let environment = run_string("var x = true - \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract hello from true")
assert_eq!(
environment.unwrap_err(),
"Cannot subtract boolean and string"
)
}
#[test]
fn test_arithmetic_sub_nb_bool() {
let environment = run_string("var x = 3 - true;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract true from 3")
assert_eq!(
environment.unwrap_err(),
"Cannot subtract number and boolean"
)
}
#[test]
fn test_arithmetic_sub_bool_bool() {
let environment = run_string("var x = true - false;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot subtract false from true")
assert_eq!(
environment.unwrap_err(),
"Cannot subtract boolean and boolean"
)
}
#[test]
@ -376,21 +391,24 @@ mod tests {
fn test_arithmetic_mul_char_char() {
let environment = run_string("var x = 'a' * 'b';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply 'a' by 'b'")
assert_eq!(environment.unwrap_err(), "Cannot multiply char and char")
}
#[test]
fn test_arithmetic_mul_str_str() {
let environment = run_string("var x = \"hello\" * \"world\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply hello by world")
assert_eq!(
environment.unwrap_err(),
"Cannot multiply string and string"
)
}
#[test]
fn test_arithmetic_mul_str_char() {
let environment = run_string("var x = \"hello\" * 'a';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply hello by 'a'")
assert_eq!(environment.unwrap_err(), "Cannot multiply string and char")
}
#[test]
@ -407,21 +425,30 @@ mod tests {
fn test_arithmetic_mul_bool_str() {
let environment = run_string("var x = true * \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply true by hello")
assert_eq!(
environment.unwrap_err(),
"Cannot multiply boolean and string"
)
}
#[test]
fn test_arithmetic_mul_nb_bool() {
let environment = run_string("var x = 3 * true;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply 3 by true")
assert_eq!(
environment.unwrap_err(),
"Cannot multiply number and boolean"
)
}
#[test]
fn test_arithmetic_mul_bool_bool() {
let environment = run_string("var x = true * false;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot multiply true by false")
assert_eq!(
environment.unwrap_err(),
"Cannot multiply boolean and boolean"
)
}
#[test]
@ -438,56 +465,59 @@ mod tests {
fn test_arithmetic_div_char_nb() {
let environment = run_string("var x = 'a' / 3;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide 'a' by 3")
assert_eq!(environment.unwrap_err(), "Cannot divide char and number")
}
#[test]
fn test_arithmetic_div_char_char() {
let environment = run_string("var x = 'a' / 'b';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide 'a' by 'b'")
assert_eq!(environment.unwrap_err(), "Cannot divide char and char")
}
#[test]
fn test_arithmetic_div_str_str() {
let environment = run_string("var x = \"hello\" / \"world\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide hello by world")
assert_eq!(environment.unwrap_err(), "Cannot divide string and string")
}
#[test]
fn test_arithmetic_div_str_char() {
let environment = run_string("var x = \"hello\" / 'a';");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide hello by 'a'")
assert_eq!(environment.unwrap_err(), "Cannot divide string and char")
}
#[test]
fn test_arithmetic_div_nb_str() {
let environment = run_string("var x = 3 / \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide 3 by hello")
assert_eq!(environment.unwrap_err(), "Cannot divide number and string")
}
#[test]
fn test_arithmetic_div_bool_str() {
let environment = run_string("var x = true / \"hello\";");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide true by hello")
assert_eq!(environment.unwrap_err(), "Cannot divide boolean and string")
}
#[test]
fn test_arithmetic_div_nb_bool() {
let environment = run_string("var x = 3 / true;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide 3 by true")
assert_eq!(environment.unwrap_err(), "Cannot divide number and boolean")
}
#[test]
fn test_arithmetic_div_bool_bool() {
let environment = run_string("var x = true / false;");
assert!(environment.is_err());
assert_eq!(environment.unwrap_err(), "Cannot divide true by false")
assert_eq!(
environment.unwrap_err(),
"Cannot divide boolean and boolean"
)
}
#[test]
@ -511,70 +541,49 @@ mod tests {
fn test_arithmetic_mod_char_nb() {
let environment = run_string("var x = 'a' % 3;");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with 'a' divisor of 3"
)
assert_eq!(environment.unwrap_err(), "Cannot modulo char and number")
}
#[test]
fn test_arithmetic_mod_char_char() {
let environment = run_string("var x = 'a' % 'b';");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with 'a' divisor of 'b'"
)
assert_eq!(environment.unwrap_err(), "Cannot modulo char and char")
}
#[test]
fn test_arithmetic_mod_str_str() {
let environment = run_string("var x = \"hello\" % \"world\";");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with hello divisor of world"
)
assert_eq!(environment.unwrap_err(), "Cannot modulo string and string")
}
#[test]
fn test_arithmetic_mod_str_char() {
let environment = run_string("var x = \"hello\" % 'a';");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with hello divisor of 'a'"
)
assert_eq!(environment.unwrap_err(), "Cannot modulo string and char")
}
#[test]
fn test_arithmetic_mod_nb_str() {
let environment = run_string("var x = 3 % \"hello\";");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with 3 divisor of hello"
)
assert_eq!(environment.unwrap_err(), "Cannot modulo number and string")
}
#[test]
fn test_arithmetic_mod_bool_str() {
let environment = run_string("var x = true % \"hello\";");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with true divisor of hello"
)
assert_eq!(environment.unwrap_err(), "Cannot modulo boolean and string")
}
#[test]
fn test_arithmetic_mod_nb_bool() {
let environment = run_string("var x = 3 % true;");
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with 3 divisor of true"
)
assert_eq!(environment.unwrap_err(), "Cannot modulo number and boolean")
}
#[test]
@ -583,7 +592,7 @@ mod tests {
assert!(environment.is_err());
assert_eq!(
environment.unwrap_err(),
"Cannot calculate the remainder with true divisor of false"
"Cannot modulo boolean and boolean"
)
}

View File

@ -7,7 +7,7 @@ use pest::Parser;
use super::{
error::{parsing_error, renamed_parsing_error_rules},
escape::{interpret_escaped_char, interpret_escaped_string, EscapeError},
syntax::{BinaryOp, Expr, Stat, UnaryOp, Value},
syntax::{BinaryOp, Expr, Stat, UnaryOp, Value, WriteMode},
};
#[derive(Parser)]
@ -101,6 +101,11 @@ fn build_expr(pair: Pair<Rule>) -> Result<Expr, String> {
}
Ok(Expr::Array(exprs))
}
Rule::read => {
let mut inner = primary.into_inner();
let name = build_expr(inner.nth(1).unwrap())?;
Ok(Expr::Read(Box::new(name)))
}
rule => Err(format!(
"atom expected number, boolean or variable, found {:?}",
rule
@ -260,6 +265,18 @@ fn build_print(pair: Pair<Rule>) -> Result<Stat, String> {
Ok(Stat::Print(expr))
}
fn build_write(pair: Pair<Rule>) -> Result<Stat, String> {
let mut inner = pair.into_inner();
let value = build_expr(inner.nth(1).unwrap())?;
let mode = match inner.next().unwrap().as_rule() {
Rule::to_keyword => WriteMode::Append,
Rule::new_keyword => WriteMode::Overwrite,
_ => unreachable!(),
};
let name = build_expr(inner.next().unwrap())?;
Ok(Stat::Write(value, mode, name))
}
fn build_stat(pair: Pair<Rule>, in_loop: bool) -> Result<Stat, String> {
match pair.as_rule() {
Rule::stat_assign => build_assign(pair),
@ -280,6 +297,7 @@ fn build_stat(pair: Pair<Rule>, in_loop: bool) -> Result<Stat, String> {
Rule::stat_define => build_define(pair),
Rule::stat_return => build_return(pair),
Rule::stat_print => build_print(pair),
Rule::stat_write => build_write(pair),
rule => Err(format!("Statement expected, found {:?}", rule)),
}
}

View File

@ -22,6 +22,9 @@ number = ${ (digit)+ }
boolean = ${ "true" | "false" }
array = { "[" ~ (expr ~ ("," ~ expr)*)? ~ "]" }
read_keyword = { "read" }
read = { read_keyword ~ expr }
op_assign = { "=" }
op_add = { "+" }
op_sub = { "-" }
@ -62,13 +65,18 @@ op_unary = _{
op_len
}
value = _{ call | boolean | number | variable | string | char | array | "(" ~ expr ~ ")" }
value = _{ read | call | boolean | number | variable | string | char | array | "(" ~ expr ~ ")" }
term = { op_unary* ~ value }
expr = { term ~ (op_binary ~ term)* }
end_of_stat = { ";" }
write_keyword = { "write" }
new_keyword = { "new" }
to_keyword = { "to" }
stat_write = { write_keyword ~ expr ~ (new_keyword | to_keyword) ~ expr ~ end_of_stat}
print_keyword = { "print" }
stat_print = { print_keyword ~ expr ~ end_of_stat }
@ -101,7 +109,7 @@ stat_elif = { (else_if_keyword ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" ~ else_key
stat_if = { (if_keyword ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" ~ else_keyword ~ "{" ~ stats ~ "}" ) |
(if_keyword ~ "(" ~ expr ~ ")" ~ "{" ~ stats ~ "}" ~ (stat_elif)?) }
stat = _{ ( stat_if | stat_while | stat_assign | stat_break | stat_continue | stat_define | stat_return | stat_print ) }
stat = _{ ( stat_if | stat_while | stat_assign | stat_break | stat_continue | stat_define | stat_return | stat_print | stat_write ) }
stats = { (stat)* }

View File

@ -104,7 +104,7 @@ impl Display for Value {
Value::Boolean(b) => write!(f, "{}", b),
Value::Function(args, _) => write!(f, "fn({})", args.join(", ")),
Value::String(s) => unsafe { write!(f, "{}", str::from_utf8_unchecked(s)) },
Value::Char(c) => write!(f, "'{}'", char::from(*c)),
Value::Char(c) => write!(f, "{}", char::from(*c)),
Value::Array(arr) => {
write!(f, "[")?;
let values = arr
@ -128,6 +128,7 @@ pub enum Expr {
UnaryOp(Box<Expr>, UnaryOp),
Call(String, Vec<Expr>),
Array(Vec<Expr>),
Read(Box<Expr>),
}
impl Display for Expr {
@ -161,6 +162,22 @@ impl Display for Expr {
write!(f, "{}", exprs.join(", "))?;
write!(f, "]")
}
Expr::Read(name) => write!(f, "read {}", name),
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum WriteMode {
Overwrite,
Append,
}
impl Display for WriteMode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
WriteMode::Overwrite => write!(f, "new"),
WriteMode::Append => write!(f, "to"),
}
}
}
@ -176,6 +193,7 @@ pub enum Stat {
VarDefine(String, Option<Expr>),
Return(Expr),
Print(Expr),
Write(Expr, WriteMode, Expr),
}
impl Display for Stat {
@ -227,6 +245,7 @@ impl Display for Stat {
},
Stat::Return(expr) => write!(f, "return {};", expr),
Stat::Print(expr) => write!(f, "print {};", expr),
Stat::Write(name, mode, expr) => write!(f, "write {} {} {}", name, mode, expr),
}
}
}

View File

@ -0,0 +1,8 @@
var x = "";
while (x != "\n") {
write "Enter a string: " new "stdout";
x = read "stdin";
if (x != "\n") {
write x to "stdout";
}
}