avram

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 17, 2024 License: MIT Imports: 8 Imported by: 0

README

avram

GitHub go.mod Go version GitHub release (latest by date including pre-releases) GoDoc Reference

avram is a generic parser-combinator library inspired by the likes of ocaml's Angstrom and Haskell's Parsec ported to Go and making use of Go's generic data types.

avram is an early work in progress and has not been extensively performance profiled or tuned in any way. The initial goal of this project was to provide the comfortable developer ergonomics of angstrom within the Go ecosystem. The API is very similar to the angstrom API as a result, replacing ocaml's custom infix operators with traditional Go functions where required.

Usage

This example is a direct port of the provided usage example in the angstrom documentation.

package main

import (
	"strconv"

	. "github.com/stntngo/avram"
	"github.com/stntngo/avram/result"
)

var expr = Finish(Fix(func(expr Parser[int]) Parser[int] {
	add := DiscardLeft(SkipWS(Rune('+')), Return(func(a, b int) int { return a + b }))
	sub := DiscardLeft(SkipWS(Rune('-')), Return(func(a, b int) int { return a - b }))
	mul := DiscardLeft(SkipWS(Rune('*')), Return(func(a, b int) int { return a * b }))
	div := DiscardLeft(SkipWS(Rune('/')), Return(func(a, b int) int { return a / b }))

	integer := result.Unwrap(Lift(
		result.Lift(strconv.Atoi),
		TakeWhile1(Runes('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')),
	))

	factor := Or(Wrap(Rune('('), expr, Rune(')')), integer)
	term := ChainL1(factor, Or(mul, div))

	return ChainL1(term, Or(add, sub))
}))

func Eval(s string) (int, error) {
	out, err := expr(NewScanner(s))
	if err != nil {
		return 0, err
	}

	return out, nil
}

Documentation

Index

Constants

This section is empty.

Variables

Space parses a single valid unicode whitespace

Functions

func AnyRune

func AnyRune(s *Scanner) (rune, error)

AnyRune accepts any rune and returns it.

func Error added in v0.3.0

func Error[A, B any](f func(A) B) func(A) (B, error)

Error wraps a non-error returning function to match the expected Lift function signature.

func Error2 added in v0.3.0

func Error2[A, B, C any](f func(A, B) C) func(A, B) (C, error)

Error2 wraps a non-error returning function to match the expected Lift function signature.

func Error3 added in v0.3.0

func Error3[A, B, C, D any](f func(A, B, C) D) func(A, B, C) (D, error)

Error3 wraps a non-error returning function to match the expected Lift function signature.

func Error4 added in v0.3.0

func Error4[A, B, C, D, E any](f func(A, B, C, D) E) func(A, B, C, D) (E, error)

Error4 wraps a non-error returning function to match the expected Lift function signature.

func Input

func Input(s *Scanner) (string, error)

Input parser returns the untouched, unconsumed input text associated with the Scanner.

func ParseString added in v0.2.1

func ParseString[A any](input string, p Parser[A]) (A, error)

ParseString parses the input string with the parser `p` constructing a new scanner as necessary.

func Position

func Position(s *Scanner) (int, error)

Position parser returns the current source position.

func Remaining

func Remaining(s *Scanner) (string, error)

Remaining parser returns the remaining input text associated with the Scanner that has yet to be consumed.

func Runes

func Runes(rs ...rune) func(rune) bool

Runes checks whether a rune `r` is within the provided set of `rs`.

Types

type Pair

type Pair[A, B any] struct {
	Left  A
	Right B
}

Pair is a simple A * B product type holding two different subtypes in its Left and Right branches.

func MakePair

func MakePair[A, B any](a A, b B) Pair[A, B]

MakePair constructs a single object Pair from the two provided arguments.

type Parser

type Parser[T any] func(*Scanner) (T, error)

Parser parses input text contained in the Scanner and produces type T. Higher order parsers are constructed through application of combinators on Parsers of different types.

func Assert added in v0.2.1

func Assert[A any](p Parser[A], pred func(A) bool, fail func(A) error) Parser[A]

Assert runs the provided parser `p` and verifies its output against the predicate `pred`. If the predicate returns false, the `fail` function is called to return an error. Otherwise, the output of the parser `p` is returned.

func Bind

func Bind[A, B any](p Parser[A], f func(A) Parser[B]) Parser[B]

Bind creates a parser that will run `p`, pass its result to `f` run the parser that `f` produces and return its result.

func Both

func Both[A, B any](p Parser[A], q Parser[B]) Parser[Pair[A, B]]

Both runs `p` followed by `q` and returns both results as a pair.

func ChainL1

func ChainL1[A any](p Parser[A], op Parser[func(A, A) A]) Parser[A]

ChainL1 parses one or more occurrences of `p`, separated by `op` and returns a value obtained by left associative application of all functions returned by `op` to the values returned by `p`.

This parser can be used to eliminate left recursion which typically occurs in expression grammars.

See ChainR1 for example.

func ChainR1 added in v0.2.1

func ChainR1[A any](p Parser[A], op Parser[func(A, A) A]) Parser[A]

ChainR1 parses one or more occurrences of `p`, separated by `op` and returns a value obtained by right associative application of all functions returned by `op` to the values returned by `p`.

This parser can be used to eliminate left recursion which typically occurs in expression grammars.

Example:

ParseExpression := Fix(func(expr Parser[int]) Parser[int] {
	ParseAdd := DiscardLeft(SkipWS(Rune('+')), Return(func(a, b int) int { return a + b }))
	ParseSub := DiscardLeft(SkipWS(Rune('-')), Return(func(a, b int) int { return a - b }))
	ParseMul := DiscardLeft(SkipWS(Rune('*')), Return(func(a, b int) int { return a * b }))
	ParseDiv := DiscardLeft(SkipWS(Rune('/')), Return(func(a, b int) int { return a / b }))

	ParseInteger := result.Unwrap(Lift(
		result.Lift(strconv.Atoi),
		TakeWhile1(Runes('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')),
	))

	ParseFactor := Or(Wrap(Rune('('), expr, Rune(')')), ParseInteger)
	ParseTerm := ChainR1(ParseFactor, Or(ParseMul, ParseDiv))

	return ChainR1(ParseTerm, Or(ParseAdd, ParseSub))
})

func Choice

func Choice[A any](msg string, ps ...Parser[A]) Parser[A]

Choice runs each parser in `ps` in order until one succeeds and returns the result. If any of the failing parsers in `ps` consumes input, the accumulated parse errors will be returned and the parse chain will abort. In the case that none of the parsers succeeds, then the parser will fail with the message "expected {msg}".

NOTE: Like with the Or combinator, this functionality maps the original haskell combinator and if you wish to allow any of the parsers within `ps` to consume input without stopping the parse chain execution, then you should wrap the provided parser with a Try meta-parser.

func Consumed

func Consumed[A any](p Parser[A]) Parser[string]

Consumed runs p and returns the contents that were consumed during the parsing as a string.

func Count

func Count[A any](n int, p Parser[A]) Parser[[]A]

Count runs `p` exactly `n` times, returning a slice of the results.

func DiscardLeft

func DiscardLeft[A, B any](p Parser[A], q Parser[B]) Parser[B]

DiscardLeft runs `p`, discards its results and then runs `q` and returns its results.

func DiscardRight

func DiscardRight[A, B any](p Parser[A], q Parser[B]) Parser[A]

DiscardRight runs `p`, then runs `q`, discards its results and returns the initial result of `p`.

func Fail

func Fail[A any](err error) Parser[A]

Fail returns a parser that will always fail with the error `err`.

func Finish

func Finish[A any](p Parser[A]) Parser[A]

Finish meta-parser ensures that the completed parser has successfully parsed the entirety of the input string contained in the scanner.

func Fix

func Fix[A any](f func(Parser[A]) Parser[A]) Parser[A]

Fix computes the fix-point of `f` and runs the resultant parser. The argument that `f` receives is the result of `Fix(f)`, which `f` must use to define `Fix(f)`.

func Lift

func Lift[A, B any](f func(A) (B, error), p Parser[A]) Parser[B]

Lift promotes functions into a parser. The returned parser first executes the provided parser `p` before transforming the returned value of `p` using `f` and returning it.

func Lift2

func Lift2[A, B, C any](
	f func(A, B) (C, error),
	p1 Parser[A],
	p2 Parser[B],
) Parser[C]

Lift2 promotes 2-ary functions into a parser.

func Lift3

func Lift3[A, B, C, D any](
	f func(A, B, C) (D, error),
	p1 Parser[A],
	p2 Parser[B],
	p3 Parser[C],
) Parser[D]

Lift3 promotes 3-ary functions into a parser.

func Lift4

func Lift4[A, B, C, D, E any](
	f func(A, B, C, D) (E, error),
	p1 Parser[A],
	p2 Parser[B],
	p3 Parser[C],
	p4 Parser[D],
) Parser[E]

Lift4 promotes 4-ary functions into a parser.

func List

func List[A any](ps []Parser[A]) Parser[[]A]

List runs each `p` in `ps` in sequence, returning a slice of results of each `p`.

func Location added in v0.2.2

func Location[A, B any](p Parser[A], f func(start int, end int, parsed A) B) Parser[B]

Location meta-parser tracks the start and end location of successfully parsed inputs, allowing the start and end location to be combined into the parsed value through the provided function.

func LookAhead

func LookAhead[A any](p Parser[A]) Parser[A]

LookAhead constructs a new parser that will apply the provided parser `p` without consuming any input regardless of whether `p` succeeds or fails.

func Many

func Many[A any](p Parser[A]) Parser[[]A]

Many runs `p` zero or more times and returns a slice of results from the runs of `p`.

func Many1

func Many1[A any](p Parser[A]) Parser[[]A]

Many` runs `p` one ore more times and returns a slice of results from the runs of `p`.

func ManyTill

func ManyTill[A, B any](p Parser[A], e Parser[B]) Parser[[]A]

ManyTill runs parser `p` zero ore more times until action `e` succeeds and returns the slice of results from the runs of `p`.

func MatchRegexp added in v0.2.1

func MatchRegexp(re *regexp.Regexp) Parser[string]

MatchRegexp accepts the target regex and returns it.

func MatchString

func MatchString(target string) Parser[string]

MatchString accepts the target string and returns it.

func Maybe

func Maybe[A any](p Parser[A]) Parser[*A]

Maybe constructs a new parser that will attempt to parse the input using the provided parser `p`. If the parser is successful, it will return a pointer to the parsed value. If the parse is unsuccessful it will return a nil pointer in a poor imitation of an Optional type.

Maybe parsers can never fail.

func Name

func Name[A any](name string, p Parser[A]) Parser[A]

Name associates `name` with parser `p` which will be reported in the case of failure.

func NotRune

func NotRune(r rune) Parser[rune]

NotRune accepts any rune that is not r and returns the matched rune.

func Option

func Option[A any](fallback A, p Parser[A]) Parser[A]

Option runs `p`, returning the result of `p` if it succeeds and `fallback` if it fails.

func Or

func Or[A any](p Parser[A], q Parser[A]) Parser[A]

Or runs `p` and returns the result if it succeeds. If `p` fails and no input has been consumed then `q` will run instead.

NOTE: This functionality maps the implementation of Or from the original haskell parsec combinator. If you wish for the parser `p` to potentially consume input and for that consumed input to be discarded when the parser `q` is run, wrap `q` in the Try meta-parser.

func PrecedingWS

func PrecedingWS[A any](p Parser[A]) Parser[A]

PrecedingWS ignores all whitespace before the data parsed by the parser p.

There must be at least one instance of valid whitespace following the parser p.

func Range added in v0.2.1

func Range(lo, hi rune) Parser[rune]

Range accepts any rune r between lo and hi

func Return

func Return[A any](v A) Parser[A]

Return creates a parser that will always succeed and return `v`.

func Rune

func Rune(r rune) Parser[rune]

Rune accepts r and returns it.

func Satisfy

func Satisfy(f func(rune) bool) Parser[rune]

Satisfy accepts any character for which f returns true and returns the accepted character. In the case that none of the parser succeeds, then the parser will fail indicating the offending character.

func SepBy

func SepBy[A, B any](s Parser[A], p Parser[B]) Parser[[]B]

SepBy runs `p` zero or more times, interspersing runs of `s` in between.

func SepBy1

func SepBy1[A, B any](s Parser[A], p Parser[B]) Parser[[]B]

SepBy1 runs `p` one or more times, interspersing runs of `s` in between.

func Skip

func Skip(f func(rune) bool) Parser[Unit]

Skip accepts any character for which f returns true and discards the accepted character. Skip(f) is equivalent to Satisfy(f) but discards the accepted character.

func SkipMany

func SkipMany[A any](p Parser[A]) Parser[Unit]

SkipMany runs `p` zero or more times, discarding the results.

func SkipMany1

func SkipMany1[A any](p Parser[A]) Parser[Unit]

SkipMany` runs `p` one or more times, discarding the results.

func SkipWS

func SkipWS[A any](p Parser[A]) Parser[A]

SkipWS ignores any whitespace surrounding the value associated with p.

func SkipWhile

func SkipWhile(f func(rune) bool) Parser[Unit]

SkipWhile accepts input as long as f returns true and discards the accepted characters.

func Take

func Take(n int) Parser[string]

Take accepts exactly n characters of input and returns them as a string.

func TakeTill

func TakeTill(f func(rune) bool) Parser[string]

TakeTill accepts input as long as f returns false and returns the accepted characters as a string.

func TakeTill1 added in v0.2.1

func TakeTill1(f func(rune) bool) Parser[string]

TakeTill1 accepts input as long as returns false and returns the accepted characters as a string so long as at least one character was matched

func TakeWhile

func TakeWhile(f func(rune) bool) Parser[string]

TakeWhile accepts input as long as f returns true and returns the accepted characters as a string.

This parser does not fail, if the first call to f returns false on the first character, it will return an empty string.

func TakeWhile1

func TakeWhile1(f func(rune) bool) Parser[string]

TakeWhile1 accepts input as long as f returns true and returns the accepted characters as a string.

This parser requires that f return true for at least one character of input and will fail if it does not.

func TrailingWS

func TrailingWS[A any](p Parser[A]) Parser[A]

TrailingWS ignores all whitespace following the data parsed by the parser p.

There must be at least one instance of valid whitespace following the parser p.

func Try

func Try[A any](p Parser[A]) Parser[A]

Try constructs a new parser that will attempt to parse the input using the provided parser `p`. If the parser is successful, it will return the parsed value, if the parse is unsuccessful it will rewind the scanner input so that no input appears to have been consumed.

func TryChoice added in v0.2.1

func TryChoice[A any](msg string, ps ...Parser[A]) Parser[A]

TryChoice wraps all but the final parser in `ps` ina Try meta-parser, and passes the new parser slice as `ps` into the Choice combinator. TryChoice will only return an error if the final provided parser returns an error.

func Wrap

func Wrap[A, B, C any](left Parser[A], p Parser[B], right Parser[C]) Parser[B]

Wrap runs `left`, discards its results, runs `p`, runs `right`, discards its results, and then returns the result of running `p`.

type Scanner

type Scanner struct {
	// contains filtered or unexported fields
}

Scanner is responsible for maintaining the iterative state through which the constructed parser moves.

Scanner exposes three matching primitives for the parsers to use:

1. MatchRegexp - Matching on compiled regular expressions 2. MatchString - Matching on concrete strings 3. MatchRune - Matching on an individual rune

Each of these matching methods can potentially advance the state of the the scanner along the input if they successfully find a match given the provided matching criteria.

If no match is found in any of these matching primitives, the state of the scanner is not advanced.

func NewScanner

func NewScanner(input string) *Scanner

NewScanner constructs a new avram Scanner from the provided input string.

func (*Scanner) MatchRegexp added in v0.2.1

func (s *Scanner) MatchRegexp(re *regexp.Regexp) (string, error)

MatchRegexp attempts to match the provided regex from the current location of the scanner, returning the first matched instance of the regex as a string if a match is found and an error otherwise.

NOTE: MatchRegexp only advances the scanner position if a valid match is successfully found.

func (*Scanner) MatchRune added in v0.2.1

func (s *Scanner) MatchRune(match func(rune) error) (r rune, err error)

MatchRune attempts to match the provided predicate function with the next rune in the scanner's input stream.

NOTE: MatchRune only advances the scanner position if a valid match is successfully found.

func (*Scanner) MatchString

func (s *Scanner) MatchString(target string) (string, error)

MatchString attempts to match the provided target string rune-by-rune exactly as specified, returning the target string if matched or an error if it was unable to match the string.

NOTE: MatchString only advances the scanner position if a valid match is successfully found.

func (*Scanner) ReadRune

func (s *Scanner) ReadRune() (rune, int, error)

ReadRune reads a single rune from the input text.

This method implements the io.RuneReader interface.

func (*Scanner) Remaining

func (s *Scanner) Remaining() string

Remaining returns the remaining unread portion of the input string.

func (*Scanner) UnreadRune

func (s *Scanner) UnreadRune() error

UnreadRune unreads the last read rune, the next call to ReadRune will return the just unread rune.

This method implements the io.RuneScanner interface along with ReadRune.

type Unit

type Unit struct{}

Unit type.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL