Skip to content

Commit

Permalink
rustfmt
Browse files Browse the repository at this point in the history
  • Loading branch information
vcfxb committed Mar 23, 2024
1 parent a5d335a commit 3cd6231
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 144 deletions.
124 changes: 72 additions & 52 deletions wright/src/parser/ast.rs
Original file line number Diff line number Diff line change
@@ -1,142 +1,154 @@
//! Abstract syntax tree representation for Wright source code.
use codespan_reporting::files::Files;
use super::{
fragment::Fragment,
lexer::{
token::{Token, TokenTy},
Lexer,
},
};
use crate::filemap::{FileId, FileMap};
use super::{fragment::Fragment, lexer::{token::{Token, TokenTy}, Lexer}};
use codespan_reporting::files::Files;

pub mod identifier;
pub mod expression;
pub mod identifier;

/// The context needed to parse AST nodes and create errors when it doesn't work out.
/// The context needed to parse AST nodes and create errors when it doesn't work out.
pub struct AstGeneratorContext<'src> {
/// The ID of the file in [AstGeneratorContext::file_map] being parsed.
/// Useful for emitting parser errors.
/// The ID of the file in [AstGeneratorContext::file_map] being parsed.
/// Useful for emitting parser errors.
file_id: FileId,

/// Immutable reference to the [FileMap] containing the source file.
/// Useful for emitting parser errors.
/// Immutable reference to the [FileMap] containing the source file.
/// Useful for emitting parser errors.
file_map: &'src FileMap<'src>,

/// The full source code of the file being parsed.
/// The full source code of the file being parsed.
full_source: Fragment<'src>,

/// The lexer that's being operated on. Just about every parser should work by
/// pulling/peeking tokens from this lexer.
pub lexer: Lexer<'src>
/// The lexer that's being operated on. Just about every parser should work by
/// pulling/peeking tokens from this lexer.
pub lexer: Lexer<'src>,
}

/// Trait implemented by all AST node types.
/// Trait implemented by all AST node types.
pub trait AstNode<'src> {
/// The type of the error that should be returned if a node of this type cannot be parsed.
/// The type of the error that should be returned if a node of this type cannot be parsed.
type Error: 'src;

/// Get the associated fragment of source code. All [AstNode]s should have one of these.
/// Get the associated fragment of source code. All [AstNode]s should have one of these.
fn fragment(&self) -> Fragment<'src>;

/// Parse a node of this type from an [AstGeneratorContext], pulling tokens from it as necessary.
///
/// If parsing a node of this type is not possible, return an error with any necessary info.
/// Parse a node of this type from an [AstGeneratorContext], pulling tokens from it as necessary.
///
/// If parsing a node of this type is not possible, return an error with any necessary info.
fn try_parse(ctx: &mut AstGeneratorContext<'src>) -> Result<Self, Self::Error>
where Self: Sized;
where
Self: Sized;
}

impl<'src> AstGeneratorContext<'src> {
/// Construct a new [AstGeneratorContext] for parsing a given file and generating its AST.
///
/// Construct a new [AstGeneratorContext] for parsing a given file and generating its AST.
///
/// # Panics
/// - This function will panic if the given `file_id` is not in the given `file_map`.
pub fn new(file_id: FileId, file_map: &'src FileMap<'src>) -> Self {
// Get the full source of the given file.
let full_source = Fragment {
inner: file_map.source(file_id).expect("Found file in file_map")
// Get the full source of the given file.
let full_source = Fragment {
inner: file_map.source(file_id).expect("Found file in file_map"),
};

AstGeneratorContext {
file_id,
file_map,
full_source,
lexer: Lexer::new(full_source.inner)
lexer: Lexer::new(full_source.inner),
}
}

/// Fork this [AstGeneratorContext] producing a new (identical) one that can be used for
/// parsing that may fail without modifying this one. This is equivalent to clone under the hood,
/// but is named differently to reflect the different use case and match up with [Lexer::fork].
///
///
/// If you parse sucessfully on the forked [AstGeneratorContext], you can use [AstGeneratorContext::update]
/// to push that progress back to this [AstGeneratorContext].
/// to push that progress back to this [AstGeneratorContext].
pub const fn fork(&self) -> Self {
Self {
Self {
file_id: self.file_id,
file_map: self.file_map,
full_source: self.full_source,
lexer: self.lexer.fork()
lexer: self.lexer.fork(),
}
}

/// Update this [AstGeneratorContext] to match the position and state of the other one. This is designed to work
/// exclusively with [AstGeneratorContext]s previously created by calling [AstGeneratorContext::fork]
/// on this one. To that end, the only field that actually gets copied over/updated from `to` is
/// [AstGeneratorContext::lexer].
/// exclusively with [AstGeneratorContext]s previously created by calling [AstGeneratorContext::fork]
/// on this one. To that end, the only field that actually gets copied over/updated from `to` is
/// [AstGeneratorContext::lexer].
pub fn update(&mut self, to: &Self) {
self.lexer = to.lexer;
}

/// Peek the next [Token] from the internal [Lexer] without consuming it or making progress.
/// Peek the next [Token] from the internal [Lexer] without consuming it or making progress.
#[inline]
pub fn peek_token(&self) -> Option<Token<'src>> {
self.lexer.fork().next_token()
}

/// Peek a [Fragment] -- this is mostly for error reporting, when you need to get the location you
/// Peek a [Fragment] -- this is mostly for error reporting, when you need to get the location you
/// expected something to be. This returns the [Fragment] for the next [Token] (without consuming it) if
/// there is one, and if not, returns a zero-length fragment at the end of the file being parsed.
/// there is one, and if not, returns a zero-length fragment at the end of the file being parsed.
pub fn peek_fragment(&self) -> Fragment<'src> {
self.peek_token()
.map(|token| token.fragment)
.unwrap_or_else(|| {
// Use this to get a fragment of "" at the end of the file.
Fragment { inner: &self.full_source.inner[self.full_source.len()..] }
// Use this to get a fragment of "" at the end of the file.
Fragment {
inner: &self.full_source.inner[self.full_source.len()..],
}
})
}

/// Consume the next [Token] from the internal [Lexer].
/// Consume the next [Token] from the internal [Lexer].
#[inline]
pub fn next_token(&mut self) -> Option<Token<'src>> {
self.lexer.next_token()
}

/// Peek the next [Token] from the internal [Lexer] and return true if it exists and
/// the [Token::variant] is equal to `kind`.
/// Peek the next [Token] from the internal [Lexer] and return true if it exists and
/// the [Token::variant] is equal to `kind`.
pub fn next_is(&self, kind: TokenTy) -> bool {
self.peek_token()
.map(|token| token.variant == kind)
.unwrap_or(false)
}

/// Consume and return the next [Token] from the internal [Lexer] if it exists and
/// Consume and return the next [Token] from the internal [Lexer] if it exists and
/// the [Token::variant] is equal to `kind`.
///
///
/// See also: [AstGeneratorContext::next_is]
pub fn next_if_is(&mut self, kind: TokenTy) -> Option<Token<'src>> {
if self.next_is(kind) {
if self.next_is(kind) {
self.next_token()
} else {
None
}
}

/// Peek the next [Token] from the internal [Lexer] and return true if it exists and
/// Peek the next [Token] from the internal [Lexer] and return true if it exists and
/// the [Token::variant] matches any of the ones produced by `kinds`.
pub fn next_is_any(&self, kinds: impl IntoIterator<Item = TokenTy>) -> bool {
self.peek_token()
.map(|token| kinds.into_iter().any(|kind| kind == token.variant))
.unwrap_or(false)
}

/// Consume and return the next [Token] from the internal [Lexer] if it exists and
/// the [Token::variant] matches any of the ones produced by `kinds`.
pub fn next_if_is_any(&mut self, kinds: impl IntoIterator<Item = TokenTy>) -> Option<Token<'src>> {
/// Consume and return the next [Token] from the internal [Lexer] if it exists and
/// the [Token::variant] matches any of the ones produced by `kinds`.
pub fn next_if_is_any(
&mut self,
kinds: impl IntoIterator<Item = TokenTy>,
) -> Option<Token<'src>> {
if self.next_is_any(kinds) {
self.next_token()
} else {
Expand All @@ -147,8 +159,11 @@ impl<'src> AstGeneratorContext<'src> {

#[cfg(test)]
mod tests {
use crate::{filemap::{FileMap, FileName}, parser::fragment::Fragment};
use super::AstGeneratorContext;
use crate::{
filemap::{FileMap, FileName},
parser::fragment::Fragment,
};

#[test]
fn peek_fragment_empty_file() {
Expand All @@ -157,7 +172,9 @@ mod tests {
let id = file_map.add_str_ref(FileName::Test("peek_empty_frag"), source);
let ast_gen_ctx = AstGeneratorContext::new(id, &file_map);

assert!(ast_gen_ctx.peek_fragment().ptr_eq(&Fragment { inner: source }));
assert!(ast_gen_ctx
.peek_fragment()
.ptr_eq(&Fragment { inner: source }));
}

#[test]
Expand All @@ -167,8 +184,12 @@ mod tests {
let id = file_map.add_str_ref(FileName::Test("peek_end_of_file_frag"), source);
let mut ast_gen_ctx = AstGeneratorContext::new(id, &file_map);

assert!(ast_gen_ctx.next_token().unwrap().fragment.ptr_eq(&Fragment { inner: source }));

assert!(ast_gen_ctx
.next_token()
.unwrap()
.fragment
.ptr_eq(&Fragment { inner: source }));

let end_frag = ast_gen_ctx.peek_fragment();

assert!(Fragment { inner: source }.contains(&end_frag));
Expand All @@ -189,4 +210,3 @@ mod tests {
AstGeneratorContext::new(id_b, &file_map);
}
}

33 changes: 18 additions & 15 deletions wright/src/parser/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ use super::AstNode;

pub mod primary;

/// An expression in Wright source code is anything that can be evaluated to a value.
/// An expression in Wright source code is anything that can be evaluated to a value.
pub enum Expression<'src> {
Primary(PrimaryExpression<'src>)
Primary(PrimaryExpression<'src>),
}

/// An error that occurs while parsing an expression.
/// An error that occurs while parsing an expression.
#[derive(Debug, Clone)]
pub enum ExpressionParsingError<'src> {
/// An expression was expected but not found.
/// An expression was expected but not found.
ExpectedExpression {
/// Where the expression was expected.
at: Fragment<'src>
/// Where the expression was expected.
at: Fragment<'src>,
},

/// An error parsing a primary expression not caused by inavailability.
PrimaryExpressionParsingError(PrimaryExpressionParsingError<'src>)
/// An error parsing a primary expression not caused by inavailability.
PrimaryExpressionParsingError(PrimaryExpressionParsingError<'src>),
}

impl<'src> AstNode<'src> for Expression<'src> {
Expand All @@ -36,26 +36,29 @@ impl<'src> AstNode<'src> for Expression<'src> {
}

fn try_parse(ctx: &mut super::AstGeneratorContext<'src>) -> Result<Self, Self::Error>
where Self: Sized
where
Self: Sized,
{
// We need to go in reverse order of strength here (from weakest to strongest) to avoid under parsing.
// (i.e. parsing a primary when the primary expression was the left side of a binary expression).

// Try parsing a binary expression.
match PrimaryExpression::try_parse(ctx) {
// If we get a primary, early return it.
// If we get a primary, early return it.
Ok(primary) => return Ok(Expression::Primary(primary)),

// If we get an error that is not unavailability, return it early too.
// If we get an error that is not unavailability, return it early too.
Err(err @ PrimaryExpressionParsingError::OtherIntegerLiteralParsingError(_)) => {
return Err(ExpressionParsingError::PrimaryExpressionParsingError(err));
},
}

// If we get an error that is unavailability, just ignore it and keep going.
// If we get an error that is unavailability, just ignore it and keep going.
Err(PrimaryExpressionParsingError::ExpectedPrimaryExpression { .. }) => {}
}

// If we get to the end of the function with no parse sucessful, we error.
Err(ExpressionParsingError::ExpectedExpression { at: ctx.peek_fragment() })
// If we get to the end of the function with no parse sucessful, we error.
Err(ExpressionParsingError::ExpectedExpression {
at: ctx.peek_fragment(),
})
}
}
47 changes: 27 additions & 20 deletions wright/src/parser/ast/expression/primary.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
//! Primary expression parsing in Wright source code.
//!
//! Primary expression parsing in Wright source code.
//!
//! Primary expressions are considered the atoms of most expressions most primary expressions are literals,
//! which cannot be broken up into sub-expressions.
//! which cannot be broken up into sub-expressions.
use crate::parser::{ast::AstNode, fragment::Fragment};
use self::integer_literal::{IntegerLiteral, IntegerLiteralParsingError};
use crate::parser::{ast::AstNode, fragment::Fragment};

pub mod integer_literal;

/// A primary expression in Wright source code. These are the atoms of expressions, and can be combined using operators
/// to form more complicated expressions.
/// to form more complicated expressions.
pub enum PrimaryExpression<'src> {
IntegerLiteral(IntegerLiteral<'src>)
IntegerLiteral(IntegerLiteral<'src>),
}

/// Error occuring when someone attempts to parse a primary expression and there is not one.
/// Error occuring when someone attempts to parse a primary expression and there is not one.
#[derive(Clone, Debug)]
pub enum PrimaryExpressionParsingError<'src> {
/// An attempt was made to parse a primary expression and there was not one available.
/// An attempt was made to parse a primary expression and there was not one available.
ExpectedPrimaryExpression {
/// The location in source code where a primary expression was expected.
at: Fragment<'src>
/// The location in source code where a primary expression was expected.
at: Fragment<'src>,
},

/// An error in parsing an integer literal besides in-availability.
/// An error in parsing an integer literal besides in-availability.
OtherIntegerLiteralParsingError(IntegerLiteralParsingError<'src>),
}

Expand All @@ -36,22 +36,29 @@ impl<'src> AstNode<'src> for PrimaryExpression<'src> {
}
}

fn try_parse(ctx: &mut crate::parser::ast::AstGeneratorContext<'src>) -> Result<Self, Self::Error>
where Self: Sized
fn try_parse(
ctx: &mut crate::parser::ast::AstGeneratorContext<'src>,
) -> Result<Self, Self::Error>
where
Self: Sized,
{
// If it's a successful parse, return Ok. `num` errors also fast-return. If it's an unavailability
// error, keep trying other types of primary.
// If it's a successful parse, return Ok. `num` errors also fast-return. If it's an unavailability
// error, keep trying other types of primary.
match IntegerLiteral::try_parse(ctx) {
Ok(int_lit) => return Ok(PrimaryExpression::IntegerLiteral(int_lit)),

Err(num_err @ IntegerLiteralParsingError::NumParsingError { .. }) => {
return Err(PrimaryExpressionParsingError::OtherIntegerLiteralParsingError(num_err));
},
return Err(PrimaryExpressionParsingError::OtherIntegerLiteralParsingError(
num_err,
));
}

Err(IntegerLiteralParsingError::ExpectedIntegerLiteral { .. }) => {}
}

// If we get to the end of the function, it's an error.
Err(PrimaryExpressionParsingError::ExpectedPrimaryExpression { at: ctx.peek_fragment() })

// If we get to the end of the function, it's an error.
Err(PrimaryExpressionParsingError::ExpectedPrimaryExpression {
at: ctx.peek_fragment(),
})
}
}
Loading

0 comments on commit 3cd6231

Please sign in to comment.