Files
bang/src/syntax/parse.rs
2025-10-11 14:46:02 -07:00

1330 lines
46 KiB
Rust

use crate::syntax::error::ParserError;
use crate::syntax::tokens::{Lexer, LocatedToken, Token};
use crate::syntax::*;
use internment::ArcIntern;
use std::collections::HashMap;
use std::ops::Range;
use std::path::{Path, PathBuf};
pub struct Parser<'lexer> {
file: ArcIntern<PathBuf>,
lexer: Lexer<'lexer>,
known_tokens: Vec<LocatedToken>,
prefix_precedence_table: HashMap<String, u8>,
infix_precedence_table: HashMap<String, (u8, u8)>,
postfix_precedence_table: HashMap<String, u8>,
}
pub enum Associativity {
Left,
Right,
None,
}
impl<'lexer> Parser<'lexer> {
/// Create a new parser from the given file index and lexer.
///
/// The file index will be used for annotating locations and for
/// error messages. If you don't care about either, you can use
/// 0 with no loss of functionality. (Obviously, it will be harder
/// to create quality error messages, but you already knew that.)
pub fn new<P: AsRef<Path>>(file: P, lexer: Lexer<'lexer>) -> Parser<'lexer> {
Parser {
file: ArcIntern::new(file.as_ref().to_path_buf()),
lexer,
known_tokens: vec![],
prefix_precedence_table: HashMap::new(),
infix_precedence_table: HashMap::new(),
postfix_precedence_table: HashMap::new(),
}
}
/// Add the given operator to our precedence table, at the given
/// precedence level and associativity.
pub fn add_infix_precedence<S: ToString>(
&mut self,
operator: S,
associativity: Associativity,
level: u8,
) {
let actual_associativity = match associativity {
Associativity::Left => (level * 2, (level * 2) + 1),
Associativity::Right => ((level * 2) + 1, level * 2),
Associativity::None => (level * 2, level * 2),
};
self.infix_precedence_table
.insert(operator.to_string(), actual_associativity);
}
pub fn add_prefix_precedence<S: ToString>(&mut self, operator: S, level: u8) {
self.prefix_precedence_table
.insert(operator.to_string(), level * 2);
}
pub fn add_postfix_precedence<S: ToString>(&mut self, operator: S, level: u8) {
self.postfix_precedence_table
.insert(operator.to_string(), level * 2);
}
fn get_precedence(&self, name: &String) -> (u8, u8) {
match self.infix_precedence_table.get(name) {
None => (19, 20),
Some(x) => *x,
}
}
/// Get the next token.
pub fn next(&mut self) -> Result<Option<LocatedToken>, ParserError> {
let result = self.known_tokens.pop();
if result.is_some() {
Ok(result)
} else {
self.lexer
.next()
.transpose()
.map_err(|error| ParserError::LexerError {
file: self.file.clone(),
error,
})
}
}
fn save(&mut self, token: LocatedToken) {
self.known_tokens.push(token)
}
fn bad_eof(&mut self, place: &'static str) -> ParserError {
ParserError::UnacceptableEof {
file: self.file.clone(),
place,
}
}
fn to_location(&self, span: Range<usize>) -> Location {
Location::new(&self.file, span)
}
pub fn parse_module(&mut self) -> Result<Module, ParserError> {
let mut definitions = vec![];
loop {
let next_token = self.next()?;
if next_token.is_none() {
return Ok(Module { definitions });
}
definitions.push(self.parse_definition()?);
}
}
pub fn parse_definition(&mut self) -> Result<Definition, ParserError> {
let (export, start) = self.parse_export_class()?;
let type_restrictions = self.parse_type_restrictions()?;
let definition = self.parse_def()?;
let location = definition.location().merge_span(start);
Ok(Definition {
location,
export,
type_restrictions,
definition,
})
}
fn parse_export_class(&mut self) -> Result<(ExportClass, Range<usize>), ParserError> {
let maybe_export = self
.next()?
.ok_or_else(|| self.bad_eof("looking for possible export"))?;
if matches!(maybe_export.token, Token::ValueName(ref x) if x == "export") {
Ok((ExportClass::Public, maybe_export.span))
} else {
let start = maybe_export.span.clone();
self.save(maybe_export);
Ok((ExportClass::Private, start))
}
}
pub fn parse_type_restrictions(&mut self) -> Result<TypeRestrictions, ParserError> {
let Some(maybe_restrict) = self.next()? else {
return Ok(TypeRestrictions::empty());
};
if !matches!(maybe_restrict.token, Token::ValueName(ref x) if x == "restrict") {
self.save(maybe_restrict);
return Ok(TypeRestrictions::empty());
}
let maybe_paren = self
.next()?
.ok_or_else(|| self.bad_eof("Looking for open paren after restrict"))?;
if !matches!(maybe_paren.token, Token::OpenParen) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: maybe_paren.span,
token: maybe_paren.token,
expected: "open parenthesis, following the restrict keyword",
});
}
let mut restrictions = vec![];
while let Some(type_restriction) = self.parse_type_restriction()? {
restrictions.push(type_restriction);
}
let maybe_paren = self
.next()?
.ok_or_else(|| self.bad_eof("Looking for open paren after restrict"))?;
if !matches!(maybe_paren.token, Token::CloseParen) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: maybe_paren.span,
token: maybe_paren.token,
expected: "close parenthesis following type restrictions",
});
}
Ok(TypeRestrictions { restrictions })
}
fn parse_type_restriction(&mut self) -> Result<Option<TypeRestriction>, ParserError> {
let maybe_constructor = self
.next()?
.ok_or_else(|| self.bad_eof("Looking for constructor for type restriction"))?;
let constructor = match maybe_constructor.token {
Token::TypeName(str) => {
let name = Name::new(self.to_location(maybe_constructor.span.clone()), str);
Type::Constructor(self.to_location(maybe_constructor.span), name)
}
Token::PrimitiveTypeName(str) => {
let name = Name::new(self.to_location(maybe_constructor.span.clone()), str);
Type::Primitive(self.to_location(maybe_constructor.span), name)
}
token @ Token::CloseParen | token @ Token::Comma => {
self.save(LocatedToken {
token,
span: maybe_constructor.span,
});
return Ok(None);
}
weird => {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: maybe_constructor.span,
token: weird,
expected: "Constructor name, comma, or close parenthesis in type restriction",
});
}
};
let mut arguments = vec![];
while let Ok(t) = self.parse_base_type() {
arguments.push(t);
}
let restriction = TypeRestriction {
constructor,
arguments,
};
let Some(maybe_comma) = self.next()? else {
return Ok(Some(restriction));
};
match maybe_comma.token {
Token::Comma => {}
_ => self.save(maybe_comma),
}
Ok(Some(restriction))
}
fn parse_def(&mut self) -> Result<Def, ParserError> {
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for definition body"))?;
if let Ok(structure) = self.parse_structure() {
return Ok(Def::Structure(structure));
}
if let Ok(enumeration) = self.parse_enumeration() {
return Ok(Def::Enumeration(enumeration));
}
if let Ok(fun_or_val) = self.parse_function_or_value() {
return Ok(fun_or_val);
}
Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "'structure', 'enumeration', or a value identifier",
})
}
pub fn parse_structure(&mut self) -> Result<StructureDef, ParserError> {
let structure_token = self
.next()?
.ok_or_else(|| self.bad_eof("looking for definition"))?;
if !matches!(structure_token.token, Token::ValueName(ref s) if s == "structure") {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: structure_token.span,
token: structure_token.token,
expected: "the 'structure' keyword",
});
}
let name = self
.next()?
.ok_or_else(|| self.bad_eof("looking for structure name"))?;
let structure_name = match name.token {
Token::TypeName(str) => Name::new(self.to_location(name.span), str),
_ => {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: name.span,
token: name.token,
expected: "a structure name",
});
}
};
let brace = self
.next()?
.ok_or_else(|| self.bad_eof("the open brace after a structure name"))?;
if !matches!(brace.token, Token::OpenBrace) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: brace.span,
token: brace.token,
expected: "the brace after a structure name",
});
}
let mut fields = vec![];
while let Some(field_definition) = self.parse_field_definition()? {
fields.push(field_definition);
}
let brace = self.next()?.ok_or_else(|| {
self.bad_eof("the close brace after at the end of a structure definition")
})?;
if !matches!(brace.token, Token::CloseBrace) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: brace.span,
token: brace.token,
expected: "the brace at the end of a structure definition",
});
}
let location = self
.to_location(structure_token.span)
.extend_to(&self.to_location(brace.span));
Ok(StructureDef {
name: structure_name,
location,
fields,
})
}
pub fn parse_field_value(&mut self) -> Result<Option<FieldValue>, ParserError> {
let maybe_name = self
.next()?
.ok_or_else(|| self.bad_eof("parsing field definition"))?;
let field = match maybe_name.token {
Token::ValueName(x) => Name::new(self.to_location(maybe_name.span), x),
_ => {
self.save(maybe_name.clone());
return Ok(None);
}
};
let maybe_colon = self.next()?.ok_or_else(|| {
self.bad_eof("looking for colon, comma, or close brace after field name")
})?;
if !matches!(maybe_colon.token, Token::Colon) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: maybe_colon.span,
token: maybe_colon.token,
expected: "colon after field name in constructor",
});
}
let value = self.parse_expression()?;
let end_token = self.next()?.ok_or_else(|| {
self.bad_eof("looking for comma or close brace after field definition")
})?;
if !matches!(end_token.token, Token::Comma) {
self.save(end_token);
}
Ok(Some(FieldValue { field, value }))
}
pub fn parse_field_definition(&mut self) -> Result<Option<StructureField>, ParserError> {
let (export, start) = self.parse_export_class()?;
let maybe_name = self
.next()?
.ok_or_else(|| self.bad_eof("parsing field definition"))?;
let name = match maybe_name.token {
Token::ValueName(x) => Name::new(self.to_location(maybe_name.span.clone()), x),
_ => {
self.save(maybe_name.clone());
if matches!(export, ExportClass::Private) {
return Ok(None);
} else {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: maybe_name.span,
token: maybe_name.token,
expected: "a field name",
});
}
}
};
let start_location = self.to_location(start);
let maybe_colon = self.next()?.ok_or_else(|| {
self.bad_eof("looking for colon, comma, or close brace after field name")
})?;
let field_type = match maybe_colon.token {
Token::Comma | Token::CloseBrace => {
self.save(maybe_colon);
None
}
Token::Colon => Some(self.parse_type()?),
_ => {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: maybe_colon.span,
token: maybe_colon.token,
expected: "colon, comma, or close brace after field name",
});
}
};
let end_token = self.next()?.ok_or_else(|| {
self.bad_eof("looking for comma or close brace after field definition")
})?;
let maybe_end_location = match end_token.token {
Token::Comma => Some(self.to_location(end_token.span)),
Token::CloseBrace => {
self.save(end_token);
None
}
_ => {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: end_token.span,
token: end_token.token,
expected: "looking for comma or close brace after field definition",
});
}
};
let end_location = maybe_end_location
.or_else(|| field_type.as_ref().map(|x| x.location()))
.unwrap_or_else(|| self.to_location(maybe_name.span));
let location = start_location.extend_to(&end_location);
Ok(Some(StructureField {
location,
export,
name,
field_type,
}))
}
pub fn parse_enumeration(&mut self) -> Result<EnumerationDef, ParserError> {
let enumeration_token = self
.next()?
.ok_or_else(|| self.bad_eof("looking for definition"))?;
if !matches!(enumeration_token.token, Token::ValueName(ref e) if e == "enumeration") {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: enumeration_token.span,
token: enumeration_token.token,
expected: "the 'enumeration' keyword",
});
}
let name = self
.next()?
.ok_or_else(|| self.bad_eof("looking for enumeration name"))?;
let enumeration_name = match name.token {
Token::TypeName(str) => Name::new(self.to_location(name.span), str),
_ => {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: name.span,
token: name.token,
expected: "an enumeration name",
});
}
};
let brace = self
.next()?
.ok_or_else(|| self.bad_eof("the open brace after an enumeration name"))?;
if !matches!(brace.token, Token::OpenBrace) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: brace.span,
token: brace.token,
expected: "the brace after an enumeration name",
});
}
let mut variants = vec![];
while let Some(variant_definition) = self.parse_enum_variant()? {
variants.push(variant_definition);
}
let brace = self.next()?.ok_or_else(|| {
self.bad_eof("the close brace after at the end of an enumeration definition")
})?;
if !matches!(brace.token, Token::CloseBrace) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: brace.span,
token: brace.token,
expected: "the brace at the end of an enumeration definition",
});
}
let location = self
.to_location(enumeration_token.span)
.extend_to(&self.to_location(brace.span));
Ok(EnumerationDef {
name: enumeration_name,
location,
variants,
})
}
pub fn parse_enum_variant(&mut self) -> Result<Option<EnumerationVariant>, ParserError> {
let maybe_name = self
.next()?
.ok_or_else(|| self.bad_eof("looking for enumeration name"))?;
let name = match maybe_name.token {
Token::TypeName(x) => Name::new(self.to_location(maybe_name.span.clone()), x),
Token::CloseBrace => {
self.save(maybe_name);
return Ok(None);
}
_ => {
self.save(maybe_name.clone());
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: maybe_name.span,
token: maybe_name.token,
expected: "variant name (identifier starting with a capital)",
});
}
};
let start_location = self.to_location(maybe_name.span);
let maybe_paren = self
.next()?
.ok_or_else(|| self.bad_eof("trying to understand enumeration variant"))?;
let (argument, arg_location) = if matches!(maybe_paren.token, Token::OpenParen) {
let t = self.parse_type()?;
let maybe_close = self
.next()?
.ok_or_else(|| self.bad_eof("trying to parse a enumeration variant's type"))?;
if !matches!(maybe_close.token, Token::CloseParen) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: maybe_close.span,
token: maybe_close.token,
expected: "close paren to end an enumeration variant's type argument",
});
}
let location = t.location();
(Some(t), location)
} else {
self.save(maybe_paren);
(None, start_location.clone())
};
let ender = self.next()?.ok_or_else(|| {
self.bad_eof("looking for comma or close brace after enumeration variant")
})?;
let end_location = match ender.token {
Token::Comma => self.to_location(ender.span),
Token::CloseBrace => {
self.save(ender);
arg_location
}
_ => {
self.save(ender.clone());
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: ender.span,
token: ender.token,
expected: "comma or close brace after enumeration variant",
});
}
};
let location = start_location.extend_to(&end_location);
Ok(Some(EnumerationVariant {
name,
location,
argument,
}))
}
fn parse_function_or_value(&mut self) -> Result<Def, ParserError> {
unimplemented!()
}
pub fn parse_expression(&mut self) -> Result<Expression, ParserError> {
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for an expression"))?;
self.save(next.clone());
match next.token {
Token::ValueName(x) if x == "match" => {
Ok(Expression::Match(self.parse_match_expression()?))
}
Token::ValueName(x) if x == "if" => {
Ok(Expression::Conditional(self.parse_if_expression()?))
}
_ => self.parse_arithmetic(0),
}
}
fn parse_match_expression(&mut self) -> Result<MatchExpr, ParserError> {
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for a 'match' to open a pattern match"))?;
if !matches!(next.token, Token::ValueName(ref x) if x == "match") {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "an 'match' to start a pattern match",
});
}
let start = self.to_location(next.span);
let value = Box::new(self.parse_arithmetic(0)?);
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for an open brace after 'match'"))?;
if !matches!(next.token, Token::OpenBrace) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "an open brace after the match expression",
});
}
let mut cases = vec![];
while let Some(case) = self.parse_match_case()? {
cases.push(case);
}
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for an open brace after 'match'"))?;
if !matches!(next.token, Token::CloseBrace) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "a close brace to end a match expression",
});
}
let end = self.to_location(next.span);
let location = start.extend_to(&end);
Ok(MatchExpr {
location,
value,
cases,
})
}
fn parse_match_case(&mut self) -> Result<Option<MatchCase>, ParserError> {
unimplemented!()
}
fn parse_if_expression(&mut self) -> Result<ConditionalExpr, ParserError> {
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for an 'if' to start conditional"))?;
if !matches!(next.token, Token::ValueName(ref x) if x == "if") {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "an 'if' to start a conditional",
});
}
let start = self.to_location(next.span);
let test = self.parse_arithmetic(0)?;
let consequent = self.parse_block()?;
let maybe_else = self.next()?;
let (alternative, location) = match maybe_else {
Some(LocatedToken {
token: Token::ValueName(ref n),
..
}) if n == "else" => {
let expr = self.parse_block()?;
let location = match expr {
Expression::Block(ref l, _) => l.clone(),
_ => panic!("How did parse_block not return a block?!"),
};
(Some(Box::new(expr)), location)
}
_ => {
let location = match consequent {
Expression::Block(ref l, _) => l.clone(),
_ => panic!("How did parse_block not return a block?!"),
};
(None, location)
}
};
Ok(ConditionalExpr {
location: start.extend_to(&location),
test: Box::new(test),
consequent: Box::new(consequent),
alternative,
})
}
pub fn parse_block(&mut self) -> Result<Expression, ParserError> {
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for open brace to start block"))?;
if !matches!(next.token, Token::OpenBrace) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "an open brace to start a block",
});
}
let start = self.to_location(next.span);
let mut statements = vec![];
let mut ended_with_expr = false;
while let Some((stmt, terminal)) = self.parse_statement()? {
statements.push(stmt);
if terminal {
ended_with_expr = true;
break;
}
}
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for statement or block close"))?;
if !matches!(next.token, Token::CloseBrace) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "a close brace to end a block",
});
}
let end = self.to_location(next.span);
if !ended_with_expr {
let void_name = Name::new(end.clone(), "%prim%void");
let void_ref = Expression::Reference(void_name);
let void_call = Expression::Call(Box::new(void_ref), CallKind::Normal, vec![]);
statements.push(Statement::Expression(void_call));
}
Ok(Expression::Block(start.extend_to(&end), statements))
}
pub fn parse_statement(&mut self) -> Result<Option<(Statement, bool)>, ParserError> {
loop {
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for a statement or close brace"))?;
match next.token {
Token::CloseBrace => {
self.save(next);
return Ok(None);
}
Token::ValueName(ref l) if l == "let" => {
self.save(next);
return Ok(Some((Statement::Binding(self.parse_let()?), false)));
}
_ => {
self.save(next);
let expr = Statement::Expression(self.parse_expression()?);
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for semicolon or close brace"))?;
if matches!(next.token, Token::Semi) {
return Ok(Some((expr, false)));
} else {
self.save(next);
return Ok(Some((expr, true)));
}
}
}
}
}
pub fn parse_let(&mut self) -> Result<BindingStmt, ParserError> {
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for a let for a binding statement"))?;
if !matches!(next.token, Token::ValueName(ref n) if n == "let") {
self.save(next.clone());
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "a 'let' to open a binding statement",
});
}
let start = self.to_location(next.span);
let next = self
.next()?
.ok_or_else(|| self.bad_eof("'mut' or a variable name"))?;
let mutable = matches!(next.token, Token::ValueName(ref n) if n == "mut");
if !mutable {
self.save(next);
}
let next = self
.next()?
.ok_or_else(|| self.bad_eof("a variable name"))?;
let variable = match next.token {
Token::ValueName(v) => Name::new(self.to_location(next.span), v),
_ => {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "a variable name for the let binding",
});
}
};
let next = self
.next()?
.ok_or_else(|| self.bad_eof("an '=' after a variable name in a binding"))?;
if !matches!(next.token, Token::OperatorName(ref x) if x == "=") {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "an '=' after the variable name in a let binding",
});
}
let value = self.parse_expression()?;
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for terminal semicolon for let statement"))?;
if !matches!(next.token, Token::Semi) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "a semicolon to finish a let statement",
});
}
let end = self.to_location(next.span);
Ok(BindingStmt {
location: start.extend_to(&end),
mutable,
variable,
value,
})
}
pub fn parse_arithmetic(&mut self, level: u8) -> Result<Expression, ParserError> {
// start by checking for prefix operators.
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for arithmetic expression"))?;
let mut lhs = if let Token::OperatorName(ref n) = next.token {
if let Some(pre_prec) = self.prefix_precedence_table.get(n) {
if *pre_prec < level {
self.save(next.clone());
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "a base expression of a tighter-binding prefix operator",
});
}
let rhs = self.parse_arithmetic(*pre_prec)?;
let opname = Name::new(self.to_location(next.span), n);
let op_expr = Expression::Reference(opname);
Expression::Call(Box::new(op_expr), CallKind::Prefix, vec![rhs])
} else {
self.save(next);
self.parse_base_expression()?
}
} else {
self.save(next);
self.parse_base_expression()?
};
loop {
let Some(next) = self.next()? else {
return Ok(lhs);
};
match next.token {
Token::OpenParen => {
self.save(next);
let args = self.parse_call_arguments()?;
lhs = Expression::Call(Box::new(lhs), CallKind::Normal, args);
}
Token::OperatorName(ref n) => {
if let Some(postprec) = self.postfix_precedence_table.get(n) {
if *postprec < level {
self.save(next);
break;
}
let opname = Name::new(self.to_location(next.span), n);
let op_expr = Expression::Reference(opname);
lhs = Expression::Call(Box::new(op_expr), CallKind::Postfix, vec![lhs]);
continue;
}
let (left_pr, right_pr) = self.get_precedence(&n);
if left_pr < level {
self.save(next);
break;
}
let rhs = self.parse_arithmetic(right_pr)?;
let name = Name::new(self.to_location(next.span), n);
let opref = Box::new(Expression::Reference(name));
let args = vec![lhs, rhs];
lhs = Expression::Call(opref, CallKind::Infix, args);
}
_ => {
self.save(next);
return Ok(lhs);
}
}
}
Ok(lhs)
}
fn parse_call_arguments(&mut self) -> Result<Vec<Expression>, ParserError> {
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for open paren for function arguments"))?;
if !matches!(next.token, Token::OpenParen) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "open paren for call arguments",
});
}
let mut args = vec![];
loop {
let next = self.next()?.ok_or_else(|| {
self.bad_eof("looking for an expression or close paren in function arguments")
})?;
if matches!(next.token, Token::CloseParen) {
break;
}
self.save(next);
let argument = self.parse_arithmetic(0)?;
args.push(argument);
let next = self.next()?.ok_or_else(|| {
self.bad_eof("looking for comma or close paren in function arguments")
})?;
match next.token {
Token::Comma => continue,
Token::CloseParen => break,
_ => {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "comma or close paren in function arguments",
});
}
}
}
Ok(args)
}
pub fn parse_base_expression(&mut self) -> Result<Expression, ParserError> {
if let Ok(v) = self.parse_constant() {
return Ok(Expression::Value(v));
}
let next = self
.next()?
.ok_or_else(|| self.bad_eof("looking for an expression"))?;
match next.token {
Token::OpenBrace => {
self.save(next);
return self.parse_block();
}
Token::OpenParen => {
let inner = self.parse_expression()?;
let hopefully_close = self
.next()?
.ok_or_else(|| self.bad_eof("looking for close paren to finish expression"))?;
if matches!(hopefully_close.token, Token::CloseParen) {
Ok(inner)
} else {
self.save(hopefully_close.clone());
Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: hopefully_close.span,
token: hopefully_close.token,
expected: "close paren after expression",
})
}
}
Token::TypeName(n) | Token::PrimitiveTypeName(n) => {
let type_name = Name::new(self.to_location(next.span), n);
let after_type_name = self.next()?.ok_or_else(|| {
self.bad_eof("looking for colon, open brace, or open paren in constructor")
})?;
match after_type_name.token {
Token::OpenBrace => {
let mut fields = vec![];
while let Some(field) = self.parse_field_value()? {
fields.push(field);
}
let closer = self.next()?.ok_or_else(|| {
self.bad_eof("looking for close brace in structure value")
})?;
if !matches!(closer.token, Token::CloseBrace) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: closer.span,
token: closer.token,
expected: "close brace or comma after field value",
});
}
Ok(Expression::StructureValue(type_name, fields))
}
Token::Colon => {
let second_colon = self.next()?.ok_or_else(|| {
self.bad_eof("looking for second colon in enumeration value")
})?;
if !matches!(second_colon.token, Token::Colon) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: second_colon.span,
token: second_colon.token,
expected: "second colon in enumeration value",
});
}
let vname = self
.next()?
.ok_or_else(|| self.bad_eof("looking for enumeration value name"))?;
let value_name = match vname.token {
Token::TypeName(s) => {
let loc = self.to_location(vname.span);
Name::new(loc, s)
}
_ => {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: vname.span,
token: vname.token,
expected: "enumeration value name",
});
}
};
let arg = if let Some(maybe_paren) = self.next()? {
let expr = self.parse_expression()?;
let tok = self.next()?.ok_or_else(|| {
self.bad_eof("looking for close paren after enum value argument")
})?;
if !matches!(tok.token, Token::CloseParen) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: tok.span,
token: tok.token,
expected: "close paren after enum value argument",
});
}
Some(Box::new(expr))
} else {
None
};
Ok(Expression::EnumerationValue(type_name, value_name, arg))
}
_ => Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: after_type_name.span,
token: after_type_name.token,
expected: "colon, open brace, or open paren in constructor",
}),
}
}
Token::ValueName(n) | Token::PrimitiveValueName(n) => {
let location = self.to_location(next.span);
let name = Name::new(location, n);
Ok(Expression::Reference(name))
}
_ => {
self.save(next.clone());
Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: next.span,
token: next.token,
expected: "some base expression or an open brace",
})
}
}
}
pub fn parse_type(&mut self) -> Result<Type, ParserError> {
self.parse_function_type()
}
fn parse_function_type(&mut self) -> Result<Type, ParserError> {
let mut args = Vec::new();
while let Ok(t) = self.parse_type_application() {
args.push(t);
}
let Some(maybe_arrow) = self.next()? else {
match args.pop() {
None => {
return Err(ParserError::UnacceptableEof {
file: self.file.clone(),
place: "parsing function type or type",
});
}
Some(t) if args.len() == 0 => return Ok(t),
Some(_) => {
return Err(ParserError::UnacceptableEof {
file: self.file.clone(),
place: "looking for '->' in function type",
});
}
}
};
if maybe_arrow.token == Token::Arrow {
let right = self.parse_function_type()?;
Ok(Type::Function(args, Box::new(right)))
} else if args.len() == 1 {
self.save(maybe_arrow);
Ok(args.pop().expect("length = 1 works"))
} else {
self.save(maybe_arrow.clone());
let LocatedToken { token, span } = maybe_arrow;
Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span,
token,
expected: "'->' in function type",
})
}
}
fn parse_type_application(&mut self) -> Result<Type, ParserError> {
let LocatedToken { token, span } =
self.next()?.ok_or_else(|| self.bad_eof("parsing type"))?;
let constructor = match token {
Token::TypeName(x) => {
let name = Name::new(self.to_location(span.clone()), x);
Type::Constructor(self.to_location(span), name)
}
Token::PrimitiveTypeName(x) => {
let name = Name::new(self.to_location(span.clone()), x);
Type::Primitive(self.to_location(span), name)
}
_ => {
self.save(LocatedToken { token, span });
return self.parse_base_type();
}
};
let mut args = vec![];
while let Ok(next_arg) = self.parse_base_type() {
args.push(next_arg);
}
Ok(Type::Application(Box::new(constructor), args))
}
fn parse_base_type(&mut self) -> Result<Type, ParserError> {
let LocatedToken { token, span } =
self.next()?.ok_or_else(|| self.bad_eof("parsing type"))?;
match token {
Token::TypeName(x) => {
let name = Name::new(self.to_location(span.clone()), x);
Ok(Type::Constructor(self.to_location(span), name))
}
Token::PrimitiveTypeName(x) => {
let name = Name::new(self.to_location(span.clone()), x);
Ok(Type::Primitive(self.to_location(span), name))
}
Token::ValueName(x) => {
let name = Name::new(self.to_location(span.clone()), x);
Ok(Type::Variable(self.to_location(span), name))
}
Token::OpenParen => {
let t = self.parse_type()?;
let closer = self
.next()?
.ok_or_else(|| self.bad_eof("close paren in type"))?;
if !matches!(closer.token, Token::CloseParen) {
return Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: closer.span,
token: closer.token,
expected: "close parenthesis to finish a type",
});
}
Ok(t)
}
token => {
self.save(LocatedToken {
token: token.clone(),
span: span.clone(),
});
Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span,
token,
expected: "type constructor, type variable, or primitive type",
})
}
}
}
pub fn parse_constant(&mut self) -> Result<ConstantValue, ParserError> {
let maybe_constant = self
.next()?
.ok_or_else(|| self.bad_eof("looking for a constant"))?;
match maybe_constant.token {
Token::Integer(iwb) => Ok(ConstantValue::Integer(
self.to_location(maybe_constant.span),
iwb,
)),
Token::Character(c) => Ok(ConstantValue::Character(
self.to_location(maybe_constant.span),
c,
)),
Token::String(s) => Ok(ConstantValue::String(
self.to_location(maybe_constant.span),
s,
)),
_ => {
self.save(maybe_constant.clone());
Err(ParserError::UnexpectedToken {
file: self.file.clone(),
span: maybe_constant.span,
token: maybe_constant.token,
expected: "constant value",
})
}
}
}
}