xsx

package module
v0.8.2 Latest Latest
Warning

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

Go to latest
Published: Nov 26, 2022 License: MIT Imports: 7 Imported by: 4

README

XSX-Logo – eXtended S-eXpressions

Test Coverage Go Report Card Go Reference

import "git.fractalqb.de/fractalqb/xsx"


Package XSX provides tools for parsing something I call eXtended S-eXpressions. Extended means the following things compared to SEXP S-expressions:

  1. Nested structures are delimited by a configurable set of balanced brackets e.g., '()', '[]' or '{}’ – not only by '()'.

  2. XSX provides a notation for "Meta Values", i.e. XSXs that provide some sort of out-of-band information.

On the other hand some properties from SEXP were dropped, e.g. typing of the so called "octet strings". Things like that are completely left to the application.

Somewhat more formal description

Frist of all, XSX is not about datatypes, in this it is comparable to e.g. XML (No! don't leave… its much simpler). Instead, its building block is the atom i.e., nothing else than a sequence of characters, aka a 'string'. Atoms come as quoted atoms and as unquoted atoms. An atom has to be quoted when the atom's string contains characters that have a special meaning in XSX: White-space, brackets, the quote and the meta character.

Regexp style definition of Atom
ws           := UNICODE whitespace
o-brackets   := “set of opening brackets” – e.g. '(' | '[' | '{'
c-brackets   := “set of closing brackets” – e.g. ')' | ']' | '}'
quote        := “the quote char”          – e.g. "
meta         := “the meta char”           – e.g. \
syntax-chars := o-brackets | c-brackets | quote | meta

atom     := nq-atom | q-atom
nq-atom  := (^(syntax-chars|ws))+
q-atom   := quote(quote{2}|^quote)*quote

I.e. x is an atom and foo, bar and baz are atoms. An atom that contains space, a '"' or '\' would be quoted. Also "(" is an atom but ( is not an atom.

Sequences now BNF Style

Each atom is an XSX and from XSX'es one can build sequences:

XSX      := atom | sequence
sequence := o-bracket {ws} c-bracket | o-bracket {ws} xsxs {ws} c-bracket
xsxs     := XSX | XSX {ws} xsxs
Out-Of-Band Information with Meta XSXs

You can prefix each XSX with the meta char to make that expression a meta-expression. A meta-expression is not considered to be an XSX, i.e. you cannot create meta-meta-expressions or meta-meta-meta-expressions… hmm… and not even meta-meta-meta-meta-expressions! I think it became clear?

The special case when the meta char does not prefix an XSX e.g., “\ ” or (\), makes the meta char the special meta token void. One might perhaps use it to denote tha absence of something.

E.g. \4711 is a meta-atom and \{foo 1 bar false baz 3.1415} is a meta-sequence. What meta means is completely up to the application. Imagine (div hiho) and (div \{class green} hiho) to be a translation from <div>hiho</div> and <div class="green">hiho</div>.

Rationale

None! … despite the fact that I found it to be fun – and useful in some situations. Because XSX syntax so simple it is easy to use for proprietary data files.

The first implementation was inspired by the expat streaming parser that allows one to push some data into the paring machinery and it will fire appropriate callbacks when tokes are detected. Reimplementing it as a simple pulling scanner simplified the code dramatically. With Go routines one can easily turn that into the streaming and event driven model again.

So, if you are looking for something that's even simpler than JSON or YAML you might give it a try… Happy coding!

Documentation

Overview

Package xsx provides tools for parsing so called eXtended S-eXpressions. Extended means the following things compared to https://people.csail.mit.edu/rivest/sexp.html:

Nested structures are delimited by balanced braces '()', '[]' or '{}’ – not only by '()'.

XSX provides a notation for "Meta Values", i.e. XSX that provide some sort of meta information that is not part of the "normal" data.

On the other hand some properties from SEXP were dropped, e.g. typing of the so called "octet strings". Things like that are completely left to the application.

Example
p := NewDefaultScanner(strings.NewReader(` "xyz"foo `))
var tok Token
var err error
for err = p.Next(&tok, true); err == nil; err = p.Next(&tok, true) {
	fmt.Printf("%s [%s] %t %t\n", tok.Type, tok.String(), tok.Meta, tok.Quoted)
}
fmt.Println(err)
Output:

Space [ ] false false
Atom [xyz] false true
Atom [foo] false false
Space [ ] false false
EOF

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ExpectModifier added in v0.8.1

type ExpectModifier int
const (
	IsMeta ExpectModifier = (1 << iota)
	NotMeta
	IsQuote
	NotQuote
)

type Expectation added in v0.8.1

type Expectation struct {
	// contains filtered or unexported fields
}
Example
var tok Token
scn := NewStringDefaultScanner("\\")
if err := scn.Next(&tok, false); err != nil {
	fmt.Println(err)
	return
}
ex := *Expect(&tok)
switch {
case ex.Begin(0, '('):
	fmt.Println("begin (")
case ex.Atom("foo", IsQuote|NotMeta):
	fmt.Println("found a \"foo\"")
default:
	fmt.Println(ex.Fails())
}
fmt.Println("Expectation failed:", ex.Failed())
Output:

[token is Void, not Begin] & [token is Void, not Atom]
Expectation failed: true

func Expect

func Expect(t *Token) *Expectation

func (*Expectation) AnyAtom added in v0.8.1

func (ex *Expectation) AnyAtom(mod ExpectModifier) bool

func (*Expectation) Atom added in v0.8.1

func (ex *Expectation) Atom(s string, mod ExpectModifier) bool

func (*Expectation) Begin added in v0.8.1

func (ex *Expectation) Begin(mod ExpectModifier, bs ...rune) bool

func (*Expectation) End added in v0.8.1

func (ex *Expectation) End() bool

func (*Expectation) Failed added in v0.8.1

func (ex *Expectation) Failed() bool

func (*Expectation) Fails added in v0.8.1

func (ex *Expectation) Fails() Fails

func (*Expectation) Reset added in v0.8.1

func (ex *Expectation) Reset(t *Token) *Expectation

func (*Expectation) Space added in v0.8.1

func (ex *Expectation) Space() bool

func (*Expectation) Void added in v0.8.1

func (ex *Expectation) Void() bool

type Fails added in v0.8.1

type Fails []error

func (Fails) Error added in v0.8.1

func (err Fails) Error() string

type Scanner

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

func NewDefaultScanner added in v0.8.0

func NewDefaultScanner(r io.Reader) *Scanner

func NewScanner

func NewScanner(r io.Reader, s Syntax) (scn *Scanner, err error)

func NewStringDefaultScanner added in v0.8.0

func NewStringDefaultScanner(str string) *Scanner

func NewStringScanner added in v0.8.0

func NewStringScanner(str string, s Syntax) (*Scanner, error)

func (*Scanner) MustNext added in v0.8.1

func (scn *Scanner) MustNext(tok *Token, space bool) bool

func (*Scanner) Next added in v0.8.0

func (scn *Scanner) Next(tok *Token, space bool) error

func (*Scanner) Syntax added in v0.8.0

func (scn *Scanner) Syntax() Syntax

type Syntax added in v0.8.0

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

func DefaultSyntax added in v0.8.0

func DefaultSyntax() Syntax

func NewSyntax added in v0.8.0

func NewSyntax(begin, end string, quote, meta rune) (Syntax, error)

func (Syntax) Begin added in v0.8.0

func (s Syntax) Begin() []rune

func (Syntax) End added in v0.8.0

func (s Syntax) End() []rune

func (Syntax) IsBegin added in v0.8.0

func (s Syntax) IsBegin(r rune) int

func (Syntax) IsEnd added in v0.8.0

func (s Syntax) IsEnd(r rune) int

func (Syntax) IsToken added in v0.8.0

func (s Syntax) IsToken(r rune) bool

func (Syntax) Meta added in v0.8.0

func (s Syntax) Meta() rune

func (Syntax) Quote added in v0.8.0

func (s Syntax) Quote() rune

func (Syntax) QuoteIf added in v0.8.0

func (syn Syntax) QuoteIf(s string) (string, bool)
Example
syn := DefaultSyntax()
fmt.Println(syn.QuoteIf("foo"))
fmt.Println(syn.QuoteIf("foo bar"))
fmt.Println(syn.QuoteIf("(foo)"))
fmt.Println(syn.QuoteIf("\\foo"))
fmt.Println(syn.QuoteIf(`a "foo"`))
Output:

foo false
"foo bar" true
"(foo)" true
"\foo" true
"a ""foo""" true

type Token

type Token struct {
	strings.Builder
	Type         TokenType
	Meta, Quoted bool
}

func (*Token) Rune added in v0.8.0

func (t *Token) Rune() rune

Returns unicode.MaxRune+1 if no rune is available

type TokenType added in v0.8.0

type TokenType int
const (
	Space TokenType = iota + 1
	Atom
	Begin
	End
	Void
)

func (TokenType) String added in v0.8.0

func (i TokenType) String() string

type Writer added in v0.8.0

type Writer struct {
	// contains filtered or unexported fields
}
Example
syn, _ := NewSyntax("<", ">", '\'', '^')
w, _ := NewWriter(os.Stdout, syn)
w.Begin(0, false)
w.Space("\n\t")
w.Atom("foo 'n' bar", true)
w.Space("\n\t")
w.Atom("4711", false)
w.Space("\n")
w.End()
w.Flush()
Output:

<
	^'foo ''n'' bar'
	4711
>

func NewDefaultWriter added in v0.8.0

func NewDefaultWriter(w io.Writer) *Writer

func NewWriter added in v0.8.0

func NewWriter(w io.Writer, s Syntax) (wr *Writer, err error)

func (*Writer) Atom added in v0.8.0

func (w *Writer) Atom(a string, meta bool) (quoted bool, n int, err error)

func (*Writer) Begin added in v0.8.0

func (w *Writer) Begin(b int, meta bool) (n int, err error)

func (*Writer) End added in v0.8.0

func (w *Writer) End() (int, error)

func (*Writer) Flush added in v0.8.0

func (w *Writer) Flush() error

func (*Writer) Space added in v0.8.0

func (w *Writer) Space(spc string) (n int, err error)

func (*Writer) Void added in v0.8.0

func (w *Writer) Void() (n int, err error)

Directories

Path Synopsis
Package gem provides a GEneric Model for XSX data
Package gem provides a GEneric Model for XSX data

Jump to

Keyboard shortcuts

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