Compare commits

..

No commits in common. "6ba05f31e6a37f482d1d168067be4d1cb73b512d" and "13c14fd38f7007dc8248d3bfb7ead0379d287383" have entirely different histories.

6 changed files with 210 additions and 254 deletions

View File

@ -50,12 +50,11 @@ pub fn store<'src>(b: &Value<'src>, val: &Value<'src>) -> Value<'src> {
pub fn doc<'src>(f: &Value<'src>) -> Value<'src> { pub fn doc<'src>(f: &Value<'src>) -> Value<'src> {
match f { match f {
Value::Fn(f) => { Value::Fn(f) => {
let name = &f.name; let Fn { name, doc, .. } = **f;
let doc = &f.doc;
if let Some(docstr) = doc { if let Some(docstr) = doc {
Value::AllocatedString(Rc::new(format!("{name}: {docstr}"))) Value::AllocatedString(Rc::new(format!("{name}: {docstr}")))
} else { } else {
Value::AllocatedString(Rc::new(format!("{name}: no documentation found"))) Value::InternedString(doc.unwrap_or("no documentation found"))
} }
} }
_ => Value::InternedString("no documentation found"), _ => Value::InternedString("no documentation found"),
@ -570,63 +569,116 @@ pub fn r#mod<'src>(x: &Value, y: &Value) -> Value<'src> {
} }
} }
pub fn base<'src>() -> Vec<(String, Value<'src>)> { pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> {
vec![ vec![
("add".to_string(), Value::Base(Base::Binary(add))), ("add", Value::Base(Base::Binary(add))),
("and".to_string(), Value::Base(Base::Binary(and))), ("and", Value::Base(Base::Binary(and))),
("append".to_string(), Value::Base(Base::Binary(append))), ("append", Value::Base(Base::Binary(append))),
("assoc".to_string(), Value::Base(Base::Ternary(assoc))), ("assoc", Value::Base(Base::Ternary(assoc))),
("at".to_string(), Value::Base(Base::Binary(at))), ("at", Value::Base(Base::Binary(at))),
("atan_2".to_string(), Value::Base(Base::Binary(atan_2))), ("atan_2", Value::Base(Base::Binary(atan_2))),
("bool".to_string(), Value::Base(Base::Unary(r#bool))), ("bool", Value::Base(Base::Unary(r#bool))),
("ceil".to_string(), Value::Base(Base::Unary(ceil))), ("ceil", Value::Base(Base::Unary(ceil))),
("chars".to_string(), Value::Base(Base::Unary(chars))), ("chars", Value::Base(Base::Unary(chars))),
("concat".to_string(), Value::Base(Base::Binary(concat))), ("concat", Value::Base(Base::Binary(concat))),
("cos".to_string(), Value::Base(Base::Unary(cos))), ("cos", Value::Base(Base::Unary(cos))),
("count".to_string(), Value::Base(Base::Unary(count))), ("count", Value::Base(Base::Unary(count))),
("dec".to_string(), Value::Base(Base::Unary(dec))), ("dec", Value::Base(Base::Unary(dec))),
("dissoc".to_string(), Value::Base(Base::Binary(dissoc))), ("dissoc", Value::Base(Base::Binary(dissoc))),
("div".to_string(), Value::Base(Base::Binary(div))), ("div", Value::Base(Base::Binary(div))),
("doc!".to_string(), Value::Base(Base::Unary(doc))), ("doc!", Value::Base(Base::Unary(doc))),
("downcase".to_string(), Value::Base(Base::Unary(downcase))), ("downcase", Value::Base(Base::Unary(downcase))),
("eq?".to_string(), Value::Base(Base::Binary(eq))), ("eq?", Value::Base(Base::Binary(eq))),
("first".to_string(), Value::Base(Base::Unary(first))), ("first", Value::Base(Base::Unary(first))),
("floor".to_string(), Value::Base(Base::Unary(floor))), ("floor", Value::Base(Base::Unary(floor))),
("get".to_string(), Value::Base(Base::Binary(get))), ("get", Value::Base(Base::Binary(get))),
("gt?".to_string(), Value::Base(Base::Binary(gt))), ("gt?", Value::Base(Base::Binary(gt))),
("gte?".to_string(), Value::Base(Base::Binary(gte))), ("gte?", Value::Base(Base::Binary(gte))),
("inc".to_string(), Value::Base(Base::Unary(inc))), ("inc", Value::Base(Base::Unary(inc))),
("last".to_string(), Value::Base(Base::Unary(last))), ("last", Value::Base(Base::Unary(last))),
("list".to_string(), Value::Base(Base::Unary(list))), ("list", Value::Base(Base::Unary(list))),
("lt?".to_string(), Value::Base(Base::Binary(lt))), ("lt?", Value::Base(Base::Binary(lt))),
("lte?".to_string(), Value::Base(Base::Binary(lte))), ("lte?", Value::Base(Base::Binary(lte))),
("mod".to_string(), Value::Base(Base::Binary(r#mod))), ("mod", Value::Base(Base::Binary(r#mod))),
("mult".to_string(), Value::Base(Base::Binary(mult))), ("mult", Value::Base(Base::Binary(mult))),
("number".to_string(), Value::Base(Base::Unary(number))), ("number", Value::Base(Base::Unary(number))),
("or".to_string(), Value::Base(Base::Binary(or))), ("or", Value::Base(Base::Binary(or))),
("pi".to_string(), Value::Number(std::f64::consts::PI)), ("pi", Value::Number(std::f64::consts::PI)),
("print!".to_string(), Value::Base(Base::Unary(print))), ("print!", Value::Base(Base::Unary(print))),
("random".to_string(), Value::Base(Base::Nullary(random))), ("random", Value::Base(Base::Nullary(random))),
("range".to_string(), Value::Base(Base::Binary(range))), ("range", Value::Base(Base::Binary(range))),
("rest".to_string(), Value::Base(Base::Unary(rest))), ("rest", Value::Base(Base::Unary(rest))),
("round".to_string(), Value::Base(Base::Unary(round))), ("round", Value::Base(Base::Unary(round))),
("show".to_string(), Value::Base(Base::Unary(show))), ("show", Value::Base(Base::Unary(show))),
("sin".to_string(), Value::Base(Base::Unary(sin))), ("sin", Value::Base(Base::Unary(sin))),
("slice".to_string(), Value::Base(Base::Ternary(slice))), ("slice", Value::Base(Base::Ternary(slice))),
("split".to_string(), Value::Base(Base::Binary(split))), ("split", Value::Base(Base::Binary(split))),
("sqrt".to_string(), Value::Base(Base::Unary(sqrt))), ("sqrt", Value::Base(Base::Unary(sqrt))),
( ("sqrt_2", Value::Number(std::f64::consts::SQRT_2)),
"sqrt_2".to_string(), ("store!", Value::Base(Base::Binary(store))),
Value::Number(std::f64::consts::SQRT_2), ("sub", Value::Base(Base::Binary(sub))),
), ("tan", Value::Base(Base::Unary(tan))),
("store!".to_string(), Value::Base(Base::Binary(store))), ("trim", Value::Base(Base::Unary(trim))),
("sub".to_string(), Value::Base(Base::Binary(sub))), ("triml", Value::Base(Base::Unary(triml))),
("tan".to_string(), Value::Base(Base::Unary(tan))), ("trimr", Value::Base(Base::Unary(trimr))),
("trim".to_string(), Value::Base(Base::Unary(trim))), ("type", Value::Base(Base::Unary(r#type))),
("triml".to_string(), Value::Base(Base::Unary(triml))), ("unbox", Value::Base(Base::Unary(unbox))),
("trimr".to_string(), Value::Base(Base::Unary(trimr))), ("upcase", Value::Base(Base::Unary(upcase))),
("type".to_string(), Value::Base(Base::Unary(r#type))),
("unbox".to_string(), Value::Base(Base::Unary(unbox))),
("upcase".to_string(), Value::Base(Base::Unary(upcase))),
] ]
} }
// * [x] atan_2 (x) -> number
// * [x] ceil (x) -> number
// * [x] cos (x) -> number
// * [x] floor (x) -> number
// * [x] gt (x, y) -> bool
// * [x] gte! (x, y) -> bool
// * [x] lt (x) -> bool
// * [x] lte (x) -> bool
// * [x] mod (x, y) -> number
// * [x] pi
// * [x] random () -> number
// * [x] round (x) -> number
// * [x] sin (x) -> number
// * [x] sqrt (x) -> number
// * [x] tan (x) -> number
// * [x] add (x, y) -> number
// * [x] and (x, y) -> value
// * [x] append (x, y) -> list
// * [x] assoc (x, y) -> dict
// * [x] bool (x) -> bool
// * [x] chars (x) -> list
// * [x] concat (x, y) -> value
// * [x] count (x) -> number
// * [x] dec (x) -> number
// * [x] dissoc (x, y) -> dict
// * [x] div (x, y) -> number
// * [x] doc (x) -> string
// * [x] downcase (x) -> string
// * [x] eq (x, y) -> bool
// * [x] first (x) -> value
// * [x] get (x, y) -> value
// * [x] inc (x) -> number
// * [x] last (x) -> value
// * [x] or (x, y) -> value
// * [x] print! (x) -> :ok
// * [x] range () -> list
// * [x] rest (x) -> coll
// * [x] show (x) -> string
// * [x] slice (x, y, z) -> list
// * [x] split (x, y) -> list(string)
// * [x] store! (x, y) -> value
// * [x] sub (x, y) -> number
// * [x] to_list (x) -> list
// * [x] to_number (x) -> number
// * [x] trim (x) -> string
// * [x] triml (x) -> string
// * [x] trimr (x) -> string
// * [x] type (x) -> keyword
// * [x] unbox (x) -> value
// * [x] upcase (x) -> string
// * [x] ~prn (x) -> value~
// * [x] ~push (x) -> list~
// * [x] ~str_slice (x, y, z) -> string~
// * [x] ~stringify (x) -> string~
// * [~] disj (x) -> set

View File

@ -3,19 +3,19 @@ use chumsky::prelude::*;
use std::fmt; use std::fmt;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Token { pub enum Token<'src> {
Nil, Nil,
Number(f64), Number(f64),
Word(&'static str), Word(&'src str),
Boolean(bool), Boolean(bool),
Keyword(&'static str), Keyword(&'src str),
String(&'static str), String(&'src str),
// todo: hard code these types // todo: hard code these types
Reserved(&'static str), Reserved(&'src str),
Punctuation(&'static str), Punctuation(&'src str),
} }
impl fmt::Display for Token { impl fmt::Display for Token<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Token::Number(n) => write!(f, "[Number {}]", n), Token::Number(n) => write!(f, "[Number {}]", n),
@ -30,8 +30,8 @@ impl fmt::Display for Token {
} }
} }
pub fn lexer( pub fn lexer<'src>(
) -> impl Parser<'static, &'static str, Vec<(Token, Span)>, extra::Err<Rich<'static, char, Span>>> { ) -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, extra::Err<Rich<'src, char, Span>>> {
let number = just('-') let number = just('-')
.or_not() .or_not()
.then(text::int(10).then(just('.').then(text::digits(10)).or_not())) .then(text::int(10).then(just('.').then(text::digits(10)).or_not()))

View File

@ -58,7 +58,8 @@ use crate::base::*;
pub fn main() { pub fn main() {
let src = " let src = "
let \"foo {bar} baz\" = \"foo bar baz\" let foo = \" fOobArbaz \"
trimr (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

@ -5,26 +5,26 @@ use std::fmt;
use struct_scalpel::Dissectible; use struct_scalpel::Dissectible;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct WhenClause { pub struct WhenClause<'src> {
pub cond: Spanned<Ast>, pub cond: Spanned<Ast<'src>>,
pub body: Spanned<Ast>, pub body: Spanned<Ast<'src>>,
} }
impl fmt::Display for WhenClause { impl<'src> fmt::Display for WhenClause<'src> {
fn fmt(self: &WhenClause, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(self: &WhenClause<'src>, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "cond: {}, body: {}", self.cond.0, self.body.0) write!(f, "cond: {}, body: {}", self.cond.0, self.body.0)
} }
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct MatchClause { pub struct MatchClause<'src> {
pub patt: Spanned<Pattern>, pub patt: Spanned<Pattern<'src>>,
pub guard: Option<Spanned<Ast>>, pub guard: Option<Spanned<Ast<'src>>>,
pub body: Spanned<Ast>, pub body: Spanned<Ast<'src>>,
} }
impl fmt::Display for MatchClause { impl<'src> fmt::Display for MatchClause<'src> {
fn fmt(self: &MatchClause, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(self: &MatchClause<'src>, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
"pattern: {}, guard: {:?} body: {}", "pattern: {}, guard: {:?} body: {}",
@ -52,15 +52,15 @@ impl fmt::Display for StringPart {
} }
#[derive(Clone, Debug, PartialEq, Dissectible)] #[derive(Clone, Debug, PartialEq, Dissectible)]
pub enum Ast { pub enum Ast<'src> {
Error, Error,
Placeholder, Placeholder,
Nil, Nil,
Boolean(bool), Boolean(bool),
Number(f64), Number(f64),
Keyword(&'static str), Keyword(&'src str),
Word(&'static str), Word(&'src str),
String(&'static str), String(&'src str),
Interpolated(Vec<Spanned<StringPart>>), Interpolated(Vec<Spanned<StringPart>>),
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>>),
@ -68,23 +68,23 @@ pub enum Ast {
Arguments(Vec<Spanned<Self>>), Arguments(Vec<Spanned<Self>>),
List(Vec<Spanned<Self>>), List(Vec<Spanned<Self>>),
Dict(Vec<Spanned<Self>>), Dict(Vec<Spanned<Self>>),
Let(Box<Spanned<Pattern>>, Box<Spanned<Self>>), Let(Box<Spanned<Pattern<'src>>>, Box<Spanned<Self>>),
Box(&'static str, Box<Spanned<Self>>), Box(&'src str, Box<Spanned<Self>>),
Synthetic(Box<Spanned<Self>>, Box<Spanned<Self>>, Vec<Spanned<Self>>), Synthetic(Box<Spanned<Self>>, Box<Spanned<Self>>, Vec<Spanned<Self>>),
When(Vec<Spanned<WhenClause>>), When(Vec<Spanned<WhenClause<'src>>>),
Match(Box<Spanned<Self>>, Vec<MatchClause>), Match(Box<Spanned<Self>>, Vec<MatchClause<'src>>),
Fn(&'static str, Vec<MatchClause>, Option<&'static str>), Fn(&'src str, Vec<MatchClause<'src>>, Option<&'src str>),
FnDeclaration(&'static str), FnDeclaration(&'src str),
Panic(Box<Spanned<Self>>), Panic(Box<Spanned<Self>>),
Do(Vec<Spanned<Self>>), Do(Vec<Spanned<Self>>),
Repeat(Box<Spanned<Self>>, Box<Spanned<Self>>), Repeat(Box<Spanned<Self>>, Box<Spanned<Self>>),
Splat(&'static str), Splat(&'src str),
Pair(&'static str, Box<Spanned<Self>>), Pair(&'src str, Box<Spanned<Self>>),
Loop(Box<Spanned<Self>>, Vec<MatchClause>), Loop(Box<Spanned<Self>>, Vec<MatchClause<'src>>),
Recur(Vec<Spanned<Self>>), Recur(Vec<Spanned<Self>>),
} }
impl fmt::Display for Ast { impl fmt::Display for Ast<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Ast::Error => write!(f, "Error"), Ast::Error => write!(f, "Error"),
@ -236,62 +236,36 @@ impl fmt::Display for Ast {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct PairPattern { pub struct PairPattern<'src> {
pub key: &'static str, pub key: &'src str,
pub patt: Spanned<Pattern>, pub patt: Spanned<Pattern<'src>>,
} }
impl fmt::Display for PairPattern { impl<'src> fmt::Display for PairPattern<'src> {
fn fmt(self: &PairPattern, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(self: &PairPattern<'src>, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "pair pattern: {}: {}", self.key, self.patt.0) write!(f, "pair pattern: {}: {}", self.key, self.patt.0)
} }
} }
pub struct StringMatcher(pub Box<dyn Fn(String) -> Option<Vec<(String, String)>>>);
impl PartialEq for StringMatcher {
fn eq(&self, _other: &StringMatcher) -> bool {
true
}
}
impl Clone for StringMatcher {
fn clone(&self) -> StringMatcher {
unreachable!()
}
}
impl fmt::Display for StringMatcher {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "string matcher")
}
}
impl fmt::Debug for StringMatcher {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "string matcher")
}
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Pattern { pub enum Pattern<'src> {
Nil, Nil,
Boolean(bool), Boolean(bool),
Number(f64), Number(f64),
String(&'static str), String(&'src str),
Interpolated(Vec<Spanned<StringPart>>, StringMatcher), Interpolated(Vec<StringPart>),
Keyword(&'static str), Keyword(&'src str),
Word(&'static str), Word(&'src str),
As(&'static str, &'static str), As(&'src str, &'src str),
Splattern(Box<Spanned<Self>>), Splattern(Box<Spanned<Self>>),
Placeholder, Placeholder,
Tuple(Vec<Spanned<Self>>), Tuple(Vec<Spanned<Self>>),
List(Vec<Spanned<Self>>), List(Vec<Spanned<Self>>),
Pair(&'static str, Box<Spanned<Self>>), Pair(&'src str, Box<Spanned<Self>>),
Dict(Vec<Spanned<Self>>), Dict(Vec<Spanned<Self>>),
} }
impl fmt::Display for Pattern { impl fmt::Display for Pattern<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Pattern::Nil => write!(f, "nil"), Pattern::Nil => write!(f, "nil"),
@ -329,12 +303,12 @@ impl fmt::Display for Pattern {
.join(", ") .join(", ")
), ),
Pattern::Pair(key, value) => write!(f, ":{} {}", key, value.0), Pattern::Pair(key, value) => write!(f, ":{} {}", key, value.0),
Pattern::Interpolated(strprts, _) => write!( Pattern::Interpolated(strprts) => write!(
f, f,
"interpolated: \"{}\"", "\"{}\"",
strprts strprts
.iter() .iter()
.map(|part| part.0.to_string()) .map(|part| part.to_string())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("") .join("")
), ),
@ -349,7 +323,14 @@ fn is_word_char(c: char) -> bool {
matches!(c, '_' | '/' | '?' | '!') matches!(c, '_' | '/' | '?' | '!')
} }
fn parse_string(s: &'static str, span: SimpleSpan) -> Result<Vec<Spanned<StringPart>>, String> { // 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}}}
fn parse_string(s: &str, span: SimpleSpan) -> Result<Vec<Spanned<StringPart>>, String> {
let mut parts = vec![]; let mut parts = vec![];
let mut current_part = String::new(); let mut current_part = String::new();
let mut start = span.start; let mut start = span.start;
@ -386,7 +367,7 @@ fn parse_string(s: &'static str, span: SimpleSpan) -> Result<Vec<Spanned<StringP
'}' => { '}' => {
if is_word { if is_word {
parts.push(( parts.push((
StringPart::Word(current_part.clone()), StringPart::Word(current_part),
SimpleSpan::new(start, start + i), SimpleSpan::new(start, start + i),
)); ));
current_part = String::new(); current_part = String::new();
@ -413,68 +394,23 @@ fn parse_string(s: &'static str, span: SimpleSpan) -> Result<Vec<Spanned<StringP
} }
} }
if current_part == s {
parts.push(( parts.push((
StringPart::Inline(current_part), if is_word {
StringPart::Word(current_part)
} else if current_part == s {
StringPart::Inline(current_part)
} else {
StringPart::Data(current_part)
},
SimpleSpan::new(start, span.end), SimpleSpan::new(start, span.end),
)) ));
}
Ok(parts) Ok(parts)
} }
pub fn compile_string_pattern(parts: Vec<Spanned<StringPart>>) -> StringMatcher { pub fn parser<'src, I>(
StringMatcher(Box::new(move |scrutinee| { ) -> impl Parser<'src, I, Spanned<Ast<'src>>, extra::Err<Rich<'src, Token<'src>, Span>>> + Clone
let mut last_match = 0;
let mut parts_iter = parts.iter();
let mut matches = vec![];
while let Some((part, _)) = parts_iter.next() {
match part {
StringPart::Data(string) => match scrutinee.find(string.as_str()) {
Some(i) => {
// if i = 0, we're at the beginning
if i == 0 && last_match == 0 {
last_match = i + string.len();
continue;
}
// in theory, we only hit this branch if the first part is Data
unreachable!("internal Ludus error: bad string pattern")
}
None => return None,
},
StringPart::Word(word) => {
let to_test = scrutinee.get(last_match..scrutinee.len()).unwrap();
match parts_iter.next() {
None => matches.push((word.clone(), to_test.to_string())),
Some(part) => {
let (StringPart::Data(part), _) = part else {
unreachable!("internal Ludus error: bad string pattern")
};
match to_test.find(part) {
None => return None,
Some(i) => {
matches.push((
word.clone(),
to_test.get(last_match..i).unwrap().to_string(),
));
last_match = i + part.len();
continue;
}
}
}
}
}
_ => unreachable!("internal Ludus error"),
}
}
Some(matches)
}))
}
pub fn parser<I>(
) -> impl Parser<'static, I, Spanned<Ast>, extra::Err<Rich<'static, Token, Span>>> + Clone
where where
I: ValueInput<'static, Token = Token, Span = Span>, I: ValueInput<'src, Token = Token<'src>, Span = Span>,
{ {
let mut expr = Recursive::declare(); let mut expr = Recursive::declare();
@ -507,23 +443,11 @@ where
Token::Boolean(b) => Pattern::Boolean(b), Token::Boolean(b) => Pattern::Boolean(b),
Token::Number(n) => Pattern::Number(n), Token::Number(n) => Pattern::Number(n),
Token::Keyword(k) => Pattern::Keyword(k), Token::Keyword(k) => Pattern::Keyword(k),
// todo: actual string patterns
Token::String(s) => Pattern::String(s)
} }
.map_with(|a, e| (a, e.span())); .map_with(|a, e| (a, e.span()));
let string_pattern = select! {Token::String(s) => s}.try_map_with(|s, e| {
let parsed = parse_string(s, e.span());
match parsed {
Ok(parts) => match parts[0] {
(StringPart::Inline(_), _) => Ok((Pattern::String(s), e.span())),
_ => Ok((
Pattern::Interpolated(parts.clone(), compile_string_pattern(parts)),
e.span(),
)),
},
Err(msg) => Err(Rich::custom(e.span(), msg)),
}
});
let bare_splat = just(Token::Punctuation("...")).map_with(|_, e| { let bare_splat = just(Token::Punctuation("...")).map_with(|_, e| {
( (
Pattern::Splattern(Box::new((Pattern::Placeholder, e.span()))), Pattern::Splattern(Box::new((Pattern::Placeholder, e.span()))),
@ -593,7 +517,6 @@ where
pattern.define( pattern.define(
atom_pattern atom_pattern
.or(string_pattern)
.or(as_pattern) .or(as_pattern)
.or(word_pattern) .or(word_pattern)
.or(placeholder_pattern) .or(placeholder_pattern)

View File

@ -8,9 +8,9 @@ use struct_scalpel::Dissectible;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Fn<'src> { pub struct Fn<'src> {
pub name: String, pub name: &'src str,
pub body: &'src Vec<MatchClause>, pub body: &'src Vec<MatchClause<'src>>,
pub doc: Option<String>, pub doc: &'src Option<&'src str>,
} }
#[derive(Debug, Dissectible)] #[derive(Debug, Dissectible)]
@ -19,10 +19,10 @@ pub enum Value<'src> {
Placeholder, Placeholder,
Boolean(bool), Boolean(bool),
Number(f64), Number(f64),
Keyword(&'static str), Keyword(&'src str),
// TODO: add a "runtime-generated" string type that wraps a Rust String // TODO: add a "runtime-generated" string type that wraps a Rust String
// this is necessary for nice documentation and string interpolation // this is necessary for nice documentation and string interpolation
InternedString(&'static str), InternedString(&'src str),
AllocatedString(Rc<String>), AllocatedString(Rc<String>),
// on the heap for now // on the heap for now
Tuple(Rc<Vec<Self>>), Tuple(Rc<Vec<Self>>),
@ -31,8 +31,8 @@ pub enum Value<'src> {
List(Vector<Self>), List(Vector<Self>),
// ref-counted, immutable, persistent // ref-counted, immutable, persistent
// dicts may only use keywords as keys // dicts may only use keywords as keys
Dict(HashMap<&'static str, Self>), Dict(HashMap<&'src str, Self>),
Box(&'static str, Rc<RefCell<Self>>), Box(&'src str, Rc<RefCell<Self>>),
Fn(Rc<Fn<'src>>), Fn(Rc<Fn<'src>>),
Base(Base<'src>), Base(Base<'src>),
Recur(Vec<Self>), Recur(Vec<Self>),
@ -62,7 +62,7 @@ impl<'src> Clone for Value<'src> {
Value::Fn(f) => Value::Fn(f.clone()), Value::Fn(f) => Value::Fn(f.clone()),
Value::List(l) => Value::List(l.clone()), Value::List(l) => Value::List(l.clone()),
Value::Dict(d) => Value::Dict(d.clone()), Value::Dict(d) => Value::Dict(d.clone()),
Value::Box(name, b) => Value::Box(*name, b.clone()), Value::Box(name, b) => Value::Box(name, b.clone()),
Value::Placeholder => Value::Placeholder, Value::Placeholder => Value::Placeholder,
Value::Base(b) => Value::Base(b.clone()), Value::Base(b) => Value::Base(b.clone()),
Value::Recur(..) => unreachable!(), Value::Recur(..) => unreachable!(),

View File

@ -53,10 +53,10 @@ where
} }
pub fn match_pattern<'src, 'a>( pub fn match_pattern<'src, 'a>(
patt: &Pattern, patt: &Pattern<'src>,
val: &Value<'src>, val: &Value<'src>,
ctx: &'a mut Vec<(String, Value<'src>)>, ctx: &'a mut Vec<(&'src str, Value<'src>)>,
) -> Option<&'a mut Vec<(String, Value<'src>)>> { ) -> Option<&'a mut Vec<(&'src str, Value<'src>)>> {
match (patt, val) { match (patt, val) {
(Pattern::Nil, Value::Nil) => Some(ctx), (Pattern::Nil, Value::Nil) => Some(ctx),
(Pattern::Placeholder, _) => Some(ctx), (Pattern::Placeholder, _) => Some(ctx),
@ -64,34 +64,15 @@ pub fn match_pattern<'src, 'a>(
(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::InternedString(y)) => match_eq(x, y, ctx), (Pattern::String(x), Value::InternedString(y)) => match_eq(x, y, ctx),
(Pattern::String(x), Value::AllocatedString(y)) => match_eq(&x.to_string(), y, ctx),
(Pattern::Interpolated(_, StringMatcher(matcher)), Value::InternedString(y)) => {
match matcher(y.to_string()) {
Some(matches) => {
let mut matches = matches
.iter()
.map(|(word, string)| {
(
word.clone(),
Value::AllocatedString(Rc::new(string.clone())),
)
})
.collect::<Vec<_>>();
ctx.append(&mut matches);
Some(ctx)
}
None => None,
}
}
(Pattern::Word(w), val) => { (Pattern::Word(w), val) => {
ctx.push((w.to_string(), val.clone())); ctx.push((w, val.clone()));
Some(ctx) Some(ctx)
} }
(Pattern::As(word, type_str), value) => { (Pattern::As(word, type_str), value) => {
let ludus_type = r#type(value); let ludus_type = r#type(value);
let type_kw = Value::Keyword(type_str); let type_kw = Value::Keyword(type_str);
if type_kw == ludus_type { if type_kw == ludus_type {
ctx.push((word.to_string(), value.clone())); ctx.push((word, value.clone()));
Some(ctx) Some(ctx)
} else { } else {
None None
@ -181,7 +162,7 @@ pub fn match_pattern<'src, 'a>(
for key in matched.iter() { for key in matched.iter() {
unmatched.remove(*key); unmatched.remove(*key);
} }
ctx.push((w.to_string(), Value::Dict(unmatched))); ctx.push((w, Value::Dict(unmatched)));
} }
Pattern::Placeholder => (), Pattern::Placeholder => (),
_ => unreachable!(), _ => unreachable!(),
@ -197,8 +178,8 @@ pub fn match_pattern<'src, 'a>(
pub fn match_clauses<'src>( pub fn match_clauses<'src>(
value: &Value<'src>, value: &Value<'src>,
clauses: &'src Vec<MatchClause>, clauses: &'src Vec<MatchClause<'src>>,
ctx: &mut Vec<(String, Value<'src>)>, ctx: &mut Vec<(&'src str, Value<'src>)>,
) -> Result<Value<'src>, LudusError> { ) -> Result<Value<'src>, LudusError> {
let to = ctx.len(); let to = ctx.len();
for MatchClause { patt, body, guard } in clauses.iter() { for MatchClause { patt, body, guard } in clauses.iter() {
@ -234,7 +215,7 @@ pub fn match_clauses<'src>(
pub fn apply<'src>( pub fn apply<'src>(
callee: Value<'src>, callee: Value<'src>,
caller: Value<'src>, caller: Value<'src>,
ctx: &mut Vec<(String, Value<'src>)>, ctx: &mut Vec<(&'src str, Value<'src>)>,
) -> Result<Value<'src>, LudusError> { ) -> Result<Value<'src>, LudusError> {
match (callee, caller) { match (callee, caller) {
(Value::Keyword(kw), Value::Dict(dict)) => { (Value::Keyword(kw), Value::Dict(dict)) => {
@ -303,8 +284,8 @@ pub fn apply<'src>(
} }
pub fn eval<'src, 'a>( pub fn eval<'src, 'a>(
ast: &'src Ast, ast: &'src Ast<'src>,
ctx: &'a mut Vec<(String, Value<'src>)>, ctx: &'a mut Vec<(&'src str, Value<'src>)>,
) -> Result<Value<'src>, LudusError> { ) -> Result<Value<'src>, LudusError> {
match ast { match ast {
Ast::Nil => Ok(Value::Nil), Ast::Nil => Ok(Value::Nil),
@ -438,7 +419,7 @@ pub fn eval<'src, 'a>(
Ast::Box(name, expr) => { Ast::Box(name, expr) => {
let val = eval(&expr.0, ctx)?; let val = eval(&expr.0, ctx)?;
let boxed = Value::Box(name, Rc::new(RefCell::new(val))); let boxed = Value::Box(name, Rc::new(RefCell::new(val)));
ctx.push((name.to_string(), boxed.clone())); ctx.push((name, boxed.clone()));
Ok(boxed) Ok(boxed)
} }
Ast::Synthetic(root, first, rest) => { Ast::Synthetic(root, first, rest) => {
@ -467,13 +448,12 @@ pub fn eval<'src, 'a>(
match_clauses(&value, clauses, ctx) match_clauses(&value, clauses, ctx)
} }
Ast::Fn(name, clauses, doc) => { Ast::Fn(name, clauses, doc) => {
let doc = doc.map(|s| s.to_string());
let the_fn = Value::Fn::<'src>(Rc::new(Fn::<'src> { let the_fn = Value::Fn::<'src>(Rc::new(Fn::<'src> {
name: name.to_string(), name,
body: clauses, body: clauses,
doc, doc,
})); }));
ctx.push((name.to_string(), the_fn.clone())); ctx.push((name, the_fn.clone()));
Ok(the_fn) Ok(the_fn)
} }
Ast::FnDeclaration(_name) => todo!(), Ast::FnDeclaration(_name) => todo!(),