Skip to main content

Parser

Struct Parser 

Source
pub struct Parser<'a> { /* private fields */ }
Expand description

Parser for Nash source code.

Combines the arena allocator with parsing state for a unified API. All parsed AST nodes are allocated in the provided bump arena.

The source bytes should already be allocated in the arena (via bump.alloc_str), so all string slices in the resulting AST share the 'a lifetime.

Implementations§

Source§

impl<'a> Parser<'a>

Source

pub fn infix_decl(&mut self) -> Result<&'a Located<Infix<'a>>, ModuleErr<'a>>

Parse an infix declaration.

Parses: infix left 6 (|>) = apR

Mirrors Elm’s infix_:

infix_ :: Parser E.Module (A.Located Src.Infix)
infix_ =
  do  start <- getPosition
      Keyword.infix_ err
      Space.chompAndCheckIndent _err err
      associativity <- oneOf err [ left, right, non ]
      Space.chompAndCheckIndent _err err
      precedence <- Number.precedence err
      Space.chompAndCheckIndent _err err
      word1 0x28 {-(-} err
      op <- Symbol.operator err _err
      word1 0x29 {-)-} err
      Space.chompAndCheckIndent _err err
      word1 0x3D {-=-} err
      Space.chompAndCheckIndent _err err
      name <- Var.lower err
      end <- getPosition
      Space.chomp _err
      Space.checkFreshLine err
      return (A.at start end (Src.Infix op associativity precedence name))
Source§

impl<'a> Parser<'a>

Source

pub fn declaration(&mut self) -> Result<(Decl<'a>, Position), Decl<'a>>

Parse a single declaration.

Mirrors Elm’s declaration:

declaration :: Space.Parser E.Decl Decl
declaration =
  do  maybeDocs <- chompDocComment
      start <- getPosition
      oneOf E.DeclStart
        [ typeDecl maybeDocs start
        , portDecl maybeDocs  -- skipped in Nash
        , valueDecl maybeDocs start
        ]
Source§

impl<'a> Parser<'a>

Source

pub fn exposing(&mut self) -> Result<Exposing<'a>, Exposing>

Parse an exposing list.

Mirrors Elm’s exposing:

exposing = '(' ( '..' | exposed { ',' exposed } ) ')'
Source§

impl<'a> Parser<'a>

Source

pub fn expression( &mut self, ) -> Result<(&'a Located<Expr<'a>>, Position), Expr<'a>>

Parse a full expression, returning the expression and end position.

Mirrors Elm’s expression:

expression =
  do  start <- getPosition
      oneOf E.Start
        [ let_ start
        , if_ start
        , case_ start
        , function start
        , do  expr <- possiblyNegativeTerm start
              ...
        ]

Currently implements: lambda, possiblyNegativeTerm + function application. TODO: let, if, case, operators

Source

pub fn term(&mut self) -> Result<&'a Located<Expr<'a>>, Expr<'a>>

Parse a term (atomic expression).

Mirrors Elm’s term:

term =
  do  start <- getPosition
      oneOf E.Start
        [ variable start >>= accessible start
        , string start
        , number start
        , ...
        ]
Source§

impl<'a> Parser<'a>

Source

pub fn import(&mut self) -> Result<&'a Import<'a>, Module<'a>>

Parse an import statement.

Mirrors Elm’s chompImport:

import = 'import' module_name [ 'as' alias ] [ 'exposing' exposing_list ]
Source§

impl<'a> Parser<'a>

Source

pub fn keyword_if<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the if keyword.

Mirrors Elm’s Keyword.if_.

Source

pub fn keyword_then<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the then keyword.

Mirrors Elm’s Keyword.then_.

Source

pub fn keyword_else<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the else keyword.

Mirrors Elm’s Keyword.else_.

Source

pub fn keyword_case<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the case keyword.

Mirrors Elm’s Keyword.case_.

Source

pub fn keyword_of<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the of keyword.

Mirrors Elm’s Keyword.of_.

Source

pub fn keyword_let<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the let keyword.

Mirrors Elm’s Keyword.let_.

Source

pub fn keyword_in<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the in keyword.

Mirrors Elm’s Keyword.in_.

Source

pub fn keyword_type<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the type keyword.

Mirrors Elm’s Keyword.type_.

Source

pub fn keyword_alias<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the alias keyword.

Mirrors Elm’s Keyword.alias_.

Source

pub fn keyword_infix<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the infix keyword.

Mirrors Elm’s Keyword.infix_.

Source

pub fn keyword_left<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the left keyword (for infix associativity).

Mirrors Elm’s Keyword.left_.

Source

pub fn keyword_right<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the right keyword (for infix associativity).

Mirrors Elm’s Keyword.right_.

Source

pub fn keyword_non<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the non keyword (for infix associativity).

Mirrors Elm’s Keyword.non_.

Source

pub fn keyword_import<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the import keyword.

Mirrors Elm’s Keyword.import_.

Source

pub fn keyword_as<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the as keyword.

Mirrors Elm’s Keyword.as_.

Source

pub fn keyword_exposing<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the exposing keyword.

Mirrors Elm’s Keyword.exposing_.

Source

pub fn keyword_module<E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse the module keyword.

Mirrors Elm’s Keyword.module_.

Source§

impl<'a> Parser<'a>

Source

pub fn module_header( &mut self, ) -> Result<(&'a Located<&'a str>, &'a Located<Exposing<'a>>), Module<'a>>

Parse a module header.

Mirrors Elm’s chompHeader (simplified - no port/effect modules):

module_header = 'module' module_name 'exposing' exposing_list

Returns the module name and exports as a tuple.

Source

pub fn module(&mut self) -> Result<Module<'a>, Module<'a>>

Parse a complete module.

Mirrors Elm’s chompModule:

module = [ module_header ] { import } { infix } { declaration }
Source§

impl<'a> Parser<'a>

Source

pub fn number_literal<E>( &mut self, to_expectation: impl FnOnce(Row, Col) -> E, to_error: impl FnOnce(Number, Row, Col) -> E, ) -> Result<i128, E>

Parse an integer literal with custom error constructors.

Mirrors Elm’s Number.number:

number :: (Row -> Col -> x) -> (E.Number -> Row -> Col -> x) -> Parser x Number

Takes two error constructors:

  • to_expectation: called when no digit is found (empty error, no input consumed)
  • to_error: called when parsing fails after consuming input

Handles:

  • Decimal integers: 42, 123
  • Hex integers: 0xFF, 0x1A2B
§Example
// From expression parsing:
self.number_literal(error::Expr::Start, error::Expr::Number)
Source§

impl<'a> Parser<'a>

Source

pub fn pattern_term(&mut self) -> Result<&'a Located<Pattern<'a>>, Pattern<'a>>

Parse an atomic pattern (no cons, as, or ctor args).

Mirrors Elm’s Pattern.term:

term =
  do  start <- getPosition
      oneOf E.PStart
        [ record start
        , tuple start
        , list start
        , termHelp start
        ]
Source

pub fn pattern_expr( &mut self, ) -> Result<(&'a Located<Pattern<'a>>, Position), Pattern<'a>>

Parse a full pattern expression including cons (::), as-patterns, and ctor with args.

Returns (pattern, end) where end is the position at the end of the pattern (before any trailing whitespace was chomped). This is important for indent checking.

Mirrors Elm’s Pattern.expression:

expression :: Space.Parser E.Pattern Src.Pattern
expression =
  do  start <- getPosition
      ePart <- exprPart
      exprHelp start [] ePart
Source§

impl<'a> Parser<'a>

Source

pub fn chomp<E>( &mut self, to_error: impl FnOnce(Space, Row, Col) -> E, ) -> Result<(), E>

Consume whitespace and comments.

Returns an error if tabs or unclosed comments are found. Mirrors Elm’s Space.chomp.

Source

pub fn chomp_and_check_indent<E>( &mut self, to_space_error: impl FnOnce(Space, Row, Col) -> E, to_indent_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Consume whitespace and check that we’re indented past the current indent level.

Mirrors Elm’s Space.chompAndCheckIndent.

Source

pub fn check_indent<E>( &self, end_row: Row, end_col: Col, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Check that current column is greater than indent level.

Called after a chomp to verify indentation. Mirrors Elm’s Space.checkIndent.

Note: Uses end_col for the indent check (not current position), matching Elm’s behavior where the position is passed explicitly.

Source

pub fn check_aligned<E>( &self, to_error: impl FnOnce(u16, Row, Col) -> E, ) -> Result<(), E>

Check that current column equals indent level (for alignment).

Mirrors Elm’s Space.checkAligned.

Source

pub fn check_fresh_line<E>( &self, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Check that we’re at column 1 (start of a fresh line).

Mirrors Elm’s Space.checkFreshLine.

Source

pub fn doc_comment<E>( &mut self, to_expectation: impl FnOnce(Row, Col) -> E, to_space_error: impl FnOnce(Space, Row, Col) -> E, ) -> Result<&'a Comment<'a>, E>

Parse a doc comment {-| ... -}.

Returns the Comment containing a Snippet of the content. Mirrors Elm’s Space.docComment.

Source§

impl<'a> Parser<'a>

Source

pub fn string_literal<E>( &mut self, to_expectation: impl FnOnce(Row, Col) -> E, to_error: impl FnOnce(StringError, Row, Col) -> E, ) -> Result<&'a str, E>

Parse a string literal with custom error constructors.

Mirrors Elm’s String.string:

string :: (Row -> Col -> x) -> (E.String -> Row -> Col -> x) -> Parser x ES.String

Handles both single-line ("...") and multi-line ("""...""") strings.

Source§

impl<'a> Parser<'a>

Source

pub fn type_expr( &mut self, ) -> Result<(&'a Located<Type<'a>>, Position), Type<'a>>

Parse a type expression including function arrows.

Returns (type, end) where end is the position at end of type (before any chomp).

Mirrors Elm’s Type.expression:

expression :: Space.Parser E.Type Src.Type
expression =
  do  start <- getPosition
      term1@(tipe1, end1) <- oneOf E.TStart [ app start, term... ]
      oneOfWithFallback [ arrow... ] term1
Source

pub fn type_term(&mut self) -> Result<&'a Located<Type<'a>>, Type<'a>>

Parse an atomic type (no arrows, no application).

Mirrors Elm’s Type.term:

  • Named types: Int, Maybe, Module.Type
  • Type variables: a, msg
  • Tuples: (), (Int, String)
  • Records: {}, { name : String }, { a | name : String }
Source§

impl<'a> Parser<'a>

Source

pub fn new(bump: &'a Bump, src: &'a [u8]) -> Self

Create a new parser for the given source bytes.

The source should already be allocated in the arena.

Source

pub fn position(&self) -> (Row, Col)

Current position as (row, col).

Source

pub fn get_position(&self) -> Position

Get the current position as a Position.

Source

pub fn add_end<T>(&self, start: Position, value: T) -> &'a Located<T>

Create a Located value spanning from start to the current position, allocated directly in the arena.

Source

pub fn row(&self) -> Row

Current row (1-indexed).

Source

pub fn col(&self) -> Col

Current column (1-indexed).

Source

pub fn indent(&self) -> u16

Current indentation level.

Source

pub fn set_indent(&mut self, indent: u16)

Set the indentation level.

Source

pub fn with_indent<T, E>( &mut self, parser: impl FnOnce(&mut Self) -> Result<T, E>, ) -> Result<T, E>

Run a parser with the current column as the indent level, then restore the old indent level.

Mirrors Elm’s withIndent:

withIndent (Parser parser) =
  Parser $ \(State src pos end oldIndent row col) cok eok cerr eerr ->
    let
      cok' a (State s p e _ r c) = cok a (State s p e oldIndent r c)
      eok' a (State s p e _ r c) = eok a (State s p e oldIndent r c)
    in
    parser (State src pos end col row col) cok' eok' cerr eerr
Source

pub fn with_backset_indent<T, E>( &mut self, backset: u16, parser: impl FnOnce(&mut Self) -> Result<T, E>, ) -> Result<T, E>

Run a parser with indent set to (current column - backset), then restore the old indent level.

Mirrors Elm’s withBacksetIndent:

withBacksetIndent backset (Parser parser) =
  Parser $ \(State src pos end oldIndent row col) cok eok cerr eerr ->
    parser (State src pos end (col - backset) row col) ...
Source

pub fn is_eof(&self) -> bool

Check if we’ve reached the end of input.

Source

pub fn one_of<T, E>( &mut self, to_error: impl FnOnce(Row, Col) -> E, parsers: Vec<Box<dyn FnOnce(&mut Self) -> Result<T, E> + '_>>, ) -> Result<T, E>

Try multiple parsers in order, returning the first success.

Mirrors Elm’s oneOf:

oneOf :: (Row -> Col -> x) -> [Parser x a] -> Parser x a

Key semantics:

  • If a parser fails without consuming input, try the next one
  • If a parser fails after consuming input, propagate the error (committed)
  • If all parsers fail without consuming, call to_error(row, col)
§Example
parser.one_of(
    error::Expr::Start,
    vec![
        Box::new(|p: &mut Parser| p.string(start)),
        Box::new(|p| p.number(start)),
    ],
)
Source

pub fn one_of_with_fallback<T, E>( &mut self, parsers: Vec<Box<dyn FnOnce(&mut Self) -> Result<T, E> + '_>>, fallback: T, ) -> Result<T, E>

Like one_of but returns a fallback value if nothing matches.

Mirrors Elm’s oneOfWithFallback:

oneOfWithFallback :: [Parser x a] -> a -> Parser x a
Source

pub fn in_context<T, StartErr, BodyErr, ContextErr>( &mut self, add_context: impl FnOnce(&'a Bump, BodyErr, Row, Col) -> ContextErr, start_parser: impl FnOnce(&mut Self) -> Result<(), StartErr>, body_parser: impl FnOnce(&mut Self) -> Result<T, BodyErr>, ) -> Result<T, ContextErr>
where StartErr: Into<ContextErr>,

Parse with error context wrapping.

Mirrors Elm’s inContext:

inContext :: (x -> Row -> Col -> y) -> Parser y start -> Parser x a -> Parser y a
  1. Saves the starting position
  2. Runs start_parser - if it fails without consuming, returns that error
  3. If start succeeds, runs body_parser
  4. If body fails, wraps the error using add_context at the original position

The add_context closure receives the bump allocator so it can allocate wrapped errors.

This is used to provide better error context, e.g., “error in list expression”.

Source

pub fn specialize<T, InnerErr, OuterErr>( &mut self, add_context: impl FnOnce(&'a Bump, InnerErr, Row, Col) -> OuterErr, parser: impl FnOnce(&mut Self) -> Result<T, InnerErr>, ) -> Result<T, OuterErr>

Transform errors from one type to another with position context.

Mirrors Elm’s specialize:

specialize :: (x -> Row -> Col -> y) -> Parser x a -> Parser y a

Runs the parser and wraps any error with the context at the starting position. The add_context closure receives the bump allocator so it can allocate wrapped errors.

Source

pub fn word1<E>( &mut self, expected: u8, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse a single expected byte.

Mirrors Elm’s word1:

word1 :: Word8 -> (Row -> Col -> x) -> Parser x ()

Returns Ok(()) and advances if the byte matches. Returns Err without consuming if it doesn’t match.

Source

pub fn word2<E>( &mut self, b1: u8, b2: u8, to_error: impl FnOnce(Row, Col) -> E, ) -> Result<(), E>

Parse two expected consecutive bytes.

Mirrors Elm’s word2:

word2 :: Word8 -> Word8 -> (Row -> Col -> x) -> Parser x ()
Source

pub fn peek(&self) -> Option<u8>

Peek at the current byte without consuming it.

Source

pub fn peek_at(&self, offset: usize) -> Option<u8>

Peek at a byte at the given offset from current position.

Source

pub fn remaining(&self) -> &'a [u8]

Get the remaining bytes from current position.

Source

pub fn advance(&mut self)

Advance by one byte, updating row/col for newlines.

Source

pub fn advance_by(&mut self, n: usize)

Advance by n bytes, tracking newlines.

Source

pub fn alloc<T>(&self, value: T) -> &'a T

Allocate a value in the arena.

Source

pub fn alloc_slice_copy<T: Copy>(&self, slice: &[T]) -> &'a [T]

Allocate a slice in the arena by copying.

Source

pub fn alloc_str(&self, s: &str) -> &'a str

Allocate a string in the arena (for constructed strings like escape sequences).

Auto Trait Implementations§

§

impl<'a> Freeze for Parser<'a>

§

impl<'a> !RefUnwindSafe for Parser<'a>

§

impl<'a> !Send for Parser<'a>

§

impl<'a> !Sync for Parser<'a>

§

impl<'a> Unpin for Parser<'a>

§

impl<'a> UnsafeUnpin for Parser<'a>

§

impl<'a> !UnwindSafe for Parser<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.