zclsyntax: Start to stub out the zclsyntax package

This package will grow to contain all of the gory details of the native
zcl syntax, including it AST, parser, etc. Most callers should access
this via the simpler API in the top-level package, which then gives
automatic support for other syntaxes too.
This commit is contained in:
Martin Atkins 2017-05-23 08:05:44 -07:00
parent 1535d4b708
commit e957bff8de
6 changed files with 212 additions and 0 deletions

37
zcl/zclsyntax/body.go Normal file
View File

@ -0,0 +1,37 @@
package zclsyntax
import (
"github.com/apparentlymart/go-zcl/zcl"
)
// Body is the implementation of zcl.Body for the zcl native syntax.
type Body struct {
SrcRange zcl.Range
}
// Assert that *Body implements zcl.Body
var assertBodyImplBody zcl.Body = &Body{}
func (b *Body) walkChildNodes(w internalWalkFunc) {
// Nothing to walk yet
}
func (b *Body) Range() zcl.Range {
return b.SrcRange
}
func (b *Body) Content(schema *zcl.BodySchema) (*zcl.BodyContent, zcl.Diagnostics) {
panic("Body.Content not yet implemented")
}
func (b *Body) PartialContent(schema *zcl.BodySchema) (*zcl.BodyContent, zcl.Body, zcl.Diagnostics) {
panic("Body.PartialContent not yet implemented")
}
func (b *Body) JustAttributes() (zcl.Attributes, zcl.Diagnostics) {
panic("Body.JustAttributes not yet implemented")
}
func (b *Body) MissingItemRange() zcl.Range {
panic("Body.MissingItemRange not yet implemented")
}

7
zcl/zclsyntax/doc.go Normal file
View File

@ -0,0 +1,7 @@
// Package zclsyntax contains the parser, AST, etc for zcl's native language,
// as opposed to the JSON variant and the HCL/HIL shim.
//
// In normal use applications should rarely depend on this package directly,
// instead preferring the higher-level interface of the main zcl page and
// its companion zclparse.
package zclsyntax

View File

@ -0,0 +1,51 @@
package zclsyntax
import (
"github.com/apparentlymart/go-cty/cty"
"github.com/apparentlymart/go-zcl/zcl"
)
// Expression is the abstract type for nodes that behave as zcl expressions.
type Expression interface {
Node
// The zcl.Expression methods are duplicated here, rather than simply
// embedded, because both Node and zcl.Expression have a Range method
// and so they conflict.
Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics)
Variables() []zcl.Traversal
StartRange() zcl.Range
}
// Assert that Expression implements zcl.Expression
var assertExprImplExpr zcl.Expression = Expression(nil)
// LiteralValueExpr is an expression that just always returns a given value.
type LiteralValueExpr struct {
Val cty.Value
SrcRange zcl.Range
hasNoVariables
}
func (e *LiteralValueExpr) walkChildNodes(w internalWalkFunc) {
// Literal values have no child nodes
}
func (e *LiteralValueExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
return e.Val, nil
}
func (e *LiteralValueExpr) Range() zcl.Range {
return e.SrcRange
}
// Embed this in an expression struct to get a default implementation of
// Variables that returns no variables.
type hasNoVariables struct {
}
func (e hasNoVariables) Variables() []zcl.Traversal {
return nil
}

18
zcl/zclsyntax/file.go Normal file
View File

@ -0,0 +1,18 @@
package zclsyntax
import "github.com/apparentlymart/go-zcl/zcl"
// File is the top-level object resulting from parsing a configuration file.
type File struct {
Body *Body
Bytes []byte
}
func (f *File) AsZCLFile() *zcl.File {
return &zcl.File{
Body: f.Body,
Bytes: f.Bytes,
// TODO: The Nav object, once we have an implementation of it
}
}

22
zcl/zclsyntax/node.go Normal file
View File

@ -0,0 +1,22 @@
package zclsyntax
import (
"github.com/apparentlymart/go-zcl/zcl"
)
// Node is the abstract type that every AST node implements.
//
// This is a closed interface, so it cannot be implemented from outside of
// this package.
type Node interface {
// This is the mechanism by which the public-facing walk functions
// are implemented. Implementations should call the given function
// for each child node and then replace that node with its return value.
// The return value might just be the same node, for non-transforming
// walks.
walkChildNodes(w internalWalkFunc)
Range() zcl.Range
}
type internalWalkFunc func(Node) Node

77
zcl/zclsyntax/walk.go Normal file
View File

@ -0,0 +1,77 @@
package zclsyntax
import (
"github.com/apparentlymart/go-zcl/zcl"
)
// VisitFunc is the callback signature for VisitAll.
type VisitFunc func(node Node) zcl.Diagnostics
// VisitAll is a basic way to traverse the AST beginning with a particular
// node. The given function will be called once for each AST node in
// depth-first order, but no context is provided about the shape of the tree.
//
// The VisitFunc may return diagnostics, in which case they will be accumulated
// and returned as a single set.
func VisitAll(node Node, f VisitFunc) zcl.Diagnostics {
diags := f(node)
node.walkChildNodes(func(node Node) Node {
diags = append(diags, VisitAll(node, f)...)
return node
})
return diags
}
// Walker is an interface used with Walk.
type Walker interface {
Enter(node Node) zcl.Diagnostics
Exit(node Node) zcl.Diagnostics
}
// Walk is a more complex way to traverse the AST starting with a particular
// node, which provides information about the tree structure via separate
// Enter and Exit functions.
func Walk(node Node, w Walker) zcl.Diagnostics {
diags := w.Enter(node)
node.walkChildNodes(func(node Node) Node {
diags = append(diags, Walk(node, w)...)
return node
})
return diags
}
// Transformer is an interface used with Transform
type Transformer interface {
// Transform accepts a node and returns a replacement node along with
// a flag for whether to also visit child nodes. If the flag is false,
// none of the child nodes will be visited and the TransformExit method
// will not be called for the node.
//
// It is acceptable and appropriate for Transform to return the same node
// it was given, for situations where no transform is needed.
Transform(node Node) (Node, bool, zcl.Diagnostics)
// TransformExit signals the end of transformations of child nodes of the
// given node. If Transform returned a new node, the given node is the
// node that was returned, rather than the node that was originally
// encountered.
TransformExit(node Node) zcl.Diagnostics
}
// Transform allows for in-place transformations of an AST starting with a
// particular node. The provider Transformer implementation drives the
// transformation process. The return value is the node that replaced the
// given top-level node.
func Transform(node Node, t Transformer) (Node, zcl.Diagnostics) {
newNode, descend, diags := t.Transform(node)
if !descend {
return newNode, diags
}
node.walkChildNodes(func(node Node) Node {
newNode, newDiags := Transform(node, t)
diags = append(diags, newDiags...)
return newNode
})
diags = append(diags, t.TransformExit(newNode)...)
return newNode, diags
}