feat(read_write): WIP read and write impl
parent
7567312055
commit
c84414b23c
|
@ -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"
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)* }
|
||||
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue