tabnashoover

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jun 18, 2026 License: MIT Imports: 2 Imported by: 0

README

hoover (Go)

A Go port of @tabnas/hoover, a syntax plugin for the tabnas parser engine that adds configurable block-delimited string parsing — hoovering up unquoted strings with internal spaces. Define custom string formats with start/end delimiters, escape sequences, and context-sensitive matching.

This port tracks the canonical TypeScript implementation in ../ts.

go get github.com/tabnas/hoover/go@latest

hoover's only dependency is the engine (github.com/tabnas/parser/go). The engine ships no grammar, and hoover is grammar-agnostic: it adds an alternate to the val rule, so register a grammar that defines val before the hoover plugin. If that rule is absent, hoover returns a clear error.

Documentation

  • Tutorial — zero to a working triple-quote parser.
  • How-to guide — escapes, trimming, delimiter consumption, rule-context matching.
  • Reference — every type, option, and the package API.
  • Concepts — how the matcher works, plus the differences from the TS version.

Quick example

package main

import (
    "fmt"

    tabnas "github.com/tabnas/parser/go"
    tabnashoover "github.com/tabnas/hoover/go"
)

func main() {
    j := tabnas.Make()
    j.Use(myGrammar) // your grammar plugin; must define the `val` rule
    j.UseDefaults(tabnashoover.Hoover, tabnashoover.Defaults, map[string]any{
        "block": []*tabnashoover.Block{
            {
                Name:  "triplequote",
                Start: tabnashoover.StartSpec{Fixed: []string{"'''"}},
                End:   tabnashoover.EndSpec{Fixed: []string{"'''"}},
            },
        },
    })

    out, err := j.Parse("'''hello world'''")
    if err != nil {
        panic(err)
    }
    fmt.Println(out) // hello world
}

For a minimal, runnable grammar to plug hoover into, see minigrammar_test.go — the tiny val + group grammar the test suite uses. The tutorial walks through it step by step.

License

MIT. Copyright (c) Richard Rodger and other contributors.

Documentation

Index

Constants

View Source
const Version = "0.2.0"

Variables

View Source
var Defaults = map[string]any{
	"lex": map[string]any{
		"order": 4500000,
	},
}

Defaults contains the default Hoover plugin options, matching TS Hoover.defaults. These are deep-merged with user-provided options by tabnas.UseDefaults().

View Source
var Hoover tabnas.Plugin = func(j *tabnas.Tabnas, opts map[string]any) (err error) {

	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("hoover: %v", r)
		}
	}()

	if val, ok := j.RSM()["val"]; !ok || val == nil || len(val.OpenAlts()) == 0 {
		return fmt.Errorf(
			"hoover: the 'val' rule is missing; register a grammar that defines it before the hoover plugin")
	}

	blockDefs, _ := opts["block"].([]*Block)
	action, _ := opts["action"].(tabnas.AltAction)

	blocks := buildBlocks(blockDefs)
	tokenMap := map[string]tabnas.Tin{}

	for _, block := range blocks {
		tin := j.Token(block.Token)
		block.tin = tin

		if _, exists := tokenMap[block.Token]; !exists {
			localTin := tin
			j.Rule("val", func(rs *tabnas.RuleSpec, _ *tabnas.Parser) {
				rs.PrependOpen(&tabnas.AltSpec{
					S: [][]tabnas.Tin{{localTin}},
					A: action,
				})
			})
		}
		tokenMap[block.Token] = tin
	}

	makeHooverMatcher := func(cfg *tabnas.LexConfig, _opts *tabnas.Options) tabnas.LexMatcher {
		var hooverMatcher tabnas.LexMatcher
		hooverMatcher = func(lex *tabnas.Lex, rule *tabnas.Rule) (tkn *tabnas.Token) {

			defer func() {
				if r := recover(); r != nil {
					tkn = lex.Bad("invalid_text")
				}
			}()
			for _, block := range blocks {
				pnt := lex.Cursor()

				hvpnt := &tabnas.Point{
					Len: pnt.Len,
					SI:  pnt.SI,
					RI:  pnt.RI,
					CI:  pnt.CI,
				}

				sr := matchStart(lex, hvpnt, block)

				if sr.match {
					result := parseToEnd(lex, hvpnt, block, cfg)

					if result.done {
						src := lex.Src[pnt.SI:hvpnt.SI]
						out := lex.Token(block.Token, block.tin, result.val, src)

						pnt.SI = hvpnt.SI
						pnt.RI = hvpnt.RI
						pnt.CI = hvpnt.CI

						return out
					}

					if result.err != "" {
						return lex.Bad(result.err)
					}
					return lex.Bad("invalid_text")
				}
			}
			return nil
		}
		return hooverMatcher
	}

	j.SetOptions(tabnas.Options{
		Lex: &tabnas.LexOptions{
			Match: map[string]*tabnas.MatchSpec{
				"hoover": {
					Order: lexOrder(opts),
					Make:  makeHooverMatcher,
				},
			},
		},
	})
	return nil
}

Hoover is the plugin function, matching the TS Hoover plugin. Use with tabnas.UseDefaults to apply Defaults automatically:

j.UseDefaults(tabnashoover.Hoover, tabnashoover.Defaults, map[string]any{
    "block": []*tabnashoover.Block{ ... },
})

Functions

This section is empty.

Types

type Block

type Block struct {
	Name               string
	Start              StartSpec
	End                EndSpec
	Token              string // Token name, default "#HV"
	EscapeChar         string
	Escape             map[string]string
	AllowUnknownEscape *bool // default true
	PreserveEscapeChar bool
	Trim               bool
	// contains filtered or unexported fields
}

Block defines a hoover block configuration.

type EndSpec

type EndSpec struct {
	Fixed   []string // End delimiter(s)
	Consume any      // bool or []string; nil = true
}

EndSpec defines how a block ends.

type HooverRuleFilter

type HooverRuleFilter struct {
	Include []string
	Exclude []string
}

HooverRuleFilter defines include/exclude lists for rule matching.

type HooverRuleSpec

type HooverRuleSpec struct {
	Parent  *HooverRuleFilter
	Current *HooverRuleFilter
	State   string // "o"/"c"/"oc" = check; "" (unset) defaults to "o" (no "don't check", unlike TS)
}

HooverRuleSpec defines rule context conditions for matching.

type StartSpec

type StartSpec struct {
	Fixed   []string        // Start delimiter(s)
	Consume any             // bool, *bool or []string; nil = true. A []string consumes only the listed delimiters.
	Rule    *HooverRuleSpec // Rule context matching
}

StartSpec defines how a block starts.

Jump to

Keyboard shortcuts

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