parse docstrings

This commit is contained in:
Scott Richmond 2024-11-21 22:36:57 -05:00
parent a35d5293a9
commit ad76f41b52
3 changed files with 34 additions and 20 deletions

View File

@ -30,7 +30,7 @@
// * [x] `loop` and `recur` // * [x] `loop` and `recur`
// * [ ] string patterns // * [ ] string patterns
// * [ ] string interpolation // * [ ] string interpolation
// * [ ] docstrings // * [x] docstrings
// * [~] write `base` in Rust // * [~] write `base` in Rust
// * [ ] turn this into a library function // * [ ] turn this into a library function
// * [ ] compile this into WASM // * [ ] compile this into WASM
@ -58,7 +58,12 @@ use crate::base::*;
pub fn main() { pub fn main() {
let src = " let src = "
true fn foo {
\"this is a docstring\"
() -> :foo
(_) -> :bar
}
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

@ -54,7 +54,7 @@ pub enum Ast<'src> {
Synthetic(Box<Spanned<Self>>, Box<Spanned<Self>>, Vec<Spanned<Self>>), Synthetic(Box<Spanned<Self>>, Box<Spanned<Self>>, Vec<Spanned<Self>>),
When(Vec<Spanned<WhenClause<'src>>>), When(Vec<Spanned<WhenClause<'src>>>),
Match(Box<Spanned<Self>>, Vec<MatchClause<'src>>), Match(Box<Spanned<Self>>, Vec<MatchClause<'src>>),
Fn(&'src str, Vec<MatchClause<'src>>), Fn(&'src str, Vec<MatchClause<'src>>, Option<&'src str>),
FnDeclaration(&'src str), FnDeclaration(&'src str),
Panic(Box<Spanned<Self>>), Panic(Box<Spanned<Self>>),
Do(Vec<Spanned<Self>>), Do(Vec<Spanned<Self>>),
@ -149,7 +149,7 @@ impl fmt::Display for Ast<'_> {
.join("\n") .join("\n")
) )
} }
Ast::Fn(name, clauses) => { Ast::Fn(name, clauses, _) => {
write!( write!(
f, f,
"fn: {}\n{}", "fn: {}\n{}",
@ -407,10 +407,11 @@ where
Token::Nil => Ast::Nil, Token::Nil => Ast::Nil,
Token::Boolean(b) => Ast::Boolean(b), Token::Boolean(b) => Ast::Boolean(b),
Token::Number(n) => Ast::Number(n), Token::Number(n) => Ast::Number(n),
Token::String(s) => Ast::String(s),
} }
.map_with(|v, e| (v, e.span())); .map_with(|v, e| (v, e.span()));
let string = select! {Token::String(s) => Ast::String(s)}.map_with(|s, e| (s, e.span()));
let tuple = simple let tuple = simple
.clone() .clone()
.separated_by(separators.clone()) .separated_by(separators.clone())
@ -505,6 +506,7 @@ where
.or(tuple.clone()) .or(tuple.clone())
.or(list) .or(list)
.or(dict) .or(dict)
.or(string)
.labelled("simple expression"), .labelled("simple expression"),
); );
@ -599,12 +601,6 @@ where
let conditional = when.or(if_).or(match_); let conditional = when.or(if_).or(match_);
//todo:
// * [x] do
// * [ ] loop
// * [x] repeat
// * [x] panic!
let panic = just(Token::Reserved("panic!")) let panic = just(Token::Reserved("panic!"))
.ignore_then(nonbinding.clone()) .ignore_then(nonbinding.clone())
.map_with(|expr, e| (Ast::Panic(Box::new(expr)), e.span())); .map_with(|expr, e| (Ast::Panic(Box::new(expr)), e.span()));
@ -653,14 +649,17 @@ where
let lambda = just(Token::Reserved("fn")) let lambda = just(Token::Reserved("fn"))
.ignore_then(fn_unguarded.clone()) .ignore_then(fn_unguarded.clone())
.map_with(|clause, e| (Ast::Fn("anonymous", vec![clause]), e.span())); .map_with(|clause, e| (Ast::Fn("anonymous", vec![clause], None), e.span()));
let fn_multiclause = fn_clause let fn_clauses = fn_clause
.clone() .clone()
.separated_by(terminators.clone()) .separated_by(terminators.clone())
.allow_leading() .allow_leading()
.allow_trailing() .allow_trailing()
.collect() .collect();
let loop_multiclause = fn_clauses
.clone()
.delimited_by(just(Token::Punctuation("{")), just(Token::Punctuation("}"))); .delimited_by(just(Token::Punctuation("{")), just(Token::Punctuation("}")));
let fn_single_clause = fn_clause.clone().map_with(|c, _| vec![c]); let fn_single_clause = fn_clause.clone().map_with(|c, _| vec![c]);
@ -668,7 +667,7 @@ where
let r#loop = just(Token::Reserved("loop")) let r#loop = just(Token::Reserved("loop"))
.ignore_then(tuple.clone()) .ignore_then(tuple.clone())
.then_ignore(just(Token::Reserved("with"))) .then_ignore(just(Token::Reserved("with")))
.then(fn_multiclause.clone().or(fn_single_clause.clone())) .then(loop_multiclause.clone().or(fn_single_clause.clone()))
.map_with(|(init, body), e| (Ast::Loop(Box::new(init), body), e.span())); .map_with(|(init, body), e| (Ast::Loop(Box::new(init), body), e.span()));
nonbinding.define( nonbinding.define(
@ -725,19 +724,29 @@ where
} else { } else {
unreachable!() unreachable!()
}; };
(Ast::Fn(name, vec![clause]), e.span()) (Ast::Fn(name, vec![clause], None), e.span())
}); });
let docstr = select! {Token::String(s) => s};
let fn_multiclause = separators
.clone()
.or_not()
.ignore_then(docstr.or_not())
.then(fn_clauses.clone())
.delimited_by(just(Token::Punctuation("{")), just(Token::Punctuation("}")))
.map_with(|(docstr, clauses), e| (docstr, clauses, e.span()));
let fn_compound = just(Token::Reserved("fn")) let fn_compound = just(Token::Reserved("fn"))
.ignore_then(word) .ignore_then(word)
.then(fn_multiclause.clone()) .then(fn_multiclause)
.map_with(|(word, clauses), e| { .map_with(|(word, (docstr, clauses, _)), e| {
let name = if let Ast::Word(word) = word.0 { let name = if let Ast::Word(word) = word.0 {
word word
} else { } else {
unreachable!() unreachable!()
}; };
(Ast::Fn(name, clauses), e.span()) (Ast::Fn(name, clauses, docstr), e.span())
}); });
let fn_ = fn_named.or(fn_compound).or(fn_decl); let fn_ = fn_named.or(fn_compound).or(fn_decl);

View File

@ -414,7 +414,7 @@ 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, ..) => {
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,