add ref-counted string type

This commit is contained in:
Scott Richmond 2024-12-04 15:03:09 -05:00
parent 2a83dbb96c
commit c5c1717e57
5 changed files with 77 additions and 9 deletions

View File

@ -1,6 +1,7 @@
// Solution to the #[derive(Clone)] problem: https://stackoverflow.com/questions/27883509/can-you-clone-a-closure // Solution to the #[derive(Clone)] problem: https://stackoverflow.com/questions/27883509/can-you-clone-a-closure
use crate::value::*; use crate::value::*;
use crate::vm::*; use crate::vm::*;
use std::rc::Rc;
// use imbl::*; // use imbl::*;
// use std::fmt; // use std::fmt;
@ -68,6 +69,24 @@ pub fn store<'src>(b: &Value<'src>, val: &Value<'src>) -> LResult<'src> {
} }
} }
// TODO: do better than returning just the docstr
// name, patterns, AND docstring
pub fn doc<'src>(f: &Value<'src>) -> LResult<'src> {
match f {
Value::Fn(f) => {
let Fn { name, doc, .. } = **f;
if let Some(docstr) = doc {
Ok(Value::AllocatedString(Rc::new(format!("{name}: {docstr}"))))
} else {
Ok(Value::InternedString(
doc.unwrap_or("no documentation found"),
))
}
}
_ => Ok(Value::InternedString("no documentation found")),
}
}
pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> { pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> {
let mut base = vec![]; let mut base = vec![];
@ -83,6 +102,8 @@ pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> {
base.push(("store", Value::Base(Base::Binary("store", store)))); base.push(("store", Value::Base(Base::Binary("store", store))));
base.push(("doc", Value::Base(Base::Unary("doc", doc))));
base base
} }

View File

@ -63,7 +63,7 @@ fn foo {
() -> :foo () -> :foo
(_) -> :bar (_) -> :bar
} }
foo () doc (foo)
"; ";
let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
if !lex_errs.is_empty() { if !lex_errs.is_empty() {

View File

@ -33,6 +33,22 @@ impl<'src> fmt::Display for MatchClause<'src> {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StringPart<'src> {
Data(&'src str),
Word(&'src str),
}
impl<'src> fmt::Display for StringPart<'src> {
fn fmt(self: &StringPart<'src>, f: &mut fmt::Formatter) -> fmt::Result {
let rep = match self {
StringPart::Data(s) => format!("{{{s}}}"),
StringPart::Word(s) => s.to_string(),
};
write!(f, "{}", rep)
}
}
#[derive(Clone, Debug, PartialEq, Dissectible)] #[derive(Clone, Debug, PartialEq, Dissectible)]
pub enum Ast<'src> { pub enum Ast<'src> {
Error, Error,
@ -43,6 +59,7 @@ pub enum Ast<'src> {
Keyword(&'src str), Keyword(&'src str),
Word(&'src str), Word(&'src str),
String(&'src str), String(&'src str),
Interpolated(Vec<Spanned<StringPart<'src>>>),
Block(Vec<Spanned<Self>>), Block(Vec<Spanned<Self>>),
If(Box<Spanned<Self>>, Box<Spanned<Self>>, Box<Spanned<Self>>), If(Box<Spanned<Self>>, Box<Spanned<Self>>, Box<Spanned<Self>>),
Tuple(Vec<Spanned<Self>>), Tuple(Vec<Spanned<Self>>),
@ -71,6 +88,16 @@ impl fmt::Display for Ast<'_> {
Ast::Error => write!(f, "Error"), Ast::Error => write!(f, "Error"),
Ast::Nil => write!(f, "nil"), Ast::Nil => write!(f, "nil"),
Ast::String(s) => write!(f, "String: \"{}\"", s), Ast::String(s) => write!(f, "String: \"{}\"", s),
Ast::Interpolated(strs) => {
write!(
f,
"String: \"{}\"",
strs.iter()
.map(|(s, _)| s.to_string())
.collect::<Vec<_>>()
.join("")
)
}
Ast::Boolean(b) => write!(f, "Boolean: {}", b), Ast::Boolean(b) => write!(f, "Boolean: {}", b),
Ast::Number(n) => write!(f, "Number: {}", n), Ast::Number(n) => write!(f, "Number: {}", n),
Ast::Keyword(k) => write!(f, "Keyword: :{}", k), Ast::Keyword(k) => write!(f, "Keyword: :{}", k),
@ -277,6 +304,17 @@ impl fmt::Display for Pattern<'_> {
} }
} }
// TODO: write this
// 1. we need an enum for a return type
// either a string part or a word part
// 2. our string types, both patterns and values, now contains a vec of nodes
// 3. this should loop through the string and allow for escaping braces
// consider using Rust-style escapes: {{}}, rather than \{\}
// {{{foo}}}
pub fn parse_string<'src>(s: &'src str) -> Vec<StringPart<'src>> {
vec![]
}
pub fn parser<'src, I>( pub fn parser<'src, I>(
) -> impl Parser<'src, I, Spanned<Ast<'src>>, extra::Err<Rich<'src, Token<'src>, Span>>> + Clone ) -> impl Parser<'src, I, Spanned<Ast<'src>>, extra::Err<Rich<'src, Token<'src>, Span>>> + Clone
where where

View File

@ -10,6 +10,7 @@ use struct_scalpel::Dissectible;
pub struct Fn<'src> { pub struct Fn<'src> {
pub name: &'src str, pub name: &'src str,
pub body: &'src Vec<MatchClause<'src>>, pub body: &'src Vec<MatchClause<'src>>,
pub doc: &'src Option<&'src str>,
} }
#[derive(Debug, Dissectible)] #[derive(Debug, Dissectible)]
@ -19,7 +20,10 @@ pub enum Value<'src> {
Boolean(bool), Boolean(bool),
Number(f64), Number(f64),
Keyword(&'src str), Keyword(&'src str),
String(&'src str), // TODO: add a "runtime-generated" string type that wraps a Rust String
// this is necessary for nice documentation and string interpolation
InternedString(&'src str),
AllocatedString(Rc<String>),
// on the heap for now // on the heap for now
Tuple(Rc<Vec<Self>>), Tuple(Rc<Vec<Self>>),
Args(Rc<Vec<Self>>), Args(Rc<Vec<Self>>),
@ -49,7 +53,8 @@ impl<'src> Clone for Value<'src> {
match self { match self {
Value::Nil => Value::Nil, Value::Nil => Value::Nil,
Value::Boolean(b) => Value::Boolean(*b), Value::Boolean(b) => Value::Boolean(*b),
Value::String(s) => Value::String(s), Value::InternedString(s) => Value::InternedString(s),
Value::AllocatedString(s) => Value::AllocatedString(s.clone()),
Value::Keyword(s) => Value::Keyword(s), Value::Keyword(s) => Value::Keyword(s),
Value::Number(n) => Value::Number(*n), Value::Number(n) => Value::Number(*n),
Value::Tuple(t) => Value::Tuple(t.clone()), Value::Tuple(t) => Value::Tuple(t.clone()),
@ -72,7 +77,8 @@ impl fmt::Display for Value<'_> {
Value::Boolean(b) => write!(f, "{}", b), Value::Boolean(b) => write!(f, "{}", b),
Value::Number(n) => write!(f, "{}", n), Value::Number(n) => write!(f, "{}", n),
Value::Keyword(k) => write!(f, ":{}", k), Value::Keyword(k) => write!(f, ":{}", k),
Value::String(s) => write!(f, "\"{}\"", s), Value::InternedString(s) => write!(f, "\"{}\"", s),
Value::AllocatedString(s) => write!(f, "\"{}\"", s),
Value::Fn(fun) => write!(f, "fn {}", fun.name), Value::Fn(fun) => write!(f, "fn {}", fun.name),
Value::Tuple(t) | Value::Args(t) => write!( Value::Tuple(t) | Value::Args(t) => write!(
f, f,
@ -132,7 +138,8 @@ impl Value<'_> {
Value::Boolean(_) => Value::Keyword("boolean"), Value::Boolean(_) => Value::Keyword("boolean"),
Value::Keyword(_) => Value::Keyword("keyword"), Value::Keyword(_) => Value::Keyword("keyword"),
Value::Tuple(_) => Value::Keyword("tuple"), Value::Tuple(_) => Value::Keyword("tuple"),
Value::String(_) => Value::Keyword("string"), Value::InternedString(_) => Value::Keyword("string"),
Value::AllocatedString(_) => Value::Keyword("string"),
Value::List(_) => Value::Keyword("list"), Value::List(_) => Value::Keyword("list"),
Value::Dict(_) => Value::Keyword("dict"), Value::Dict(_) => Value::Keyword("dict"),
Value::Fn(_) => Value::Keyword("fn"), Value::Fn(_) => Value::Keyword("fn"),
@ -152,7 +159,7 @@ impl<'src> PartialEq for Value<'src> {
(Value::Nil, Value::Nil) => true, (Value::Nil, Value::Nil) => true,
(Value::Boolean(x), Value::Boolean(y)) => x == y, (Value::Boolean(x), Value::Boolean(y)) => x == y,
(Value::Number(x), Value::Number(y)) => x == y, (Value::Number(x), Value::Number(y)) => x == y,
(Value::String(x), Value::String(y)) => x == y, (Value::InternedString(x), Value::InternedString(y)) => x == y,
(Value::Keyword(x), Value::Keyword(y)) => x == y, (Value::Keyword(x), Value::Keyword(y)) => x == y,
(Value::Tuple(x), Value::Tuple(y)) => x == y, (Value::Tuple(x), Value::Tuple(y)) => x == y,
(Value::List(x), Value::List(y)) => x == y, (Value::List(x), Value::List(y)) => x == y,

View File

@ -62,7 +62,7 @@ pub fn match_pattern<'src, 'a>(
(Pattern::Number(x), Value::Number(y)) => match_eq(x, y, ctx), (Pattern::Number(x), Value::Number(y)) => match_eq(x, y, ctx),
(Pattern::Boolean(x), Value::Boolean(y)) => match_eq(x, y, ctx), (Pattern::Boolean(x), Value::Boolean(y)) => match_eq(x, y, ctx),
(Pattern::Keyword(x), Value::Keyword(y)) => match_eq(x, y, ctx), (Pattern::Keyword(x), Value::Keyword(y)) => match_eq(x, y, ctx),
(Pattern::String(x), Value::String(y)) => match_eq(x, y, ctx), (Pattern::String(x), Value::InternedString(y)) => match_eq(x, y, ctx),
(Pattern::Word(w), val) => { (Pattern::Word(w), val) => {
ctx.push((w, val.clone())); ctx.push((w, val.clone()));
Some(ctx) Some(ctx)
@ -281,7 +281,8 @@ pub fn eval<'src, 'a>(
Ast::Boolean(b) => Ok(Value::Boolean(*b)), Ast::Boolean(b) => Ok(Value::Boolean(*b)),
Ast::Number(n) => Ok(Value::Number(*n)), Ast::Number(n) => Ok(Value::Number(*n)),
Ast::Keyword(k) => Ok(Value::Keyword(k)), Ast::Keyword(k) => Ok(Value::Keyword(k)),
Ast::String(s) => Ok(Value::String(s)), Ast::String(s) => Ok(Value::InternedString(s)),
Ast::Interpolated(s) => todo!(),
Ast::Block(exprs) => { Ast::Block(exprs) => {
let to = ctx.len(); let to = ctx.len();
let mut result = Value::Nil; let mut result = Value::Nil;
@ -414,10 +415,11 @@ pub fn eval<'src, 'a>(
let value = eval(&value.0, ctx)?; let value = eval(&value.0, ctx)?;
match_clauses(&value, clauses, ctx) match_clauses(&value, clauses, ctx)
} }
Ast::Fn(name, clauses, ..) => { Ast::Fn(name, clauses, doc) => {
let the_fn = Value::Fn::<'src>(Rc::new(Fn::<'src> { let the_fn = Value::Fn::<'src>(Rc::new(Fn::<'src> {
name, name,
body: clauses, body: clauses,
doc,
})); }));
ctx.push((name, the_fn.clone())); ctx.push((name, the_fn.clone()));
Ok(the_fn) Ok(the_fn)