parser

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 28, 2022 License: MIT Imports: 4 Imported by: 0

README

go-parser

Actions Status Coverage Status PkgGoDev go-report

A golang library to build a simple parser.

package main

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/Eun/go-parser"
)

func main() {
	// this example parses the git commit message

	tokens, err := parser.Lex(bytes.NewReader([]byte(`
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
1. Implemented awesome feature
2. Fixed a nasty bug
# not sure about this:
3. Optimization
`)))
	if err != nil {
		panic(err)
	}

	// 1. replace '\n' RuneTokens with a NewLineToken
	// this makes matching easier

	chain := []parser.MatchFunc{
		parser.Equal(parser.RuneToken{Rune: '\n'})(1, 1),
	}

	type NewLineToken struct{}

	tokens, err = parser.ReplaceTokens(tokens, chain, func(tokens []parser.Token) ([]parser.Token, error) {
		return []parser.Token{NewLineToken{}}, nil
	})
	if err != nil {
		panic(err)
	}

	// 2. replace all lines start with '#'
	chain = []parser.MatchFunc{
		parser.Equal(parser.RuneToken{Rune: '#'})(1, 0),
		parser.EqualType[parser.RuneToken](0, 0),
		parser.Equal(NewLineToken{})(1, 1),
	}

	type Comment struct {
		Text string
	}

	tokens, err = parser.ReplaceTokens(tokens, chain, func(tokens []parser.Token) ([]parser.Token, error) {
		// we could filter out the first '#' here
		// but let's skip this for simplicity
		var sb strings.Builder
		for _, token := range tokens {
			if t, ok := token.(parser.RuneToken); ok {
				sb.WriteRune(t.Rune)
			}
		}
		return []parser.Token{
			Comment{Text: sb.String()},
		}, nil
	})
	if err != nil {
		panic(err)
	}

	// 3. Everything that is left is the commit
	var sb strings.Builder
	for _, token := range tokens {
		switch t := token.(type) {
		case parser.RuneToken:
			sb.WriteRune(t.Rune)
		case NewLineToken:
			sb.WriteRune('\n')
		}
	}
	fmt.Println(strings.TrimSpace(sb.String()))
}

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Equal

func Equal(tkn Token) func(min, max int) MatchFunc

Types

type MatchFunc

type MatchFunc func([]Token) (bool, int)

func EqualType

func EqualType[T any](min, max int) MatchFunc

func Or

func Or(matchFuncs ...MatchFunc) MatchFunc

type ReplaceFunc

type ReplaceFunc func([]Token) ([]Token, error)

type RuneToken

type RuneToken struct {
	Rune rune
}

type Token

type Token any

func Lex

func Lex(r io.Reader) ([]Token, error)

func ReplaceTokens

func ReplaceTokens(tokens []Token, chain []MatchFunc, replace ReplaceFunc) ([]Token, error)
Example
// this example parses the git commit message

tokens, err := parser.Lex(bytes.NewReader([]byte(`
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
1. Implemented awesome feature
2. Fixed a nasty bug
# not sure about this:
3. Optimization
`)))
if err != nil {
	panic(err)
}

// 1. replace '\n' RuneTokens with a NewLineToken
// this makes matching easier

chain := []parser.MatchFunc{
	parser.Equal(parser.RuneToken{Rune: '\n'})(1, 1),
}

type NewLineToken struct{}

tokens, err = parser.ReplaceTokens(tokens, chain, func(tokens []parser.Token) ([]parser.Token, error) {
	return []parser.Token{NewLineToken{}}, nil
})
if err != nil {
	panic(err)
}

// 2. replace all lines start with '#'
chain = []parser.MatchFunc{
	parser.Equal(parser.RuneToken{Rune: '#'})(1, 0),
	parser.EqualType[parser.RuneToken](0, 0),
	parser.Equal(NewLineToken{})(1, 1),
}

type Comment struct {
	Text string
}

tokens, err = parser.ReplaceTokens(tokens, chain, func(tokens []parser.Token) ([]parser.Token, error) {
	// we could filter out the first '#' here
	// but let's skip this for simplicity
	var sb strings.Builder
	for _, token := range tokens {
		if t, ok := token.(parser.RuneToken); ok {
			sb.WriteRune(t.Rune)
		}
	}
	return []parser.Token{
		Comment{Text: sb.String()},
	}, nil
})
if err != nil {
	panic(err)
}

// 3. Everything that is left is the commit
var sb strings.Builder
for _, token := range tokens {
	switch t := token.(type) {
	case parser.RuneToken:
		sb.WriteRune(t.Rune)
	case NewLineToken:
		sb.WriteRune('\n')
	}
}
fmt.Println(strings.TrimSpace(sb.String()))
Output:

Jump to

Keyboard shortcuts

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