add ref-counted string type
This commit is contained in:
parent
2a83dbb96c
commit
c5c1717e57
21
src/base.rs
21
src/base.rs
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
|
17
src/value.rs
17
src/value.rs
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user