Skip to content
91 changes: 91 additions & 0 deletions examples/stdlib_demo.woke
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Example: WokeLang Standard Library Demo
// Demonstrates usage of std.math, std.time, std.json, std.io, and std.net

thanks to {
"WokeLang Standard Library" -> "For comprehensive utilities";
}

to main() {
hello "Standard Library Demo";

print("=== Math Functions ===");
// Math doesn't require capabilities
print("abs(-42) = " + toString(std.math.abs(-42)));
print("sqrt(16) = " + toString(std.math.sqrt(16)));
print("pow(2, 8) = " + toString(std.math.pow(2, 8)));
print("floor(3.7) = " + toString(std.math.floor(3.7)));
print("ceil(3.2) = " + toString(std.math.ceil(3.2)));
print("min(10, 5) = " + toString(std.math.min(10, 5)));
print("max(10, 5) = " + toString(std.math.max(10, 5)));
print("PI = " + toString(std.math.pi()));
print("E = " + toString(std.math.e()));

print("");
print("=== Time Functions ===");
remember now_ms = std.time.now();
print("Current time (ms): " + toString(now_ms));

remember formatted = std.time.format(now_ms, "%Y-%m-%d %H:%M:%S");
print("Formatted: " + formatted);

// Elapsed timer example
std.time.elapsed("start", "demo_timer");
// Do some work...
std.time.sleep(100);
remember elapsed_ms = std.time.elapsed("stop", "demo_timer");
print("Elapsed: " + toString(elapsed_ms) + "ms");

print("");
print("=== JSON Functions ===");
// Parse JSON
remember json_str = "{\"name\": \"WokeLang\", \"version\": 1, \"features\": [\"consent\", \"safety\"]}";
remember parsed = std.json.parse(json_str);
print("Parsed JSON: " + toString(parsed));

// Get nested value
remember name = std.json.get(parsed, "name");
print("Name: " + toString(name));

// Stringify
remember back_to_string = std.json.stringify(parsed);
print("Stringified: " + back_to_string);

print("");
print("=== File I/O (requires consent) ===");
// These operations require file:read and file:write capabilities
// In a real program, the user would be prompted for consent

only if okay {
superpower file:write;

remember path = "/tmp/wokelang_demo.txt";
std.io.writeFile(path, "Hello from WokeLang!");
print("Wrote to: " + path);

remember content = std.io.readFile(path);
print("Read back: " + content);

remember exists = std.io.exists(path);
print("File exists: " + toString(exists));

std.io.delete(path);
print("File deleted");
}

print("");
print("=== Network (requires consent) ===");
// Network operations require network:* capability
// Note: HTTPS requires TLS library

only if okay {
superpower network:*;

// HTTP GET (would work with a real HTTP server)
// remember response = std.net.httpGet("http://example.com");
// print("Response: " + response);

print("Network functions available but require actual server");
}

goodbye "Demo complete!";
}
287 changes: 287 additions & 0 deletions src/stdlib/io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
//! WokeLang Standard Library - I/O Module
//!
//! File I/O operations that require explicit consent through capabilities.

use crate::interpreter::Value;
use crate::security::{Capability, CapabilityRegistry};
use super::{check_arity, check_arity_range, expect_string, StdlibError};
use std::fs;
use std::io::{self, BufRead, Write};
use std::path::PathBuf;

/// Helper to require file read capability
fn require_read(path: &str, caps: &mut CapabilityRegistry) -> Result<(), StdlibError> {
let cap = Capability::FileRead(Some(PathBuf::from(path)));
if caps.request("stdlib", &cap).is_err() {
Err(StdlibError::PermissionDenied(format!(
"File read access denied: {}",
path
)))
} else {
Ok(())
}
}

/// Helper to require file write capability
fn require_write(path: &str, caps: &mut CapabilityRegistry) -> Result<(), StdlibError> {
let cap = Capability::FileWrite(Some(PathBuf::from(path)));
if caps.request("stdlib", &cap).is_err() {
Err(StdlibError::PermissionDenied(format!(
"File write access denied: {}",
path
)))
} else {
Ok(())
}
}

/// Read entire file contents as a string
pub fn read_file(args: &[Value], caps: &mut CapabilityRegistry) -> Result<Value, StdlibError> {
check_arity(args, 1)?;
let path = expect_string(&args[0], "path")?;

require_read(&path, caps)?;

match fs::read_to_string(&path) {
Ok(contents) => Ok(Value::String(contents)),
Err(e) => Err(StdlibError::IoError(e.to_string())),
}
}

/// Write string contents to a file
pub fn write_file(args: &[Value], caps: &mut CapabilityRegistry) -> Result<Value, StdlibError> {
check_arity(args, 2)?;
let path = expect_string(&args[0], "path")?;
let contents = expect_string(&args[1], "contents")?;

require_write(&path, caps)?;

match fs::write(&path, &contents) {
Ok(()) => Ok(Value::Bool(true)),
Err(e) => Err(StdlibError::IoError(e.to_string())),
}
}

/// Append string contents to a file
pub fn append_file(args: &[Value], caps: &mut CapabilityRegistry) -> Result<Value, StdlibError> {
check_arity(args, 2)?;
let path = expect_string(&args[0], "path")?;
let contents = expect_string(&args[1], "contents")?;

require_write(&path, caps)?;

use std::fs::OpenOptions;
match OpenOptions::new().create(true).append(true).open(&path) {
Ok(mut file) => match file.write_all(contents.as_bytes()) {
Ok(()) => Ok(Value::Bool(true)),
Err(e) => Err(StdlibError::IoError(e.to_string())),
},
Err(e) => Err(StdlibError::IoError(e.to_string())),
}
}

/// Check if a file or directory exists
pub fn exists(args: &[Value], caps: &mut CapabilityRegistry) -> Result<Value, StdlibError> {
check_arity(args, 1)?;
let path = expect_string(&args[0], "path")?;

// exists only needs read capability to check
require_read(&path, caps)?;

Ok(Value::Bool(std::path::Path::new(&path).exists()))
}

/// Delete a file
pub fn delete(args: &[Value], caps: &mut CapabilityRegistry) -> Result<Value, StdlibError> {
check_arity(args, 1)?;
let path = expect_string(&args[0], "path")?;

require_write(&path, caps)?;

match fs::remove_file(&path) {
Ok(()) => Ok(Value::Bool(true)),
Err(e) => Err(StdlibError::IoError(e.to_string())),
}
}

/// List directory contents
pub fn list_dir(args: &[Value], caps: &mut CapabilityRegistry) -> Result<Value, StdlibError> {
check_arity(args, 1)?;
let path = expect_string(&args[0], "path")?;

require_read(&path, caps)?;

match fs::read_dir(&path) {
Ok(entries) => {
let files: Vec<Value> = entries
.filter_map(|e| e.ok())
.map(|e| Value::String(e.file_name().to_string_lossy().to_string()))
.collect();
Ok(Value::Array(files))
}
Err(e) => Err(StdlibError::IoError(e.to_string())),
}
}

/// Create a directory (and parents if needed)
pub fn create_dir(args: &[Value], caps: &mut CapabilityRegistry) -> Result<Value, StdlibError> {
check_arity(args, 1)?;
let path = expect_string(&args[0], "path")?;

require_write(&path, caps)?;

match fs::create_dir_all(&path) {
Ok(()) => Ok(Value::Bool(true)),
Err(e) => Err(StdlibError::IoError(e.to_string())),
}
}

/// Read a line from stdin (interactive)
pub fn read_line(args: &[Value], caps: &mut CapabilityRegistry) -> Result<Value, StdlibError> {
check_arity_range(args, 0, 1)?;

// Print optional prompt
if let Some(prompt) = args.first() {
let prompt_str = expect_string(prompt, "prompt")?;
print!("{}", prompt_str);
io::stdout().flush().ok();
}

let stdin = io::stdin();
let mut line = String::new();
match stdin.lock().read_line(&mut line) {
Ok(_) => Ok(Value::String(line.trim_end_matches('\n').to_string())),
Err(e) => Err(StdlibError::IoError(e.to_string())),
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::env;

fn test_caps() -> CapabilityRegistry {
CapabilityRegistry::permissive()
}

fn temp_file(name: &str) -> String {
env::temp_dir()
.join(format!("wokelang_test_{}", name))
.to_string_lossy()
.to_string()
}

#[test]
fn test_write_and_read() {
let mut caps = test_caps();
let path = temp_file("io_test.txt");

// Write
let write_result = write_file(
&[Value::String(path.clone()), Value::String("Hello, WokeLang!".to_string())],
&mut caps,
);
assert!(write_result.is_ok());

// Read
let read_result = read_file(&[Value::String(path.clone())], &mut caps);
assert_eq!(
read_result.unwrap(),
Value::String("Hello, WokeLang!".to_string())
);

// Cleanup
let _ = fs::remove_file(&path);
}

#[test]
fn test_exists() {
let mut caps = test_caps();
let path = temp_file("exists_test.txt");

// Should not exist yet
let result = exists(&[Value::String(path.clone())], &mut caps);
assert_eq!(result.unwrap(), Value::Bool(false));

// Create file
fs::write(&path, "test").unwrap();

// Should exist now
let result = exists(&[Value::String(path.clone())], &mut caps);
assert_eq!(result.unwrap(), Value::Bool(true));

// Cleanup
let _ = fs::remove_file(&path);
}

#[test]
fn test_append() {
let mut caps = test_caps();
let path = temp_file("append_test.txt");

// Write initial content
write_file(
&[Value::String(path.clone()), Value::String("Hello".to_string())],
&mut caps,
)
.unwrap();

// Append
append_file(
&[Value::String(path.clone()), Value::String(", World!".to_string())],
&mut caps,
)
.unwrap();

// Read and verify
let result = read_file(&[Value::String(path.clone())], &mut caps);
assert_eq!(
result.unwrap(),
Value::String("Hello, World!".to_string())
);

// Cleanup
let _ = fs::remove_file(&path);
}

#[test]
fn test_delete() {
let mut caps = test_caps();
let path = temp_file("delete_test.txt");

// Create file
fs::write(&path, "test").unwrap();
assert!(std::path::Path::new(&path).exists());

// Delete
let result = delete(&[Value::String(path.clone())], &mut caps);
assert!(result.is_ok());
assert!(!std::path::Path::new(&path).exists());
}

#[test]
fn test_create_dir_and_list() {
let mut caps = test_caps();
let dir_path = temp_file("test_dir");

// Create directory
create_dir(&[Value::String(dir_path.clone())], &mut caps).unwrap();
assert!(std::path::Path::new(&dir_path).is_dir());

// Create a file in the directory
let file_path = format!("{}/test.txt", dir_path);
fs::write(&file_path, "test").unwrap();

// List directory
let result = list_dir(&[Value::String(dir_path.clone())], &mut caps);
match result.unwrap() {
Value::Array(files) => {
assert!(files.contains(&Value::String("test.txt".to_string())));
}
_ => panic!("Expected array"),
}

// Cleanup
let _ = fs::remove_dir_all(&dir_path);
}
}
Loading
Loading