use std::collections::HashMap;
use std::sync::Arc as Rc;
use crate::nan_value::{Arena, NanValue};
use crate::value::{RuntimeError, Value, aver_display};
pub fn register(global: &mut HashMap<String, Value>) {
let mut members = HashMap::new();
for method in &["print", "error", "warn", "readLine"] {
members.insert(
method.to_string(),
Value::Builtin(format!("Console.{}", method)),
);
}
global.insert(
"Console".to_string(),
Value::Namespace {
name: "Console".to_string(),
members,
},
);
}
pub const DECLARED_EFFECTS: &[&str] = &[
"Console.print",
"Console.error",
"Console.warn",
"Console.readLine",
];
pub fn effects(name: &str) -> &'static [&'static str] {
match name {
"Console.print" => &["Console.print"],
"Console.error" => &["Console.error"],
"Console.warn" => &["Console.warn"],
"Console.readLine" => &["Console.readLine"],
_ => &[],
}
}
pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
match name {
"Console.print" => Some(one_msg(name, args, |s| {
println!("{}", s);
})),
"Console.error" => Some(one_msg(name, args, |s| {
eprintln!("{}", s);
})),
"Console.warn" => Some(one_msg(name, args, |s| {
eprintln!("[warn] {}", s);
})),
"Console.readLine" => Some(read_line(args)),
_ => None,
}
}
fn one_msg(name: &str, args: &[Value], emit: impl Fn(&str)) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::Error(format!(
"{}() takes 1 argument, got {}",
name,
args.len()
)));
}
if let Some(s) = aver_display(&args[0]) {
emit(&s);
}
Ok(Value::Unit)
}
fn read_line(args: &[Value]) -> Result<Value, RuntimeError> {
if !args.is_empty() {
return Err(RuntimeError::Error(format!(
"Console.readLine() takes 0 arguments, got {}",
args.len()
)));
}
match aver_rt::read_line() {
Ok(line) => Ok(Value::Ok(Box::new(Value::Str(line)))),
Err(e) => Ok(Value::Err(Box::new(Value::Str(e)))),
}
}
pub fn register_nv(global: &mut HashMap<String, NanValue>, arena: &mut Arena) {
let methods = &["print", "error", "warn", "readLine"];
let mut members: Vec<(Rc<str>, NanValue)> = Vec::with_capacity(methods.len());
for method in methods {
let idx = arena.push_builtin(&format!("Console.{}", method));
members.push((Rc::from(*method), NanValue::new_builtin(idx)));
}
let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
name: Rc::from("Console"),
members,
});
global.insert("Console".to_string(), NanValue::new_namespace(ns_idx));
}
pub fn call_nv(
name: &str,
args: &[NanValue],
arena: &mut Arena,
) -> Option<Result<NanValue, RuntimeError>> {
match name {
"Console.print" => Some(one_msg_nv(name, args, arena, |s| {
println!("{}", s);
})),
"Console.error" => Some(one_msg_nv(name, args, arena, |s| {
eprintln!("{}", s);
})),
"Console.warn" => Some(one_msg_nv(name, args, arena, |s| {
eprintln!("[warn] {}", s);
})),
"Console.readLine" => Some(read_line_nv(args, arena)),
_ => None,
}
}
fn one_msg_nv(
name: &str,
args: &[NanValue],
arena: &mut Arena,
emit: impl Fn(&str),
) -> Result<NanValue, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::Error(format!(
"{}() takes 1 argument, got {}",
name,
args.len()
)));
}
if let Some(s) = args[0].display(arena) {
emit(&s);
}
Ok(NanValue::UNIT)
}
fn read_line_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
if !args.is_empty() {
return Err(RuntimeError::Error(format!(
"Console.readLine() takes 0 arguments, got {}",
args.len()
)));
}
match aver_rt::read_line() {
Ok(line) => {
let inner = NanValue::new_string_value(&line, arena);
Ok(NanValue::new_ok_value(inner, arena))
}
Err(e) => {
let inner = NanValue::new_string_value(&e, arena);
Ok(NanValue::new_err_value(inner, arena))
}
}
}