Initial stubs of the public API
This commit is contained in:
parent
bde4c176b9
commit
b9183e85e4
63
zcl/diagnostic.go
Normal file
63
zcl/diagnostic.go
Normal file
@ -0,0 +1,63 @@
|
||||
package zcl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// DiagnosticSeverity represents the severity of a diagnostic.
|
||||
type DiagnosticSeverity int
|
||||
|
||||
const (
|
||||
// DiagInvalid is the invalid zero value of DiagnosticSeverity
|
||||
DiagInvalid DiagnosticSeverity = iota
|
||||
|
||||
// DiagError indicates that the problem reported by a diagnostic prevents
|
||||
// further progress in parsing and/or evaluating the subject.
|
||||
DiagError
|
||||
|
||||
// DiagWarning indicates that the problem reported by a diagnostic warrants
|
||||
// user attention but does not prevent further progress. It is most
|
||||
// commonly used for showing deprecation notices.
|
||||
DiagWarning
|
||||
)
|
||||
|
||||
// Diagnostic represents information to be presented to a user about an
|
||||
// error or anomoly in parsing or evaluating configuration.
|
||||
type Diagnostic struct {
|
||||
Severity DiagnosticSeverity
|
||||
|
||||
// Summary and detail contain the English-language description of the
|
||||
// problem. Summary is a terse description of the general problem and
|
||||
// detail is a more elaborate, often-multi-sentence description of
|
||||
// the probem and what might be done to solve it.
|
||||
Summary string
|
||||
Detail string
|
||||
Subject *Range
|
||||
Context *Range
|
||||
}
|
||||
|
||||
// Diagnostics is a list of Diagnostic instances.
|
||||
type Diagnostics []Diagnostic
|
||||
|
||||
// error implementation, so that diagnostics can be returned via APIs
|
||||
// that normally deal in vanilla Go errors.
|
||||
//
|
||||
// This presents only minimal context about the error, for compatibility
|
||||
// with usual expectations about how errors will present as strings.
|
||||
func (d *Diagnostic) Error() string {
|
||||
return fmt.Sprintf("%s: %s", d.Subject.Start, d.Summary)
|
||||
}
|
||||
|
||||
// error implementation, so that sets of diagnostics can be returned via
|
||||
// APIs that normally deal in vanilla Go errors.
|
||||
func (d Diagnostics) Error() string {
|
||||
count := len(d)
|
||||
switch {
|
||||
case count == 0:
|
||||
return "no diagnostics"
|
||||
case count == 1:
|
||||
return d[0].Error()
|
||||
default:
|
||||
return fmt.Sprintf("%s, and %d other diagnostic(s)", d[0].Error(), count-1)
|
||||
}
|
||||
}
|
1
zcl/doc.go
Normal file
1
zcl/doc.go
Normal file
@ -0,0 +1 @@
|
||||
package zcl
|
88
zcl/pos.go
Normal file
88
zcl/pos.go
Normal file
@ -0,0 +1,88 @@
|
||||
package zcl
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Pos represents a single position in a source file, by addressing the
|
||||
// start byte of a unicode character encoded in UTF-8.
|
||||
//
|
||||
// Pos is generally used only in the context of a Range, which then defines
|
||||
// which source file the position is within.
|
||||
type Pos struct {
|
||||
// Line is the source code line where this position points. Lines are
|
||||
// counted starting at 1 and incremented for each newline character
|
||||
// encountered.
|
||||
Line int
|
||||
|
||||
// Column is the source code column where this position points, in
|
||||
// unicode characters, with counting starting at 1.
|
||||
//
|
||||
// Column counts characters as they appear visually, so for example a
|
||||
// latin letter with a combining diacritic mark counts as one character.
|
||||
// This is intended for rendering visual markers against source code in
|
||||
// contexts where these diacritics would be rendered in a single character
|
||||
// cell. Technically speaking, Column is counting grapheme clusters as
|
||||
// used in unicode normalization.
|
||||
Column int
|
||||
|
||||
// Byte is the byte offset into the file where the indicated character
|
||||
// begins. This is a zero-based offset to the first byte of the first
|
||||
// UTF-8 codepoint sequence in the character, and thus gives a position
|
||||
// that can be resolved _without_ awareness of Unicode characters.
|
||||
Byte int
|
||||
}
|
||||
|
||||
// Range represents a span of characters between two positions in a source
|
||||
// file.
|
||||
//
|
||||
// This struct is usually used by value in types that represent AST nodes,
|
||||
// but by pointer in types that refer to the positions of other objects,
|
||||
// such as in diagnostics.
|
||||
type Range struct {
|
||||
// Filename is the name of the file into which this range's positions
|
||||
// point.
|
||||
Filename string
|
||||
|
||||
// Start and End represent the bounds of this range. Start is inclusive
|
||||
// and End is exclusive.
|
||||
Start, End Pos
|
||||
}
|
||||
|
||||
// RangeBetween returns a new range that spans from the beginning of the
|
||||
// start range to the end of the end range.
|
||||
//
|
||||
// The result is meaningless if the two ranges do not belong to the same
|
||||
// source file or if the end range appears before the start range.
|
||||
func RangeBetween(start, end Range) Range {
|
||||
return Range{
|
||||
Filename: start.Filename,
|
||||
Start: start.Start,
|
||||
End: end.End,
|
||||
}
|
||||
}
|
||||
|
||||
// ContainsOffset returns true if and only if the given byte offset is within
|
||||
// the receiving Range.
|
||||
func (r *Range) ContainsOffset(offset int) bool {
|
||||
return offset >= r.Start.Byte && offset < r.End.Byte
|
||||
}
|
||||
|
||||
// String returns a compact string representation of the receiver.
|
||||
// Callers should generally prefer to present a range more visually,
|
||||
// e.g. via markers directly on the relevant portion of source code.
|
||||
func (r *Range) String() string {
|
||||
if r.Start.Line == r.End.Line {
|
||||
return fmt.Sprintf(
|
||||
"%s:%d,%d-%d",
|
||||
r.Filename,
|
||||
r.Start.Line, r.Start.Column,
|
||||
r.End.Column,
|
||||
)
|
||||
} else {
|
||||
return fmt.Sprintf(
|
||||
"%s:%d,%d-%d,%d",
|
||||
r.Filename,
|
||||
r.Start.Line, r.Start.Column,
|
||||
r.End.Line, r.End.Column,
|
||||
)
|
||||
}
|
||||
}
|
15
zcl/schema.go
Normal file
15
zcl/schema.go
Normal file
@ -0,0 +1,15 @@
|
||||
package zcl
|
||||
|
||||
// ElementHeaderSchema represents the shape of an element header, and is
|
||||
// used for matching elements within bodies.
|
||||
type ElementHeaderSchema struct {
|
||||
Name string
|
||||
LabelNames []string
|
||||
Single bool
|
||||
}
|
||||
|
||||
// BodySchema represents the desired shallow structure of a body.
|
||||
type BodySchema struct {
|
||||
Attributes []string
|
||||
Elements []ElementHeaderSchema
|
||||
}
|
95
zcl/structure.go
Normal file
95
zcl/structure.go
Normal file
@ -0,0 +1,95 @@
|
||||
package zcl
|
||||
|
||||
import (
|
||||
"github.com/apparentlymart/go-cty/cty"
|
||||
)
|
||||
|
||||
// File is the top-level node that results from parsing a ZCL file.
|
||||
type File struct {
|
||||
Body Body
|
||||
}
|
||||
|
||||
// Element represents a nested block within a Body.
|
||||
type Element struct {
|
||||
Type string
|
||||
Labels []string
|
||||
Body Body
|
||||
|
||||
DefRange Range // Range that can be considered the "definition" for seeking in an editor
|
||||
TypeRange Range // Range for the element type declaration specifically.
|
||||
LabelRanges []Range // Ranges for the label values specifically.
|
||||
}
|
||||
|
||||
// Elements is a sequence of Element.
|
||||
type Elements []*Element
|
||||
|
||||
// Body is a container for attributes and elements. It serves as the primary
|
||||
// unit of heirarchical structure within configuration.
|
||||
//
|
||||
// The content of a body cannot be meaningfully intepreted without a schema,
|
||||
// so Body represents the raw body content and has methods that allow the
|
||||
// content to be extracted in terms of a given schema.
|
||||
type Body interface {
|
||||
// Content verifies that the entire body content conforms to the given
|
||||
// schema and then returns it, and/or returns diagnostics. The returned
|
||||
// body content is valid if non-nil, regardless of whether Diagnostics
|
||||
// are provided, but diagnostics should still be eventually shown to
|
||||
// the user.
|
||||
Content(schema *BodySchema) (*BodyContent, Diagnostics)
|
||||
|
||||
// PartialContent is like Content except that it permits the configuration
|
||||
// to contain additional elements or attributes not specified in the
|
||||
// schema. If any are present, the returned Body is non-nil and contains
|
||||
// the remaining items from the body that were not selected by the schema.
|
||||
PartialContent(schema *BodySchema) (*BodyContent, Body, Diagnostics)
|
||||
}
|
||||
|
||||
// BodyContent is the result of applying a BodySchema to a Body.
|
||||
type BodyContent struct {
|
||||
Attributes map[string]Attribute
|
||||
Elements Elements
|
||||
}
|
||||
|
||||
// Attribute represents an attribute from within a body.
|
||||
type Attribute struct {
|
||||
Name string
|
||||
Expr Expression
|
||||
|
||||
Range Range
|
||||
NameRange Range
|
||||
ExprRange Range
|
||||
}
|
||||
|
||||
// Expression is a literal value or an expression provided in the
|
||||
// configuration, which can be evaluated within a scope to produce a value.
|
||||
type Expression interface {
|
||||
LiteralValue() cty.Value
|
||||
// TODO: evaluation of non-literal expressions
|
||||
}
|
||||
|
||||
// OfType filters the receiving element sequence by element type name,
|
||||
// returning a new element sequence including only the elements of the
|
||||
// requested type.
|
||||
func (els Elements) OfType(typeName string) Elements {
|
||||
ret := make(Elements, 0)
|
||||
for _, el := range els {
|
||||
if el.Type == typeName {
|
||||
ret = append(ret, el)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// ByType transforms the receiving elements sequence into a map from type
|
||||
// name to element sequences of only that type.
|
||||
func (els Elements) ByType() map[string]Elements {
|
||||
ret := make(map[string]Elements)
|
||||
for _, el := range els {
|
||||
ty := el.Type
|
||||
if ret[ty] == nil {
|
||||
ret[ty] = make(Elements, 0, 1)
|
||||
}
|
||||
ret[ty] = append(ret[ty], el)
|
||||
}
|
||||
return ret
|
||||
}
|
Loading…
Reference in New Issue
Block a user