Documentation
¶
Index ¶
- func Beautify(err error, getcont func(string) []byte, ctx *SourceContext, colors bool) string
- func DontPanic(f func() error) (err error)
- type Helper
- func (h Helper) Bool() (bool, error)
- func (h Helper) Child(n int) Helper
- func (h Helper) Float64() (float64, error)
- func (h Helper) Int() (int, error)
- func (h Helper) IsList() bool
- func (h Helper) IsScalar() bool
- func (h Helper) IsValid() bool
- func (h Helper) MustBool() bool
- func (h Helper) MustFloat64() float64
- func (h Helper) MustInt() int
- func (h Helper) MustNode() *Node
- func (h Helper) MustString() string
- func (h Helper) Next() Helper
- func (h Helper) Node() (*Node, error)
- func (h Helper) String() (string, error)
- type Node
- func (n *Node) IsList() bool
- func (n *Node) IsScalar() bool
- func (n *Node) IterKeyValues(f func(k, v *Node) error) error
- func (n *Node) Nth(num int) (*Node, error)
- func (n *Node) NumChildren() int
- func (n *Node) String() string
- func (n *Node) Unmarshal(vals ...interface{}) (err error)
- func (n *Node) UnmarshalChildren(vals ...interface{}) (err error)
- type ParseError
- type SourceContext
- type SourceFile
- type SourceLoc
- type SourceLocEx
- type UnmarshalError
- type Unmarshaler
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Beautify ¶
Returns a prettified version of the `err`.
The arguments need explanation, if you get a parser error (returned by one of the Parse functions) or unmarshaling error (returned by one of the Node methods), it can be prettified given that you have access to the SourceContext used in parsing and the source data.
You need to provide a closure `getcont` which will return contents of the source file for a given filename argument. The reason for this complicated interface is because SourceContext supports multiple files and it's not necessarly clear where the error is.
Colors argument specified whether you want to use colors or not. It applies typical terminal escape sequnces to the resulting string in case if the argument is true.
It will prettify only ParseError or UnmarshalError errors, if something else is given it will return error.Error() output.
Example ¶
package main import ( "fmt" "github.com/nsf/sexp" "strings" ) func main() { const example_sexp = ` (correct syntax) ( ; oops, no enclosing ')' here ` var ctx sexp.SourceContext f := ctx.AddFile("example.sexp", -1) _, err := sexp.Parse(strings.NewReader(example_sexp), f) if err != nil { // we know the contents of the only source file used, let's // just return it: getcont := func(string) []byte { return []byte(example_sexp) } fmt.Println(sexp.Beautify(err, getcont, &ctx, false)) } }
Output: example.sexp:3:3: error: missing matching sequence delimiter ')' ( ; oops, no enclosing ')' here ↑
Types ¶
type Helper ¶
type Helper struct {
// contains filtered or unexported fields
}
A simple helper structure inspired by the simplejson-go API. Use Help function to actually acquire it from the given *Node.
func (Helper) MustFloat64 ¶
func (Helper) MustString ¶
type Node ¶
The main and only AST structure. All fields are self explanatory, however the way they are being formed needs explanation.
A list node has empty value and non-nil children pointer, which is a nil-terminated list of children nodes.
A scalar node has nil children pointer.
Take a look at this example:
((1 2) 3 4)
will yield:
Node{Children: Node{Children: Node{Value: "1", Next: Node{Value: "2"}}, Next: Node{Value: "3", Next: Node{Value: "4"}}}}
func Parse ¶
func Parse(r io.RuneReader, f *SourceFile) (*Node, error)
Parses S-expressions from a given io.RuneReader.
Returned node is a virtual list node with all the S-expressions read from the stream as children. In case of a syntax error, the returned error is not nil.
It's worth explaining where do you get *SourceFile from. The typical way to create it is:
var ctx SourceContext f := ctx.AddFile(filename, length)
And you'll be able to use ctx later for decoding source location information. It's ok to provide -1 as length if it's unknown. In that case though you won't be able to add more files to the given SourceContext until the file with unknown length is finalized, which happens when parsing is finished.
Also f is optional, nil is a perfectly valid argument for it, in that case it will create a temporary context and add an unnamed file to it. Less setup work is required, but you lose the ability to decode error source code locations.
func ParseOne ¶
func ParseOne(r io.RuneScanner, f *SourceFile) (*Node, error)
Parses a single S-expression node from a stream.
Returns just one node, be it a value or a list, doesn't touch the rest of the data. In case of a syntax error, the returned error is not nil.
Note that unlike Parse it requires io.RuneScanner. It's a technical requirement, because in some cases s-expressions syntax delimiter is not part of the s-expression value, like in a very simple example: "x y". "x" here will be returned as a value Node, but " y" should remain untouched, however without reading the space character we can't tell if this is the end of "x" or not. Hence the requirement of being able to unread one rune.
It's unclear what to do about error reporting for S-expressions read from the stream. The usual idea of lines and columns doesn't apply here. Hence if you do want to report errors gracefully some hacks will be necessary to do so.
NOTE: Maybe ParseOne will be changed in future to better serve the need of good error reporting.
func (*Node) IterKeyValues ¶
Walk over children nodes, assuming they are key/value pairs. It returns error if the iterable node is not a list or if any of its children is not a key/value pair.
func (*Node) NumChildren ¶
Returns the number of children nodes. Has O(N) complexity.
func (*Node) Unmarshal ¶
Unmarshals the node and its siblings to pointer values.
The function expects pointers to values with arbitrary types. If one of the arguments is not a pointer it will panic.
It supports unmarshaling to the following types:
- all number types: int{8,16,32,64}, uint{8,16,32,64}, float{32,64}
- bool
- string
- arrays and slices of all supported types
- empty interfaces{}
- maps
- structs
- pointers to any of the supported types (only one level of indirection)
- any type which implements Unmarshaler
Here's some details on unmarshaling semantics:
(u)ints: unmarshaled using strconv.ParseInt/strconv.ParseUint with base 10 only floats: unmarshaled using strconv.ParseFloat bool: works strictly on two values "true" or "false" string: unmarshaled as is (keep in mind that lexer supports escape sequences) arrays: uses up to len(array) elements, if there is a smaller amount of elements, the rest is zeroed slices: uses all elements appending them to the slice, however if the slice was bigger than the amount of elements, it will reslice it to the appropriate length iface: only empty interfaces are supported, it will unmarshal AST to a []interface{} or a string map: when unmarshaling to the map, assumes this AST form: `((key value) (key value) (key value))`, doesn't clear the map before appending all the key value pairs struct: uses the same AST form as the map, where `key` means `field`, supports `sexp` tags (see description below), will try to match name specified in the tag, the field name and the field name ignoring the case in that order
Struct tags have the form: "name,opt,opt". Special tag "-" means "skip me". Supported options:
siblings: will use sibling nodes instead of children for unmarshaling to an array or a slice.
Important note: If the type implements Unmarshaler interface, it will use it instead of applying default unmarshaling strategies described above.
Example ¶
package main import ( "fmt" "github.com/nsf/sexp" "strings" ) func main() { const example_sexp = ` (position 5 10 4.7) (target 10 -2.4 30.3) ` var example struct { Pos [3]float32 `sexp:"position,siblings"` Tgt [3]float32 `sexp:"target,siblings"` } ast, err := sexp.Parse(strings.NewReader(example_sexp), nil) if err != nil { fmt.Println(err) return } err = ast.Unmarshal(&example) if err != nil { fmt.Println(err) return } fmt.Println(example.Pos) fmt.Println(example.Tgt) }
Output: [5 10 4.7] [10 -2.4 30.3]
func (*Node) UnmarshalChildren ¶
Unmarshals all children nodes of the node to pointer values. Applies the same logic as Unmarshal. See description of the (*Node).Unmarshal method for more details.
type ParseError ¶
type ParseError struct { Location SourceLoc // contains filtered or unexported fields }
This error structure is Parse* functions family specific, it returns information about errors encountered during parsing. Location can be decoded using the context you passed in as an argument. If the context was nil, then the location is simply a byte offset from the beginning of the input stream.
func (*ParseError) Error ¶
func (e *ParseError) Error() string
Satisfy the built-in error interface. Returns the error message (without source location).
type SourceContext ¶
type SourceContext struct {
// contains filtered or unexported fields
}
Source context holds information needed to decompress source locations. It supports multiple files with knowns and unknowns lengths. Although having a file with unknown length prevents you from adding more files until it's been finalized.
func (*SourceContext) AddFile ¶
func (s *SourceContext) AddFile(filename string, length int) *SourceFile
Adds a new file to the context, use -1 as length if the length is unknown, but keep in mind that having a file with unknown length prevents further AddFile calls, they will panic. In order to continue adding files to the context, the last file with unknown length must be finalized. Method doesn't read anything, all the arguments are purely informative.
func (*SourceContext) Decode ¶
func (s *SourceContext) Decode(loc SourceLoc) SourceLocEx
Decodes an encoded source location.
type SourceFile ¶
type SourceFile struct {
// contains filtered or unexported fields
}
Represents one file within source context, usually a parser will require you to pass source file before parsing. Parser should use SourceFile.Encode method to encode source location information, method takes byte offset from the beginning of the file as an argument.
func (*SourceFile) AddLine ¶
func (f *SourceFile) AddLine(offset int)
Adds a new line with a given offset, keep in mind that the first line is added automatically by SourceContext.AddFile. A parser typically calls that method each time it encounters a newline character.
func (*SourceFile) Encode ¶
func (f *SourceFile) Encode(offset int) SourceLoc
Encodes an offset from the beginning of the file as a source location.
func (*SourceFile) Finalize ¶
func (f *SourceFile) Finalize(len int)
If the length of the file is unknown at the beginning, the file must be finalized at some point using this method. Otherwise no new files can be added to the source context.
type SourceLoc ¶
type SourceLoc uint32
Compressed SourceLocEx, can be decoded using an appropriate SourceContext.
type SourceLocEx ¶
type SourceLocEx struct { Filename string Line int // starting from 1 LineOffset int // offset to the beginning of the line (in bytes) Offset int // offset to the location (in bytes) }
Complete source location information. Line number starts from 1, it is a traditional choice. The column is not specified, but you can find it by counting runes between LineOffset and Offset within the source file this location belongs to.
type UnmarshalError ¶
type UnmarshalError struct { Type reflect.Type Node *Node // contains filtered or unexported fields }
func NewUnmarshalError ¶
func NewUnmarshalError(n *Node, t reflect.Type, format string, args ...interface{}) *UnmarshalError
func (*UnmarshalError) Error ¶
func (e *UnmarshalError) Error() string