use parking_lot::RwLock;
use crate::env::Env;
use crate::error::LispError;
use crate::eval::EvalContext;
use crate::value::{bool_val_at, keyword_at, list_val, num_at, str_val, sym, sym_at, BlinkValue, SourcePos, SourceRange};
use crate::value::{LispNode, Value};
use std::collections::HashMap;
use std::sync::Arc;
pub struct ReaderContext {
pub reader_macros: HashMap<String, BlinkValue>,
}
impl ReaderContext {
pub fn new() -> Self {
ReaderContext {
reader_macros: HashMap::new(),
}
}
}
fn expand_macro_body(form: BlinkValue, env: Arc<RwLock<Env>>) -> Result<BlinkValue, LispError> {
match &form.read().value {
Value::Symbol(s) => {
if let Some(val) = env.read().get(s) {
Ok(val)
} else {
Ok(form.clone()) }
}
Value::List(forms) => {
let mut expanded = Vec::new();
for f in forms {
expanded.push(expand_macro_body(f.clone(), env.clone())?);
}
Ok(list_val(expanded))
}
_ => Ok(form.clone()),
}
}
fn apply_reader_macro(macro_fn: BlinkValue, form: BlinkValue) -> Result<BlinkValue, LispError> {
match ¯o_fn.read().value {
Value::FuncUserDefined { params, body, env } => {
if params.len() != 1 {
return Err(LispError::EvalError {
message: "Reader macro must take exactly 1 argument".into(),
pos: None,
});
}
let local_env = Arc::new(RwLock::new(Env::with_parent(env.clone())));
local_env.write().set(¶ms[0], form);
let macro_body = body.get(0).ok_or_else(|| LispError::EvalError {
message: "Reader macro has no body".into(),
pos: None,
})?;
expand_macro_body(macro_body.clone(), local_env)
}
Value::NativeFunc(f) => f(vec![form]).map_err(|e| LispError::EvalError {
message: e,
pos: None,
}),
_ => Err(LispError::EvalError {
message: "Reader macro must be a function".into(),
pos: None,
}),
}
}
pub fn tokenize(code: &str) -> Result<Vec<(String, SourcePos)>, LispError> {
tokenize_at(code, None)
}
pub fn tokenize_at(
code: &str,
pos: Option<SourcePos>,
) -> Result<Vec<(String, SourcePos)>, LispError> {
let mut tokens = Vec::new();
let mut current = String::new();
let mut in_str = false;
let mut line = 1;
let mut col = 0;
if let Some(pos) = pos {
line = pos.line;
col = pos.col;
}
for c in code.chars() {
col += 1;
if c == '\n' {
line += 1;
col = 0;
}
match c {
'(' | ')' | '[' | ']' | '{' | '}' => {
if !current.is_empty() {
tokens.push((current.clone(), SourcePos { line, col }));
current.clear();
}
tokens.push((c.to_string(), SourcePos { line, col }));
}
'"' => {
current.push(c);
if in_str {
tokens.push((current.clone(), SourcePos { line, col }));
current.clear();
}
in_str = !in_str;
}
c if c.is_whitespace() && !in_str => {
if !current.is_empty() {
tokens.push((current.clone(), SourcePos { line, col }));
current.clear();
}
}
_ => current.push(c),
}
}
if in_str {
return Err(LispError::TokenizerError {
message: "Unterminated string literal".into(),
pos: SourcePos { line, col },
});
}
if !current.is_empty() {
tokens.push((current, SourcePos { line, col }));
}
Ok(tokens)
}
pub fn atom(token: &str, pos: Option<SourcePos>) -> BlinkValue {
if token.starts_with('"') && token.ends_with('"') {
str_val(&token[1..token.len() - 1])
} else if let Ok(n) = token.parse::<f64>() {
num_at(n, pos)
} else if token == "true" {
bool_val_at(true, pos)
} else if token == "false" {
bool_val_at(false, pos)
} else if token.starts_with(':') {
keyword_at(&token[1..], pos)
} else {
sym_at(token, pos)
}
}
pub fn parse(
tokens: &mut Vec<(String, SourcePos)>,
rcx: &mut ReaderContext,
) -> Result<BlinkValue, LispError> {
if tokens.is_empty() {
return Err(LispError::UnexpectedToken {
pos: SourcePos { line: 0, col: 0 },
token: "EOF".into(),
});
}
let (token, start) = tokens.remove(0);
match token.as_str() {
"(" => {
let mut list = Vec::new();
let mut end = start.clone();
while let Some((tok, next_pos)) = tokens.first() {
if tok == ")" {
end = next_pos.clone(); tokens.remove(0); break;
}
let item = parse(tokens, rcx)?;
if let Some(item_end) = item.read().pos.as_ref().map(|r| r.end.clone()) {
end = item_end;
}
list.push(item);
}
let range = SourceRange { start, end };
Ok(BlinkValue(Arc::new(RwLock::new(LispNode {
value: Value::List(list),
pos: Some(range),
}))))
}
"[" => {
let mut elements = Vec::new();
let mut end = start.clone();
while let Some((t, next_pos)) = tokens.first() {
if t == "]" {
end = next_pos.clone(); tokens.remove(0);
return Ok(list_val(
std::iter::once(sym("vector"))
.chain(elements.into_iter())
.collect(),
));
}
let item = parse(tokens, rcx)?;
if let Some(item_end) = item.read().pos.as_ref().map(|r| r.end.clone()) {
end = item_end;
}
elements.push(item);
}
let pos = SourceRange { start: start.clone(), end };
Err(LispError::ParseError {
message: "Unclosed vector literal".into(),
pos,
})
}
"{" => {
let mut entries = Vec::new();
let mut end = start.clone();
while let Some((t, next_pos)) = tokens.first() {
if t == "}" {
end = next_pos.clone(); tokens.remove(0); return Ok(list_val(
std::iter::once(sym("hash-map"))
.chain(entries.into_iter())
.collect(),
));
}
entries.push(parse(tokens, rcx)?); entries.push(parse(tokens, rcx)?); }
let pos = SourceRange { start: start.clone(), end };
Err(LispError::ParseError {
message: "Unclosed map literal".into(),
pos,
})
}
")" | "]" | "}" => Err(LispError::UnexpectedToken { token, pos: start }),
_ => {
let mut matched_macro: Option<(String, BlinkValue)> = None;
for (prefix, macro_fn) in rcx.reader_macros.iter() {
if token.starts_with(prefix) {
if let Some((best_prefix, _)) = &matched_macro {
if prefix.len() > best_prefix.len() {
matched_macro = Some((prefix.clone(), macro_fn.clone()));
}
} else {
matched_macro = Some((prefix.clone(), macro_fn.clone()));
}
}
}
if let Some((prefix, macro_fn)) = matched_macro {
let rest = &token[prefix.len()..];
let target_form = if rest.is_empty() {
if tokens.is_empty() {
return Err(LispError::ParseError {
message: "Unexpected EOF after reader macro".into(),
pos: SourceRange {
start: start.clone(),
end: start.clone(),
},
});
}
parse(tokens, rcx)?
} else {
let mut rest_tokens = tokenize(rest)?;
parse(&mut rest_tokens, rcx)?
};
let node = apply_reader_macro(macro_fn, target_form.clone())?;
let target_range = target_form.read().pos.clone();
let end = target_range.map(|r| r.end).unwrap_or_else(|| start.clone());
node.write().pos = Some(SourceRange {
start: start.clone(),
end,
});
return Ok(node);
}
Ok(atom(&token, Some(start)))
}
}
}
pub fn parse_all(code: &str) -> Result<Vec<BlinkValue>, LispError> {
let mut tokens = tokenize(code)?;
let mut forms = Vec::new();
let mut reader_ctx = ReaderContext::new();
while !tokens.is_empty() {
let form = parse(&mut tokens, &mut reader_ctx)?;
forms.push(form);
}
Ok(forms)
}
pub fn preload_builtin_reader_macros(ctx: &mut EvalContext) {
fn build_simple_macro(name: &str) -> BlinkValue {
BlinkValue(Arc::new(RwLock::new(LispNode {
value: Value::FuncUserDefined {
params: vec!["x".to_string()],
body: vec![list_val(vec![
sym(name), sym("x"), ])],
env: Arc::new(RwLock::new(Env::new())), },
pos: None,
})))
}
let mut rm = ctx.reader_macros.write();
rm.reader_macros
.insert("\'".into(), build_simple_macro("quo"));
rm.reader_macros
.insert("`".into(), build_simple_macro("quasiquote"));
rm.reader_macros
.insert("~".into(), build_simple_macro("unquote"));
}