Skip to content

Commit 7b7ce4c

Browse files
committed
Adjust <open> to new metaStmt structure
1 parent 82dfa61 commit 7b7ce4c

2 files changed

Lines changed: 117 additions & 2 deletions

File tree

src/parser/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ pub const KEYWORDS: &[&str] = &[
2323
"False",
2424
"print",
2525
"input",
26+
"open",
2627
];

src/stdlib/standard_library.rs

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ use crate::environment::environment::Environment;
22
use crate::ir::ast::{Statement, Expression};
33
use std::collections::HashMap;
44
use std::sync::OnceLock;
5+
use std::fs::File;
6+
use std::io::Read;
7+
use std::io::Write;
58

69
pub type MetaBuiltinStmt = fn(&mut Environment<Expression>) -> Statement;
710

@@ -13,6 +16,7 @@ pub fn get_metabuiltins_table() -> &'static HashMap<String, MetaBuiltinStmt> {
1316
let mut table = HashMap::new();
1417
table.insert("input".to_string(), input_builtin as MetaBuiltinStmt);
1518
table.insert("print".to_string(), print_builtin as MetaBuiltinStmt);
19+
table.insert("open".to_string(), open_builtin as MetaBuiltinStmt);
1620
table
1721
})
1822
}
@@ -44,6 +48,88 @@ pub fn print_builtin(env: &mut Environment<Expression>) -> Statement {
4448
Statement::Return(Box::new(Expression::CVoid))
4549
}
4650

51+
pub fn open_builtin(env: &mut Environment<Expression>) -> Statement{
52+
let path = match env.lookup(&"path".to_string()) {
53+
Some((_, Expression::CString(p))) => p.clone(),
54+
_ => {
55+
return Statement::Return(Box::new(Expression::CString(
56+
"open: first argument must be a string with the file path".to_string())));
57+
}
58+
};
59+
60+
let mode = match env.lookup(&"mode".to_string()) {
61+
Some((_, Expression::CString(m))) => m.clone(),
62+
_ => "r".to_string(),
63+
};
64+
65+
match mode.as_str() {
66+
"r" => {
67+
let mut file = match File::open(&path) {
68+
Ok(f) => f,
69+
Err(e) => {
70+
return Statement::Return(Box::new(Expression::CString(
71+
format!("open: could not open '{}' for reading: {}", path, e))));
72+
}
73+
};
74+
let mut contents = String::new();
75+
if let Err(e) = file.read_to_string(&mut contents) {
76+
return Statement::Return(Box::new(Expression::CString(
77+
format!("open: error reading '{}': {}", path, e))));
78+
}
79+
80+
Statement::Return(Box::new(Expression::CString(contents)))
81+
}
82+
83+
"w" => {
84+
let content = match env.lookup(&"content".to_string()) {
85+
Some((_, Expression::CString(c))) => c.clone(),
86+
_ => {
87+
return Statement::Return(Box::new(Expression::CString(
88+
"open: when using mode 'w', a third argument with the content to write is required".to_string())));
89+
}
90+
};
91+
92+
match std::fs::write(&path, content) {
93+
Ok(_) => Statement::Return(Box::new(Expression::CVoid)),
94+
Err(e) => Statement::Return(Box::new(Expression::CString(
95+
format!("open: could not write to '{}': {}", path, e)))),
96+
}
97+
}
98+
99+
"a" => {
100+
let content = match env.lookup(&"content".to_string()) {
101+
Some((_, Expression::CString(c))) => c.clone(),
102+
_ => {
103+
return Statement::Return(Box::new(Expression::CString(
104+
"open: when using mode 'a', a third argument with the content to append is required".to_string())));
105+
}
106+
};
107+
108+
match std::fs::OpenOptions::new()
109+
.append(true)
110+
.create(true)
111+
.open(&path) {
112+
Ok(mut file) => {
113+
if let Err(e) = writeln!(file, "{}", content) {
114+
return Statement::Return(Box::new(Expression::CString(
115+
format!("open: could not append to '{}': {}", path, e))));
116+
}
117+
Statement::Return(Box::new(Expression::CVoid))
118+
},
119+
Err(e) => Statement::Return(Box::new(Expression::CString(
120+
format!("open: could not open '{}' for appending: {}", path, e)))),
121+
}
122+
123+
}
124+
125+
m => {
126+
Statement::Return(Box::new(Expression::CString(
127+
format!("open: unsupported mode '{}'.", m))))
128+
}
129+
}
130+
131+
}
132+
47133
#[cfg(test)]
48134
mod tests {
49135
use super::*;
@@ -80,7 +166,7 @@ mod tests {
80166
#[test]
81167
fn test_meta_stmt_table_has_correct_size() {
82168
let table = get_metabuiltins_table();
83-
assert_eq!(table.len(), 2, "The table must contain exactly 2 functions");
169+
assert_eq!(table.len(), 3, "The table must contain exactly 3 functions");
84170
}
85171

86172
#[test]
@@ -90,7 +176,8 @@ mod tests {
90176

91177
assert!(keys.contains(&&"input".to_string()), "The table must contain the key 'input'");
92178
assert!(keys.contains(&&"print".to_string()), "The table must contain the key 'print'");
93-
assert_eq!(keys.len(), 2, "The table must contain only 2 keys");
179+
assert!(keys.contains(&&"open".to_string()), "The table must contain the key 'open'");
180+
assert_eq!(keys.len(), 3, "The table must contain only 3 keys");
94181
}
95182

96183
#[test]
@@ -133,4 +220,31 @@ mod tests {
133220
panic!("The 'print' function must be in the table");
134221
}
135222
}
223+
224+
#[test]
225+
fn test_open_builtin_read_mode_success() {
226+
use std::fs;
227+
let test_file_path = "test_read_file.txt";
228+
let test_content = "Hello, World!\nThis is a test file.";
229+
fs::write(test_file_path, test_content).unwrap();
230+
231+
let mut env: Environment<Expression> = Environment::new();
232+
env.map_variable("path".to_string(), false, Expression::CString(test_file_path.to_string()));
233+
env.map_variable("mode".to_string(), false, Expression::CString("r".to_string()));
234+
235+
let result = open_builtin(&mut env);
236+
237+
match result {
238+
Statement::Return(expr) => {
239+
if let Expression::CString(content) = *expr {
240+
assert_eq!(content, test_content, "File content should match");
241+
} else {
242+
panic!("Expected CString with file content");
243+
}
244+
}
245+
_ => panic!("Expected Statement::Return"),
246+
}
247+
248+
fs::remove_file(test_file_path).unwrap();
249+
}
136250
}

0 commit comments

Comments
 (0)