use super::{PResult, Parser};
use crate::ast::{Arg, Expr, ExprKind, Generic, Name, Param, Pass, Variance};
use crate::source::token::{Punct, StrPart, Tok};
use crate::words::{r#in, r#let, r#move, r#mut, out, r#ref};
impl Parser<'_, '_, '_> {
pub(crate) fn name(&mut self, what: &str) -> PResult<Name> {
if let Tok::Ident(text) = self.tok() {
let span = self.bump().span;
return Ok(Name {
text: text.clone(),
span,
});
}
Err(self.expected(what))
}
pub(crate) fn any_name(&mut self, what: &str) -> PResult<Name> {
if let Tok::Keyword(w) = self.tok() {
let span = self.bump().span;
return Ok(Name {
text: w.text.to_string(),
span,
});
}
self.name(what)
}
pub(crate) fn path(&mut self, what: &str) -> PResult<Vec<Name>> {
let mut path = vec![self.any_name(what)?];
while self.at(Punct::Dot) && matches!(self.nth(1), Tok::Ident(_) | Tok::Keyword(_)) {
self.bump();
path.push(self.any_name(what)?);
}
Ok(path)
}
pub(crate) fn skip_docs(&mut self) {
while matches!(self.tok(), Tok::Doc(_)) {
self.bump();
}
}
pub(crate) fn params(&mut self) -> PResult<Vec<Param>> {
self.need(Punct::LParen)?;
self.braces(|p| p.list(Punct::RParen, Self::param))
}
pub(crate) fn index_params(&mut self) -> PResult<Vec<Param>> {
self.need(Punct::LBracket)?;
self.braces(|p| p.list(Punct::RBracket, Self::param))
}
fn param(&mut self) -> PResult<Param> {
let extension = self.eat_word(&crate::words::this::WORD);
let params = self.eat_word(&crate::words::params::WORD);
let pass = self.pass();
let lifetime = self.lifetime();
let ty = self.ty()?;
let name = self.name("a parameter's name")?;
let default = if self.eat(Punct::Eq) {
Some(self.expr()?)
} else {
None
};
Ok(Param {
extension,
params,
pass,
lifetime,
ty,
name,
default,
})
}
pub(crate) fn args(&mut self) -> PResult<Vec<Arg>> {
self.need(Punct::LParen)?;
self.braces(|p| p.list(Punct::RParen, Self::arg))
}
fn arg(&mut self) -> PResult<Arg> {
let named = matches!(self.tok(), Tok::Ident(_)) && self.nth_is(1, Punct::Colon);
let name = if named {
let name = self.name("an argument")?;
self.bump();
Some(name)
} else {
None
};
let pass = self.pass();
let value = if pass == Some(Pass::Out) {
self.declared()?
} else {
self.expr()?
};
Ok(Arg { name, pass, value })
}
pub(crate) fn lifetime(&mut self) -> Option<Name> {
let Tok::Lifetime(text) = self.tok() else {
return None;
};
let text = text.clone();
let span = self.bump().span;
Some(Name { text, span })
}
fn pass(&mut self) -> Option<Pass> {
let pass = [
(&r#in::WORD, Pass::In),
(&r#ref::WORD, Pass::Ref),
(&out::WORD, Pass::Out),
(&r#move::WORD, Pass::Move),
]
.into_iter()
.find(|(w, _)| self.at_word(w))?;
self.bump();
Some(pass.1)
}
fn declared(&mut self) -> PResult<Expr> {
let start = self.span();
let (ty, mutable) = if self.eat_word(&r#let::WORD) {
(None, self.eat_word(&r#mut::WORD))
} else if self.typed_ahead().is_some() {
(Some(self.ty()?), true)
} else {
return self.expr();
};
let name = self.name("a name")?;
Ok(Expr {
kind: ExprKind::Declare { ty, name, mutable },
span: start.to(self.prev()),
})
}
pub(crate) fn generics(&mut self) -> PResult<Vec<Generic>> {
if !self.eat(Punct::Lt) {
return Ok(Vec::new());
}
self.list(Punct::Gt, |p| {
if let Some(name) = p.lifetime() {
return Ok(Generic {
name,
variance: None,
lifetime: true,
});
}
let variance = if p.eat_word(&r#in::WORD) {
Some(Variance::In)
} else if p.eat_word(&out::WORD) {
Some(Variance::Out)
} else {
None
};
let name = p.name("a type parameter")?;
Ok(Generic {
name,
variance,
lifetime: false,
})
})
}
pub(crate) fn list<T>(
&mut self,
close: Punct,
mut item: impl FnMut(&mut Self) -> PResult<T>,
) -> PResult<Vec<T>> {
let mut out = Vec::new();
loop {
if self.eat(close) {
return Ok(out);
}
out.push(item(self)?);
if !self.eat(Punct::Comma) {
self.need(close)?;
return Ok(out);
}
}
}
pub(crate) fn plain_string(&mut self, what: &str) -> PResult<String> {
let Tok::Str(parts) = self.tok() else {
return Err(self.expected(what));
};
let span = self.bump().span;
let mut text = String::new();
for part in parts {
match part {
StrPart::Text(t) => text.push_str(t),
StrPart::Expr { .. } => {
let code = yog_ids::script_problem::EXPECTED;
return Err(self.error(code, span, format!("{what} can't have `{{…}}` in it")));
}
}
}
Ok(text)
}
pub(crate) fn version(&mut self) -> PResult<String> {
if matches!(self.tok(), Tok::Version(_) | Tok::Float(_) | Tok::Int(_)) {
let span = self.bump().span;
return Ok(self.report.text(span).to_string());
}
Err(self.expected("a version"))
}
pub(crate) fn int(&mut self, what: &str) -> PResult<i64> {
if let Tok::Int(v) = self.tok() {
self.bump();
return Ok(*v);
}
Err(self.expected(what))
}
}