parse

package module
v0.0.0-...-c4fd10a Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2018 License: Apache-2.0 Imports: 7 Imported by: 12

README

parse

Sourcegraph GoDoc Build Status codecov rcard License

pratt parser framework, implements https://tdop.github.io/

  • the main parse loop: plugin in your lexer and token, we can parse anything
  • a look-ahead parser source can read byte by byte, or rune by rune
  • reusable parsing sub-routines to read or discard frequently used sequence types, like space, numeric

here is an example

src := parse.NewSourceString(`4/(1+1)+2`)
parsed := parse.Parse(src, newExprLexer(), 0)
fmt.Println(parsed) // 4

the parser implementation is very short

const precedenceAssignment = 1
const precedenceConditional = 2
const precedenceSum = 3
const precedenceProduct = 4
const precedenceExponent = 5
const precedencePrefix = 6
const precedencePostfix = 7
const precedenceCall = 8

type exprLexer struct {
	value    *valueToken
	plus     *plusToken
	minus    *minusToken
	multiply *multiplyToken
	divide   *divideToken
	group    *groupToken
}

var expr = newExprLexer()

func newExprLexer() *exprLexer {
	return &exprLexer{}
}

func (lexer *exprLexer) Parse(src *parse.Source, precedence int) interface{} {
	return parse.Parse(src, lexer, precedence)
}

func (lexer *exprLexer) InfixToken(src *parse.Source) (parse.InfixToken, int) {
	switch src.Peek1() {
	case '+':
		return lexer.plus, precedenceSum
	case '-':
		return lexer.minus, precedenceSum
	case '*':
		return lexer.multiply, precedenceProduct
	case '/':
		return lexer.divide, precedenceProduct
	default:
		return nil, 0
	}
}

func (lexer *exprLexer) PrefixToken(src *parse.Source) parse.PrefixToken {
	switch src.Peek1() {
	case '(':
		return lexer.group
	case '-':
		return lexer.minus
	default:
		return lexer.value
	}
}

type valueToken struct {
}

func (token *valueToken) PrefixParse(src *parse.Source) interface{} {
	return read.Int(src)
}

type plusToken struct {
}

func (token *plusToken) InfixParse(src *parse.Source, left interface{}) interface{} {
	leftValue := left.(int)
	src.Expect1('+')
	rightValue := expr.Parse(src, precedenceSum).(int)
	return leftValue + rightValue
}

type minusToken struct {
}

func (token *minusToken) PrefixParse(src *parse.Source) interface{} {
	src.Expect1('-')
	expr := expr.Parse(src, precedencePrefix).(int)
	return -expr
}

func (token *minusToken) InfixParse(src *parse.Source, left interface{}) interface{} {
	leftValue := left.(int)
	src.Expect1('-')
	rightValue := expr.Parse(src, precedenceSum).(int)
	return leftValue - rightValue
}

type multiplyToken struct {
}

func (token *multiplyToken) InfixParse(src *parse.Source, left interface{}) interface{} {
	leftValue := left.(int)
	src.Expect1('*')
	rightValue := expr.Parse(src, precedenceProduct).(int)
	return leftValue * rightValue
}

type divideToken struct {
}

func (token *divideToken) InfixParse(src *parse.Source, left interface{}) interface{} {
	leftValue := left.(int)
	src.Expect1('/')
	rightValue := expr.Parse(src, precedenceProduct).(int)
	return leftValue / rightValue
}

type groupToken struct {
}

func (token *groupToken) PrefixParse(src *parse.Source) interface{} {
	src.Expect1('(')
	expr := expr.Parse(src, 0)
	src.Expect1(')')
	return expr
}

Documentation

Index

Constants

View Source
const DefaultPrecedence = 1

DefaultPrecedence should be used when precedence does not matter

Variables

View Source
var InfoLogger = log.New(ioutil.Discard, "", 0)

InfoLogger is used to print informational message, default to off

Functions

func Parse

func Parse(src *Source, lexer Lexer, precedence int) interface{}

Parse parse the source with provided lexer, might call this recursively. If precedence > 0, some infix will be skipped due to precedence.

func String

func String(input string, lexer Lexer) (interface{}, error)

String parse the string with provided lexer

Types

type InfixToken

type InfixToken interface {
	InfixParse(src *Source, left interface{}) interface{}
}

InfixToken parse the source at infix position

type Lexer

type Lexer interface {
	PrefixToken(src *Source) PrefixToken
	InfixToken(src *Source) (InfixToken, int)
}

Lexer tell the current token in the head of source

type PrefixToken

type PrefixToken interface {
	PrefixParse(src *Source) interface{}
}

PrefixToken parse the source at prefix position

type Source

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

Source is generalization of io.Reader and []byte. It supports read ahead. It supports read byte by byte. It supports read unicode code point by code point (as rune or []byte). It supports savepoint and rollback.

func NewSource

func NewSource(reader io.Reader, bufLen int) (*Source, error)

NewSource construct a source from io.Reader. At least one byte should be read from the io.Reader, otherwise error will be returned.

func NewSourceString

func NewSourceString(str string) (*Source, error)

NewSourceString construct a source from string. Len should >= 1.

func (*Source) DeleteSavepoint

func (src *Source) DeleteSavepoint()

DeleteSavepoint delete the lastest savepoint

func (*Source) Error

func (src *Source) Error() error

Error tells if the source is in error condition. EOF is considered as error.

func (*Source) Expect

func (src *Source) Expect(expect []byte) bool

ExpectN like ReadN. bytes will not be consumed if not match

func (*Source) Expect1

func (src *Source) Expect1(b1 byte) bool

Expect1 like Read1 bytes will not be consumed if not match

func (*Source) Expect2

func (src *Source) Expect2(b1, b2 byte) bool

Expect2 like ReadN, with N == 2. bytes will not be consumed if not match

func (*Source) Expect3

func (src *Source) Expect3(b1, b2, b3 byte) bool

Expect3 like ReadN, with N == 3. bytes will not be consumed if not match

func (*Source) Expect4

func (src *Source) Expect4(b1, b2, b3, b4 byte) bool

Expect4 like ReadN, with N == 4. bytes will not be consumed if not match

func (*Source) FatalError

func (src *Source) FatalError() error

FatalError tells if the source is in fatal error condition

func (*Source) Peek

func (src *Source) Peek() []byte

Peek peeks as many bytes as possible without triggering consume

func (*Source) Peek1

func (src *Source) Peek1() byte

Peek1 return the first byte in the buffer to parse.

func (*Source) PeekAll

func (src *Source) PeekAll() []byte

PeekAll peek all of the rest bytes

func (*Source) PeekN

func (src *Source) PeekN(n int) []byte

PeekN return the first N bytes in the buffer to parse. If N is longer than current buffer, it will read from reader. The cursor will not be moved.

func (*Source) PeekRune

func (src *Source) PeekRune() (rune, int)

PeekRune read unicode code point as rune, without moving cursor.

func (*Source) PeekUtf8

func (src *Source) PeekUtf8() []byte

PeekUtf8 read one full code point without decoding into rune

func (*Source) Read1

func (src *Source) Read1() byte

Read1 like ConsumeN, with N == 1

func (*Source) ReadAll

func (src *Source) ReadAll() []byte

ReadAll read all bytes until EOF

func (*Source) ReadByte

func (src *Source) ReadByte() (byte, error)

ReadByte is the same as Read1, just for implementing the io.ByteReader interface

func (*Source) ReadN

func (src *Source) ReadN(n int) []byte

ReadN read N bytes and move cursor forward

func (*Source) ReportError

func (src *Source) ReportError(err error)

ReportError set the source in error condition.

func (*Source) ResetError

func (src *Source) ResetError()

ResetError clear the error

func (*Source) RollbackToSavepoint

func (src *Source) RollbackToSavepoint()

RollbackToSavepoint rollback the cursor to previous savepoint. The bytes read from reader will be replayed.

func (*Source) StoreSavepoint

func (src *Source) StoreSavepoint()

StoreSavepoint mark current position, and start recording. Later we can rollback to current position. Make sure there's no error, rollback will clear the error

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

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