Compare commits

..

No commits in common. "db52bc2687b9f1cfdd1baaa4b4ef7d19eb553ef9" and "759fc63caea9e0ff99d8f3a79925ed3e4b6cee3d" have entirely different histories.

9 changed files with 531 additions and 496 deletions

View File

@ -1218,11 +1218,8 @@ fn self {
}
fn send {
"Sends a message to the specified process and returns the message."
(pid as :keyword, msg) -> {
base :process (:send, pid, msg)
msg
}
"Sends a message to the specified process."
(pid as :keyword, msg) -> base :process (:send, pid, msg)
}
fn spawn! {
@ -1248,11 +1245,21 @@ fn link! {
(pid1 as :keyword, pid2 as :keyword, :enforce) -> base :process (:link_enforce, pid1, pid2)
}
fn msgs {
"Returns the entire contents of the current process as a list. Leaves all messages in the process mailbox."
() -> base :process (:msgs)
}
fn flush! {
"Clears the current process's mailbox and returns all the messages."
"Clears the current process's mailbox."
() -> base :process (:flush)
}
fn flush_i! {
"Flushes the message at the indicated index in the current process's mailbox."
(i as :number) -> base :process (:flush_i, i)
}
fn sleep! {
"Puts the current process to sleep for at least the specified number of milliseconds."
(ms as :number) -> base :process (:sleep, ms)

View File

@ -969,8 +969,7 @@ match (nil) with {
In particular, function arities need to be changed, flushes need to be inserted, anonymous lambdas need to be created (which can't be multi-clause), etc.
~* [ ] There was another bug that I was going to write down and fix, but I forgot what it was. Something with processes.~
* [ ] I remembered: I got some weird behaviour when `MAX_REDUCTIONS` was set to 100; I've increased it to 1000, but now need to test what happens when we yield because of reductions.
* [ ] There was another bug that I was going to write down and fix, but I forgot what it was. Something with processes.
* [ ] Also: the `butlast` bug is still outstanding: `base :slice` causes a panic in that function, but not the call to the Ludus function. Still have to investigate that one.

View File

@ -1,459 +0,0 @@
use crate::parser::StringMatcher;
use crate::spans::*;
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StringPart {
Data(String),
Word(String),
Inline(String),
}
impl fmt::Display for StringPart {
fn fmt(self: &StringPart, f: &mut fmt::Formatter) -> fmt::Result {
let rep = match self {
StringPart::Word(s) => format!("{{{s}}}"),
StringPart::Data(s) => s.to_string(),
StringPart::Inline(s) => s.to_string(),
};
write!(f, "{}", rep)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Ast {
// a special Error node
// may come in handy?
Error,
And,
Or,
// expression nodes
Placeholder,
Nil,
Boolean(bool),
Number(f64),
Keyword(&'static str),
Word(&'static str),
String(&'static str),
Interpolated(Vec<Spanned<StringPart>>),
Block(Vec<Spanned<Self>>),
If(Box<Spanned<Self>>, Box<Spanned<Self>>, Box<Spanned<Self>>),
Tuple(Vec<Spanned<Self>>),
Arguments(Vec<Spanned<Self>>),
List(Vec<Spanned<Self>>),
Dict(Vec<Spanned<Self>>),
Let(Box<Spanned<Self>>, Box<Spanned<Self>>),
LBox(&'static str, Box<Spanned<Self>>),
Synthetic(Box<Spanned<Self>>, Box<Spanned<Self>>, Vec<Spanned<Self>>),
When(Vec<Spanned<Self>>),
WhenClause(Box<Spanned<Self>>, Box<Spanned<Self>>),
Match(Box<Spanned<Self>>, Vec<Spanned<Self>>),
Receive(Vec<Spanned<Self>>),
MatchClause(
Box<Spanned<Self>>,
Box<Option<Spanned<Self>>>,
Box<Spanned<Self>>,
),
Fn(&'static str, Box<Spanned<Ast>>, Option<&'static str>),
FnBody(Vec<Spanned<Ast>>),
FnDeclaration(&'static str),
Panic(Box<Spanned<Self>>),
Do(Vec<Spanned<Self>>),
Repeat(Box<Spanned<Self>>, Box<Spanned<Self>>),
Splat(&'static str),
Pair(&'static str, Box<Spanned<Self>>),
Loop(Box<Spanned<Self>>, Vec<Spanned<Self>>),
Recur(Vec<Spanned<Self>>),
// pattern nodes
NilPattern,
BooleanPattern(bool),
NumberPattern(f64),
StringPattern(&'static str),
InterpolatedPattern(Vec<Spanned<StringPart>>),
KeywordPattern(&'static str),
WordPattern(&'static str),
AsPattern(&'static str, &'static str),
Splattern(Box<Spanned<Self>>),
PlaceholderPattern,
TuplePattern(Vec<Spanned<Self>>),
ListPattern(Vec<Spanned<Self>>),
PairPattern(&'static str, Box<Spanned<Self>>),
DictPattern(Vec<Spanned<Self>>),
}
impl Ast {
pub fn show(&self) -> String {
use Ast::*;
match self {
And => "and".to_string(),
Or => "or".to_string(),
Error => unreachable!(),
Nil | NilPattern => "nil".to_string(),
String(s) | StringPattern(s) => format!("\"{s}\""),
Interpolated(strs) | InterpolatedPattern(strs) => {
let mut out = "".to_string();
out = format!("\"{out}");
for (part, _) in strs {
out = format!("{out}{part}");
}
format!("{out}\"")
}
Boolean(b) | BooleanPattern(b) => b.to_string(),
Number(n) | NumberPattern(n) => n.to_string(),
Keyword(k) | KeywordPattern(k) => format!(":{k}"),
Word(w) | WordPattern(w) => w.to_string(),
Block(lines) => {
let mut out = "{\n".to_string();
for (line, _) in lines {
out = format!("{out}\n {}", line.show());
}
format!("{out}\n}}")
}
If(cond, then, r#else) => format!(
"if {}\n then {}\n else {}",
cond.0.show(),
then.0.show(),
r#else.0.show()
),
Let(pattern, expression) => {
format!("let {} = {}", pattern.0.show(), expression.0.show())
}
Dict(entries) | DictPattern(entries) => {
format!(
"#{{{}}}",
entries
.iter()
.map(|(pair, _)| pair.show())
.collect::<Vec<_>>()
.join(", ")
)
}
List(members) | ListPattern(members) => format!(
"[{}]",
members
.iter()
.map(|(member, _)| member.show())
.collect::<Vec<_>>()
.join(", ")
),
Arguments(members) => format!(
"({})",
members
.iter()
.map(|(member, _)| member.show())
.collect::<Vec<_>>()
.join(", ")
),
Tuple(members) | TuplePattern(members) => format!(
"({})",
members
.iter()
.map(|(member, _)| member.show())
.collect::<Vec<_>>()
.join(", ")
),
Synthetic(root, first, rest) => format!(
"{} {} {}",
root.0.show(),
first.0.show(),
rest.iter()
.map(|(term, _)| term.show())
.collect::<Vec<_>>()
.join(" ")
),
When(clauses) => format!(
"when {{\n {}\n}}",
clauses
.iter()
.map(|(clause, _)| clause.show())
.collect::<Vec<_>>()
.join("\n ")
),
Placeholder | PlaceholderPattern => "_".to_string(),
LBox(name, rhs) => format!("box {name} = {}", rhs.0.show()),
Match(scrutinee, clauses) => format!(
"match {} with {{\n {}\n}}",
scrutinee.0.show(),
clauses
.iter()
.map(|(clause, _)| clause.show())
.collect::<Vec<_>>()
.join("\n ")
),
FnBody(clauses) => clauses
.iter()
.map(|(clause, _)| clause.show())
.collect::<Vec<_>>()
.join("\n "),
Fn(name, body, doc) => {
let mut out = format!("fn {name} {{\n");
if let Some(doc) = doc {
out = format!("{out} {doc}\n");
}
format!("{out} {}\n}}", body.0.show())
}
FnDeclaration(name) => format!("fn {name}"),
Panic(expr) => format!("panic! {}", expr.0.show()),
Do(terms) => {
format!(
"do {}",
terms
.iter()
.map(|(term, _)| term.show())
.collect::<Vec<_>>()
.join(" > ")
)
}
Repeat(times, body) => format!("repeat {} {{\n{}\n}}", times.0.show(), body.0.show()),
Splat(word) => format!("...{}", word),
Splattern(pattern) => format!("...{}", pattern.0.show()),
AsPattern(word, type_keyword) => format!("{word} as :{type_keyword}"),
Pair(key, value) | PairPattern(key, value) => format!(":{key} {}", value.0.show()),
Loop(init, body) => format!(
"loop {} with {{\n {}\n}}",
init.0.show(),
body.iter()
.map(|(clause, _)| clause.show())
.collect::<Vec<_>>()
.join("\n ")
),
Recur(args) => format!(
"recur ({})",
args.iter()
.map(|(arg, _)| arg.show())
.collect::<Vec<_>>()
.join(", ")
),
MatchClause(pattern, guard, body) => {
let mut out = pattern.0.show();
if let Some(guard) = guard.as_ref() {
out = format!("{out} if {}", guard.0.show());
}
format!("{out} -> {}", body.0.show())
}
WhenClause(cond, body) => format!("{} -> {}", cond.0.show(), body.0.show()),
}
}
}
impl fmt::Display for Ast {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Ast::*;
match self {
And => write!(f, "And"),
Or => write!(f, "Or"),
Error => write!(f, "Error"),
Nil => write!(f, "nil"),
String(s) => write!(f, "String: \"{}\"", s),
Interpolated(strs) => {
write!(
f,
"Interpolated: \"{}\"",
strs.iter()
.map(|(s, _)| s.to_string())
.collect::<Vec<_>>()
.join("")
)
}
Boolean(b) => write!(f, "Boolean: {}", b),
Number(n) => write!(f, "Number: {}", n),
Keyword(k) => write!(f, "Keyword: :{}", k),
Word(w) => write!(f, "Word: {}", w),
Block(b) => write!(
f,
"Block: <{}>",
b.iter()
.map(|(line, _)| line.to_string())
.collect::<Vec<_>>()
.join("\n")
),
If(cond, then_branch, else_branch) => write!(
f,
"If: {} Then: {} Else: {}",
cond.0, then_branch.0, else_branch.0
),
Let(pattern, expression) => {
write!(f, "Let: {} = {}", pattern.0, expression.0)
}
Dict(entries) => write!(
f,
"#{{{}}}",
entries
.iter()
.map(|pair| pair.0.to_string())
.collect::<Vec<_>>()
.join(", ")
),
List(l) => write!(
f,
"List: [{}]",
l.iter()
.map(|(line, _)| line.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Arguments(a) => write!(
f,
"Arguments: ({})",
a.iter()
.map(|(line, _)| line.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Tuple(t) => write!(
f,
"Tuple: ({})",
t.iter()
.map(|(line, _)| line.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Synthetic(root, first, rest) => write!(
f,
"Synth: [{}, {}, {}]",
root.0,
first.0,
rest.iter()
.map(|(term, _)| term.to_string())
.collect::<Vec<_>>()
.join("\n")
),
When(clauses) => write!(
f,
"When: [{}]",
clauses
.iter()
.map(|clause| clause.0.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Placeholder => write!(f, "Placeholder"),
LBox(_name, _rhs) => todo!(),
Match(value, clauses) => {
write!(
f,
"match: {} with {}",
&value.0.to_string(),
clauses
.iter()
.map(|clause| clause.0.to_string())
.collect::<Vec<_>>()
.join("\n")
)
}
FnBody(clauses) => {
write!(
f,
"{}",
clauses
.iter()
.map(|clause| clause.0.to_string())
.collect::<Vec<_>>()
.join("\n")
)
}
Fn(name, body, ..) => {
write!(f, "fn: {name}\n{}", body.0)
}
FnDeclaration(_name) => todo!(),
Panic(_expr) => todo!(),
Do(terms) => {
write!(
f,
"do: {}",
terms
.iter()
.map(|(term, _)| term.to_string())
.collect::<Vec<_>>()
.join(" > ")
)
}
Repeat(_times, _body) => todo!(),
Splat(word) => {
write!(f, "splat: {}", word)
}
Pair(k, v) => {
write!(f, "pair: {} {}", k, v.0)
}
Loop(init, body) => {
write!(
f,
"loop: {} with {}",
init.0,
body.iter()
.map(|clause| clause.0.to_string())
.collect::<Vec<_>>()
.join("\n")
)
}
Recur(args) => {
write!(
f,
"recur: {}",
args.iter()
.map(|(arg, _)| arg.to_string())
.collect::<Vec<_>>()
.join(", ")
)
}
MatchClause(pattern, guard, body) => {
write!(
f,
"match clause: {} if {:?} -> {}",
pattern.0, guard, body.0
)
}
WhenClause(cond, body) => {
write!(f, "when clause: {} -> {}", cond.0, body.0)
}
NilPattern => write!(f, "nil"),
BooleanPattern(b) => write!(f, "{}", b),
NumberPattern(n) => write!(f, "{}", n),
StringPattern(s) => write!(f, "{}", s),
KeywordPattern(k) => write!(f, ":{}", k),
WordPattern(w) => write!(f, "{}", w),
AsPattern(w, t) => write!(f, "{} as :{}", w, t),
Splattern(p) => write!(f, "...{}", p.0),
PlaceholderPattern => write!(f, "_"),
TuplePattern(t) => write!(
f,
"({})",
t.iter()
.map(|x| x.0.to_string())
.collect::<Vec<_>>()
.join(", ")
),
ListPattern(l) => write!(
f,
"({})",
l.iter()
.map(|x| x.0.to_string())
.collect::<Vec<_>>()
.join(", ")
),
DictPattern(entries) => write!(
f,
"#{{{}}}",
entries
.iter()
.map(|(pair, _)| pair.to_string())
.collect::<Vec<_>>()
.join(", ")
),
PairPattern(key, value) => write!(f, ":{} {}", key, value.0),
InterpolatedPattern(strprts) => write!(
f,
"interpolated: \"{}\"",
strprts
.iter()
.map(|part| part.0.to_string())
.collect::<Vec<_>>()
.join("")
),
}
}
}

View File

@ -1,6 +1,7 @@
use crate::ast::{Ast, StringPart};
use crate::chunk::{Chunk, StrPattern};
use crate::op::Op;
use crate::parser::Ast;
use crate::parser::StringPart;
use crate::spans::Spanned;
use crate::value::*;
use chumsky::prelude::SimpleSpan;

View File

@ -56,9 +56,7 @@ pub fn lexer(
"nil" => Token::Nil,
// todo: hard code these as type constructors
"as" | "box" | "do" | "else" | "fn" | "if" | "let" | "loop" | "match" | "panic!"
| "recur" | "repeat" | "then" | "when" | "with" | "or" | "and" | "receive" => {
Token::Reserved(word)
}
| "recur" | "repeat" | "then" | "when" | "with" | "or" | "and" => Token::Reserved(word),
_ => Token::Word(word),
});

View File

@ -7,9 +7,6 @@ const DEBUG_SCRIPT_RUN: bool = false;
const DEBUG_PRELUDE_COMPILE: bool = false;
const DEBUG_PRELUDE_RUN: bool = false;
mod ast;
use crate::ast::Ast;
mod base;
mod world;
@ -22,7 +19,7 @@ mod lexer;
use crate::lexer::lexer;
mod parser;
use crate::parser::parser;
use crate::parser::{parser, Ast};
mod validator;
use crate::validator::Validator;

View File

@ -2,13 +2,467 @@
// TODO: remove StringMatcher cruft
// TODO: good error messages?
use crate::ast::{Ast, StringPart};
use crate::lexer::*;
use crate::spans::*;
use chumsky::{input::ValueInput, prelude::*, recursive::Recursive};
use std::fmt;
pub struct StringMatcher();
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StringPart {
Data(String),
Word(String),
Inline(String),
}
impl fmt::Display for StringPart {
fn fmt(self: &StringPart, f: &mut fmt::Formatter) -> fmt::Result {
let rep = match self {
StringPart::Word(s) => format!("{{{s}}}"),
StringPart::Data(s) => s.to_string(),
StringPart::Inline(s) => s.to_string(),
};
write!(f, "{}", rep)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Ast {
// a special Error node
// may come in handy?
Error,
And,
Or,
// expression nodes
Placeholder,
Nil,
Boolean(bool),
Number(f64),
Keyword(&'static str),
Word(&'static str),
String(&'static str),
Interpolated(Vec<Spanned<StringPart>>),
Block(Vec<Spanned<Self>>),
If(Box<Spanned<Self>>, Box<Spanned<Self>>, Box<Spanned<Self>>),
Tuple(Vec<Spanned<Self>>),
Arguments(Vec<Spanned<Self>>),
List(Vec<Spanned<Self>>),
Dict(Vec<Spanned<Self>>),
Let(Box<Spanned<Self>>, Box<Spanned<Self>>),
LBox(&'static str, Box<Spanned<Self>>),
Synthetic(Box<Spanned<Self>>, Box<Spanned<Self>>, Vec<Spanned<Self>>),
When(Vec<Spanned<Self>>),
WhenClause(Box<Spanned<Self>>, Box<Spanned<Self>>),
Match(Box<Spanned<Self>>, Vec<Spanned<Self>>),
MatchClause(
Box<Spanned<Self>>,
Box<Option<Spanned<Self>>>,
Box<Spanned<Self>>,
),
Fn(&'static str, Box<Spanned<Ast>>, Option<&'static str>),
FnBody(Vec<Spanned<Ast>>),
FnDeclaration(&'static str),
Panic(Box<Spanned<Self>>),
Do(Vec<Spanned<Self>>),
Repeat(Box<Spanned<Self>>, Box<Spanned<Self>>),
Splat(&'static str),
Pair(&'static str, Box<Spanned<Self>>),
Loop(Box<Spanned<Self>>, Vec<Spanned<Self>>),
Recur(Vec<Spanned<Self>>),
// pattern nodes
NilPattern,
BooleanPattern(bool),
NumberPattern(f64),
StringPattern(&'static str),
InterpolatedPattern(Vec<Spanned<StringPart>>, StringMatcher),
KeywordPattern(&'static str),
WordPattern(&'static str),
AsPattern(&'static str, &'static str),
Splattern(Box<Spanned<Self>>),
PlaceholderPattern,
TuplePattern(Vec<Spanned<Self>>),
ListPattern(Vec<Spanned<Self>>),
PairPattern(&'static str, Box<Spanned<Self>>),
DictPattern(Vec<Spanned<Self>>),
}
impl Ast {
pub fn show(&self) -> String {
use Ast::*;
match self {
And => "and".to_string(),
Or => "or".to_string(),
Error => unreachable!(),
Nil | NilPattern => "nil".to_string(),
String(s) | StringPattern(s) => format!("\"{s}\""),
Interpolated(strs) | InterpolatedPattern(strs, _) => {
let mut out = "".to_string();
out = format!("\"{out}");
for (part, _) in strs {
out = format!("{out}{part}");
}
format!("{out}\"")
}
Boolean(b) | BooleanPattern(b) => b.to_string(),
Number(n) | NumberPattern(n) => n.to_string(),
Keyword(k) | KeywordPattern(k) => format!(":{k}"),
Word(w) | WordPattern(w) => w.to_string(),
Block(lines) => {
let mut out = "{\n".to_string();
for (line, _) in lines {
out = format!("{out}\n {}", line.show());
}
format!("{out}\n}}")
}
If(cond, then, r#else) => format!(
"if {}\n then {}\n else {}",
cond.0.show(),
then.0.show(),
r#else.0.show()
),
Let(pattern, expression) => {
format!("let {} = {}", pattern.0.show(), expression.0.show())
}
Dict(entries) | DictPattern(entries) => {
format!(
"#{{{}}}",
entries
.iter()
.map(|(pair, _)| pair.show())
.collect::<Vec<_>>()
.join(", ")
)
}
List(members) | ListPattern(members) => format!(
"[{}]",
members
.iter()
.map(|(member, _)| member.show())
.collect::<Vec<_>>()
.join(", ")
),
Arguments(members) => format!(
"({})",
members
.iter()
.map(|(member, _)| member.show())
.collect::<Vec<_>>()
.join(", ")
),
Tuple(members) | TuplePattern(members) => format!(
"({})",
members
.iter()
.map(|(member, _)| member.show())
.collect::<Vec<_>>()
.join(", ")
),
Synthetic(root, first, rest) => format!(
"{} {} {}",
root.0.show(),
first.0.show(),
rest.iter()
.map(|(term, _)| term.show())
.collect::<Vec<_>>()
.join(" ")
),
When(clauses) => format!(
"when {{\n {}\n}}",
clauses
.iter()
.map(|(clause, _)| clause.show())
.collect::<Vec<_>>()
.join("\n ")
),
Placeholder | PlaceholderPattern => "_".to_string(),
LBox(name, rhs) => format!("box {name} = {}", rhs.0.show()),
Match(scrutinee, clauses) => format!(
"match {} with {{\n {}\n}}",
scrutinee.0.show(),
clauses
.iter()
.map(|(clause, _)| clause.show())
.collect::<Vec<_>>()
.join("\n ")
),
FnBody(clauses) => clauses
.iter()
.map(|(clause, _)| clause.show())
.collect::<Vec<_>>()
.join("\n "),
Fn(name, body, doc) => {
let mut out = format!("fn {name} {{\n");
if let Some(doc) = doc {
out = format!("{out} {doc}\n");
}
format!("{out} {}\n}}", body.0.show())
}
FnDeclaration(name) => format!("fn {name}"),
Panic(expr) => format!("panic! {}", expr.0.show()),
Do(terms) => {
format!(
"do {}",
terms
.iter()
.map(|(term, _)| term.show())
.collect::<Vec<_>>()
.join(" > ")
)
}
Repeat(times, body) => format!("repeat {} {{\n{}\n}}", times.0.show(), body.0.show()),
Splat(word) => format!("...{}", word),
Splattern(pattern) => format!("...{}", pattern.0.show()),
AsPattern(word, type_keyword) => format!("{word} as :{type_keyword}"),
Pair(key, value) | PairPattern(key, value) => format!(":{key} {}", value.0.show()),
Loop(init, body) => format!(
"loop {} with {{\n {}\n}}",
init.0.show(),
body.iter()
.map(|(clause, _)| clause.show())
.collect::<Vec<_>>()
.join("\n ")
),
Recur(args) => format!(
"recur ({})",
args.iter()
.map(|(arg, _)| arg.show())
.collect::<Vec<_>>()
.join(", ")
),
MatchClause(pattern, guard, body) => {
let mut out = pattern.0.show();
if let Some(guard) = guard.as_ref() {
out = format!("{out} if {}", guard.0.show());
}
format!("{out} -> {}", body.0.show())
}
WhenClause(cond, body) => format!("{} -> {}", cond.0.show(), body.0.show()),
}
}
}
impl fmt::Display for Ast {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Ast::*;
match self {
And => write!(f, "And"),
Or => write!(f, "Or"),
Error => write!(f, "Error"),
Nil => write!(f, "nil"),
String(s) => write!(f, "String: \"{}\"", s),
Interpolated(strs) => {
write!(
f,
"Interpolated: \"{}\"",
strs.iter()
.map(|(s, _)| s.to_string())
.collect::<Vec<_>>()
.join("")
)
}
Boolean(b) => write!(f, "Boolean: {}", b),
Number(n) => write!(f, "Number: {}", n),
Keyword(k) => write!(f, "Keyword: :{}", k),
Word(w) => write!(f, "Word: {}", w),
Block(b) => write!(
f,
"Block: <{}>",
b.iter()
.map(|(line, _)| line.to_string())
.collect::<Vec<_>>()
.join("\n")
),
If(cond, then_branch, else_branch) => write!(
f,
"If: {} Then: {} Else: {}",
cond.0, then_branch.0, else_branch.0
),
Let(pattern, expression) => {
write!(f, "Let: {} = {}", pattern.0, expression.0)
}
Dict(entries) => write!(
f,
"#{{{}}}",
entries
.iter()
.map(|pair| pair.0.to_string())
.collect::<Vec<_>>()
.join(", ")
),
List(l) => write!(
f,
"List: [{}]",
l.iter()
.map(|(line, _)| line.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Arguments(a) => write!(
f,
"Arguments: ({})",
a.iter()
.map(|(line, _)| line.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Tuple(t) => write!(
f,
"Tuple: ({})",
t.iter()
.map(|(line, _)| line.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Synthetic(root, first, rest) => write!(
f,
"Synth: [{}, {}, {}]",
root.0,
first.0,
rest.iter()
.map(|(term, _)| term.to_string())
.collect::<Vec<_>>()
.join("\n")
),
When(clauses) => write!(
f,
"When: [{}]",
clauses
.iter()
.map(|clause| clause.0.to_string())
.collect::<Vec<_>>()
.join("\n")
),
Placeholder => write!(f, "Placeholder"),
LBox(_name, _rhs) => todo!(),
Match(value, clauses) => {
write!(
f,
"match: {} with {}",
&value.0.to_string(),
clauses
.iter()
.map(|clause| clause.0.to_string())
.collect::<Vec<_>>()
.join("\n")
)
}
FnBody(clauses) => {
write!(
f,
"{}",
clauses
.iter()
.map(|clause| clause.0.to_string())
.collect::<Vec<_>>()
.join("\n")
)
}
Fn(name, body, ..) => {
write!(f, "fn: {name}\n{}", body.0)
}
FnDeclaration(_name) => todo!(),
Panic(_expr) => todo!(),
Do(terms) => {
write!(
f,
"do: {}",
terms
.iter()
.map(|(term, _)| term.to_string())
.collect::<Vec<_>>()
.join(" > ")
)
}
Repeat(_times, _body) => todo!(),
Splat(word) => {
write!(f, "splat: {}", word)
}
Pair(k, v) => {
write!(f, "pair: {} {}", k, v.0)
}
Loop(init, body) => {
write!(
f,
"loop: {} with {}",
init.0,
body.iter()
.map(|clause| clause.0.to_string())
.collect::<Vec<_>>()
.join("\n")
)
}
Recur(args) => {
write!(
f,
"recur: {}",
args.iter()
.map(|(arg, _)| arg.to_string())
.collect::<Vec<_>>()
.join(", ")
)
}
MatchClause(pattern, guard, body) => {
write!(
f,
"match clause: {} if {:?} -> {}",
pattern.0, guard, body.0
)
}
WhenClause(cond, body) => {
write!(f, "when clause: {} -> {}", cond.0, body.0)
}
NilPattern => write!(f, "nil"),
BooleanPattern(b) => write!(f, "{}", b),
NumberPattern(n) => write!(f, "{}", n),
StringPattern(s) => write!(f, "{}", s),
KeywordPattern(k) => write!(f, ":{}", k),
WordPattern(w) => write!(f, "{}", w),
AsPattern(w, t) => write!(f, "{} as :{}", w, t),
Splattern(p) => write!(f, "...{}", p.0),
PlaceholderPattern => write!(f, "_"),
TuplePattern(t) => write!(
f,
"({})",
t.iter()
.map(|x| x.0.to_string())
.collect::<Vec<_>>()
.join(", ")
),
ListPattern(l) => write!(
f,
"({})",
l.iter()
.map(|x| x.0.to_string())
.collect::<Vec<_>>()
.join(", ")
),
DictPattern(entries) => write!(
f,
"#{{{}}}",
entries
.iter()
.map(|(pair, _)| pair.to_string())
.collect::<Vec<_>>()
.join(", ")
),
PairPattern(key, value) => write!(f, ":{} {}", key, value.0),
InterpolatedPattern(strprts, _) => write!(
f,
"interpolated: \"{}\"",
strprts
.iter()
.map(|part| part.0.to_string())
.collect::<Vec<_>>()
.join("")
),
}
}
}
pub struct StringMatcher(pub Box<dyn Fn(String) -> Option<Vec<(String, String)>>>);
impl PartialEq for StringMatcher {
fn eq(&self, _other: &StringMatcher) -> bool {
@ -122,6 +576,54 @@ fn parse_string(s: &'static str, span: SimpleSpan) -> Result<Vec<Spanned<StringP
Ok(parts)
}
pub fn compile_string_pattern(parts: Vec<Spanned<StringPart>>) -> StringMatcher {
StringMatcher(Box::new(move |scrutinee| {
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
@ -167,7 +669,10 @@ where
match parsed {
Ok(parts) => match parts[0] {
(StringPart::Inline(_), _) => Ok((StringPattern(s), e.span())),
_ => Ok((InterpolatedPattern(parts.clone()), e.span())),
_ => Ok((
InterpolatedPattern(parts.clone(), compile_string_pattern(parts)),
e.span(),
)),
},
Err(msg) => Err(Rich::custom(e.span(), msg)),
}
@ -377,7 +882,7 @@ where
|span| (Error, span),
)));
let r#if = just(Token::Reserved("if"))
let if_ = just(Token::Reserved("if"))
.ignore_then(simple.clone())
.then_ignore(terminators.clone().or_not())
.then_ignore(just(Token::Reserved("then")))
@ -443,7 +948,7 @@ where
.then(
match_clause
.clone()
.or(guarded_clause.clone())
.or(guarded_clause)
.separated_by(terminators.clone())
.allow_leading()
.allow_trailing()
@ -452,20 +957,7 @@ where
)
.map_with(|(expr, clauses), e| (Match(Box::new(expr), clauses), e.span()));
let receive = just(Token::Reserved("receive"))
.ignore_then(
match_clause
.clone()
.or(guarded_clause)
.separated_by(terminators.clone())
.allow_leading()
.allow_trailing()
.collect()
.delimited_by(just(Token::Punctuation("{")), just(Token::Punctuation("}"))),
)
.map_with(|clauses, e| (Receive(clauses), e.span()));
let conditional = when.or(r#if).or(r#match).or(receive);
let conditional = when.or(if_).or(r#match);
let panic = just(Token::Reserved("panic!"))
.ignore_then(nonbinding.clone())

View File

@ -4,7 +4,7 @@
// * [ ] ensure loops have fixed arity (no splats)
// * [ ] ensure fn pattern splats are always highest (and same) arity
use crate::ast::{Ast, StringPart};
use crate::parser::*;
use crate::spans::{Span, Spanned};
use crate::value::Value;
use std::collections::{HashMap, HashSet};

View File

@ -1,7 +1,7 @@
use crate::ast::Ast;
use crate::base::BaseFn;
use crate::chunk::Chunk;
use crate::op::Op;
use crate::parser::Ast;
use crate::spans::Spanned;
use crate::value::{LFn, Value};
use crate::world::Zoo;