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
use crate::value::*;
use crate::vm::*;
use std::rc::Rc;
// use imbl::*;
// 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>)> {
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(("doc", Value::Base(Base::Unary("doc", doc))));
base
}

View File

@ -63,7 +63,7 @@ fn foo {
() -> :foo
(_) -> :bar
}
foo ()
doc (foo)
";
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
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)]
pub enum Ast<'src> {
Error,
@ -43,6 +59,7 @@ pub enum Ast<'src> {
Keyword(&'src str),
Word(&'src str),
String(&'src str),
Interpolated(Vec<Spanned<StringPart<'src>>>),
Block(Vec<Spanned<Self>>),
If(Box<Spanned<Self>>, Box<Spanned<Self>>, Box<Spanned<Self>>),
Tuple(Vec<Spanned<Self>>),
@ -71,6 +88,16 @@ impl fmt::Display for Ast<'_> {
Ast::Error => write!(f, "Error"),
Ast::Nil => write!(f, "nil"),
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::Number(n) => write!(f, "Number: {}", n),
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>(
) -> impl Parser<'src, I, Spanned<Ast<'src>>, extra::Err<Rich<'src, Token<'src>, Span>>> + Clone
where

View File

@ -10,6 +10,7 @@ use struct_scalpel::Dissectible;
pub struct Fn<'src> {
pub name: &'src str,
pub body: &'src Vec<MatchClause<'src>>,
pub doc: &'src Option<&'src str>,
}
#[derive(Debug, Dissectible)]
@ -19,7 +20,10 @@ pub enum Value<'src> {
Boolean(bool),
Number(f64),
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
Tuple(Rc<Vec<Self>>),
Args(Rc<Vec<Self>>),
@ -49,7 +53,8 @@ impl<'src> Clone for Value<'src> {
match self {
Value::Nil => Value::Nil,
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::Number(n) => Value::Number(*n),
Value::Tuple(t) => Value::Tuple(t.clone()),
@ -72,7 +77,8 @@ impl fmt::Display for Value<'_> {
Value::Boolean(b) => write!(f, "{}", b),
Value::Number(n) => write!(f, "{}", n),
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::Tuple(t) | Value::Args(t) => write!(
f,
@ -132,7 +138,8 @@ impl Value<'_> {
Value::Boolean(_) => Value::Keyword("boolean"),
Value::Keyword(_) => Value::Keyword("keyword"),
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::Dict(_) => Value::Keyword("dict"),
Value::Fn(_) => Value::Keyword("fn"),
@ -152,7 +159,7 @@ impl<'src> PartialEq for Value<'src> {
(Value::Nil, Value::Nil) => true,
(Value::Boolean(x), Value::Boolean(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::Tuple(x), Value::Tuple(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::Boolean(x), Value::Boolean(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) => {
ctx.push((w, val.clone()));
Some(ctx)
@ -281,7 +281,8 @@ pub fn eval<'src, 'a>(
Ast::Boolean(b) => Ok(Value::Boolean(*b)),
Ast::Number(n) => Ok(Value::Number(*n)),
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) => {
let to = ctx.len();
let mut result = Value::Nil;
@ -414,10 +415,11 @@ pub fn eval<'src, 'a>(
let value = eval(&value.0, 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> {
name,
body: clauses,
doc,
}));
ctx.push((name, the_fn.clone()));
Ok(the_fn)