feat: add File and Process effects for real I/O
Adds two essential effects that enable Lux to interact with the system:
File effect:
- read(path) - Read file contents as string
- write(path, content) - Write string to file
- append(path, content) - Append to file
- exists(path) - Check if file/directory exists
- delete(path) - Delete a file
- readDir(path) - List directory contents
- isDir(path) - Check if path is directory
- mkdir(path) - Create directory (including parents)
Process effect:
- exec(cmd) - Run shell command, return stdout
- execStatus(cmd) - Run command, return exit code
- env(name) - Get environment variable (returns Option)
- args() - Get command line arguments
- exit(code) - Exit program with code
- cwd() - Get current working directory
- setCwd(path) - Change working directory
Also fixes formatter bug with empty handler blocks in `run ... with {}`.
These effects make Lux capable of writing real CLI tools and scripts.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -167,19 +167,51 @@ impl Formatter {
|
||||
|
||||
self.write(" =");
|
||||
|
||||
// Body
|
||||
let body_str = self.format_expr(&func.body);
|
||||
if self.is_block_expr(&func.body) || body_str.contains('\n') {
|
||||
// Body - handle blocks specially to keep `= {` on same line
|
||||
if let Expr::Block { statements, result, .. } = &func.body {
|
||||
self.write(" {");
|
||||
self.newline();
|
||||
self.indent_level += 1;
|
||||
for stmt in statements {
|
||||
let indent = self.indent();
|
||||
match stmt {
|
||||
Statement::Let { name, typ, value, .. } => {
|
||||
let type_str = typ.as_ref()
|
||||
.map(|t| format!(": {}", self.format_type_expr(t)))
|
||||
.unwrap_or_default();
|
||||
self.write(&indent);
|
||||
self.writeln(&format!(
|
||||
"let {}{} = {}",
|
||||
name.name,
|
||||
type_str,
|
||||
self.format_expr(value)
|
||||
));
|
||||
}
|
||||
Statement::Expr(e) => {
|
||||
self.write(&indent);
|
||||
self.writeln(&self.format_expr(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
self.write(&self.indent());
|
||||
self.write(&body_str);
|
||||
self.writeln(&self.format_expr(result));
|
||||
self.indent_level -= 1;
|
||||
self.write(&self.indent());
|
||||
self.writeln("}");
|
||||
} else {
|
||||
self.write(" ");
|
||||
self.write(&body_str);
|
||||
let body_str = self.format_expr(&func.body);
|
||||
if body_str.contains('\n') {
|
||||
self.newline();
|
||||
self.indent_level += 1;
|
||||
self.write(&self.indent());
|
||||
self.write(&body_str);
|
||||
self.indent_level -= 1;
|
||||
self.newline();
|
||||
} else {
|
||||
self.write(" ");
|
||||
self.writeln(&body_str);
|
||||
}
|
||||
}
|
||||
self.newline();
|
||||
}
|
||||
|
||||
fn format_let(&mut self, let_decl: &LetDecl) {
|
||||
@@ -674,12 +706,16 @@ impl Formatter {
|
||||
)
|
||||
}
|
||||
Expr::Run { expr, handlers, .. } => {
|
||||
let mut s = format!("run {} with {{\n", self.format_expr(expr));
|
||||
for (effect, handler) in handlers {
|
||||
s.push_str(&format!(" {} = {},\n", effect.name, self.format_expr(handler)));
|
||||
if handlers.is_empty() {
|
||||
format!("run {} with {{}}", self.format_expr(expr))
|
||||
} else {
|
||||
let mut s = format!("run {} with {{\n", self.format_expr(expr));
|
||||
for (effect, handler) in handlers {
|
||||
s.push_str(&format!(" {} = {},\n", effect.name, self.format_expr(handler)));
|
||||
}
|
||||
s.push('}');
|
||||
s
|
||||
}
|
||||
s.push('}');
|
||||
s
|
||||
}
|
||||
Expr::Resume { value, .. } => {
|
||||
format!("resume({})", self.format_expr(value))
|
||||
|
||||
Reference in New Issue
Block a user