mirror of
https://github.com/google/go-jsonnet.git
synced 2025-08-07 14:57:24 +02:00
524 lines
12 KiB
Go
524 lines
12 KiB
Go
/*
|
|
Copyright 2016 Google Inc. All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package jsonnet
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// identifier represents a variable / parameter / field name.
|
|
//+gen set
|
|
type identifier string
|
|
type identifiers []identifier
|
|
|
|
// TODO(jbeda) implement interning of identifiers if necessary. The C++
|
|
// version does so.
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type astNode interface {
|
|
Loc() *LocationRange
|
|
FreeVariables() identifiers
|
|
}
|
|
type astNodes []astNode
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type astNodeBase struct {
|
|
loc LocationRange
|
|
freeVariables identifiers
|
|
}
|
|
|
|
func (n *astNodeBase) Loc() *LocationRange {
|
|
return &n.loc
|
|
}
|
|
|
|
func (n *astNodeBase) FreeVariables() identifiers {
|
|
return n.freeVariables
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// +gen stringer
|
|
type astCompKind int
|
|
|
|
const (
|
|
astCompFor astCompKind = iota
|
|
astCompIf
|
|
)
|
|
|
|
type astCompSpec struct {
|
|
kind astCompKind
|
|
varName *identifier // nil when kind != compSpecFor
|
|
expr astNode
|
|
}
|
|
type astCompSpecs []astCompSpec
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astApply represents a function call
|
|
type astApply struct {
|
|
astNodeBase
|
|
target astNode
|
|
arguments astNodes
|
|
trailingComma bool
|
|
tailStrict bool
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astApplyBrace represents e { }. Desugared to e + { }.
|
|
type astApplyBrace struct {
|
|
astNodeBase
|
|
left astNode
|
|
right astNode
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astArray represents array constructors [1, 2, 3].
|
|
type astArray struct {
|
|
astNodeBase
|
|
elements astNodes
|
|
trailingComma bool
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astArrayComp represents array comprehensions (which are like Python list
|
|
// comprehensions)
|
|
type astArrayComp struct {
|
|
astNodeBase
|
|
body astNode
|
|
trailingComma bool
|
|
specs astCompSpecs
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astAssert represents an assert expression (not an object-level assert).
|
|
//
|
|
// After parsing, message can be nil indicating that no message was
|
|
// specified. This AST is elimiated by desugaring.
|
|
type astAssert struct {
|
|
astNodeBase
|
|
cond astNode
|
|
message astNode
|
|
rest astNode
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type binaryOp int
|
|
|
|
const (
|
|
bopMult binaryOp = iota
|
|
bopDiv
|
|
bopPercent
|
|
|
|
bopPlus
|
|
bopMinus
|
|
|
|
bopShiftL
|
|
bopShiftR
|
|
|
|
bopGreater
|
|
bopGreaterEq
|
|
bopLess
|
|
bopLessEq
|
|
|
|
bopManifestEqual
|
|
bopManifestUnequal
|
|
|
|
bopBitwiseAnd
|
|
bopBitwiseXor
|
|
bopBitwiseOr
|
|
|
|
bopAnd
|
|
bopOr
|
|
)
|
|
|
|
var bopStrings = []string{
|
|
bopMult: "*",
|
|
bopDiv: "/",
|
|
bopPercent: "%",
|
|
|
|
bopPlus: "+",
|
|
bopMinus: "-",
|
|
|
|
bopShiftL: "<<",
|
|
bopShiftR: ">>",
|
|
|
|
bopGreater: ">",
|
|
bopGreaterEq: ">=",
|
|
bopLess: "<",
|
|
bopLessEq: "<=",
|
|
|
|
bopManifestEqual: "==",
|
|
bopManifestUnequal: "!=",
|
|
|
|
bopBitwiseAnd: "&",
|
|
bopBitwiseXor: "^",
|
|
bopBitwiseOr: "|",
|
|
|
|
bopAnd: "&&",
|
|
bopOr: "||",
|
|
}
|
|
|
|
var bopMap = map[string]binaryOp{
|
|
"*": bopMult,
|
|
"/": bopDiv,
|
|
"%": bopPercent,
|
|
|
|
"+": bopPlus,
|
|
"-": bopMinus,
|
|
|
|
"<<": bopShiftL,
|
|
">>": bopShiftR,
|
|
|
|
">": bopGreater,
|
|
">=": bopGreaterEq,
|
|
"<": bopLess,
|
|
"<=": bopLessEq,
|
|
|
|
"==": bopManifestEqual,
|
|
"!=": bopManifestUnequal,
|
|
|
|
"&": bopBitwiseAnd,
|
|
"^": bopBitwiseXor,
|
|
"|": bopBitwiseOr,
|
|
|
|
"&&": bopAnd,
|
|
"||": bopOr,
|
|
}
|
|
|
|
func (b binaryOp) String() string {
|
|
if b < 0 || int(b) >= len(bopStrings) {
|
|
panic(fmt.Sprintf("INTERNAL ERROR: Unrecognised binary operator: %d", b))
|
|
}
|
|
return bopStrings[b]
|
|
}
|
|
|
|
// astBinary represents binary operators.
|
|
type astBinary struct {
|
|
astNodeBase
|
|
left astNode
|
|
op binaryOp
|
|
right astNode
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astBuiltin represents built-in functions.
|
|
//
|
|
// There is no parse rule to build this AST. Instead, it is used to build the
|
|
// std object in the interpreter.
|
|
type astBuiltin struct {
|
|
astNodeBase
|
|
id int
|
|
params identifiers
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astConditional represents if/then/else.
|
|
//
|
|
// After parsing, branchFalse can be nil indicating that no else branch
|
|
// was specified. The desugarer fills this in with a LiteralNull
|
|
type astConditional struct {
|
|
astNodeBase
|
|
cond astNode
|
|
branchTrue astNode
|
|
branchFalse astNode
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astDollar represents the $ keyword
|
|
type astDollar struct{ astNodeBase }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astError represents the error e.
|
|
type astError struct {
|
|
astNodeBase
|
|
expr astNode
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astFunction represents a function call. (jbeda: or is it function defn?)
|
|
type astFunction struct {
|
|
astNodeBase
|
|
parameters identifiers
|
|
trailingComma bool
|
|
body astNode
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astImport represents import "file".
|
|
type astImport struct {
|
|
astNodeBase
|
|
file string
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astImportStr represents importstr "file".
|
|
type astImportStr struct {
|
|
astNodeBase
|
|
file string
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astIndex represents both e[e] and the syntax sugar e.f.
|
|
//
|
|
// One of index and id will be nil before desugaring. After desugaring id
|
|
// will be nil.
|
|
type astIndex struct {
|
|
astNodeBase
|
|
target astNode
|
|
index astNode
|
|
id *identifier
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astLocalBind is a helper struct for astLocal
|
|
type astLocalBind struct {
|
|
variable identifier
|
|
body astNode
|
|
functionSugar bool
|
|
params identifiers // if functionSugar is true
|
|
trailingComma bool
|
|
}
|
|
type astLocalBinds []astLocalBind
|
|
|
|
// astLocal represents local x = e; e. After desugaring, functionSugar is false.
|
|
type astLocal struct {
|
|
astNodeBase
|
|
binds astLocalBinds
|
|
body astNode
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astLiteralBoolean represents true and false
|
|
type astLiteralBoolean struct {
|
|
astNodeBase
|
|
value bool
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astLiteralNull represents the null keyword
|
|
type astLiteralNull struct{ astNodeBase }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astLiteralNumber represents a JSON number
|
|
type astLiteralNumber struct {
|
|
astNodeBase
|
|
value float64
|
|
originalString string
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// +gen stringer
|
|
type astLiteralStringKind int
|
|
|
|
const (
|
|
astStringSingle astLiteralStringKind = iota
|
|
astStringDouble
|
|
astStringBlock
|
|
)
|
|
|
|
// astLiteralString represents a JSON string
|
|
type astLiteralString struct {
|
|
astNodeBase
|
|
value string
|
|
kind astLiteralStringKind
|
|
blockIndent string
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// +gen stringer
|
|
type astObjectFieldKind int
|
|
|
|
const (
|
|
astObjectAssert astObjectFieldKind = iota // assert expr2 [: expr3] where expr3 can be nil
|
|
astObjectFieldID // id:[:[:]] expr2
|
|
astObjectFieldExpr // '['expr1']':[:[:]] expr2
|
|
astObjectFieldStr // expr1:[:[:]] expr2
|
|
astObjectLocal // local id = expr2
|
|
)
|
|
|
|
// +gen stringer
|
|
type astObjectFieldHide int
|
|
|
|
const (
|
|
astObjectFieldHidden astObjectFieldHide = iota // f:: e
|
|
astObjectFieldInherit // f: e
|
|
astObjectFieldVisible // f::: e
|
|
)
|
|
|
|
type astObjectField struct {
|
|
kind astObjectFieldKind
|
|
hide astObjectFieldHide // (ignore if kind != astObjectField*)
|
|
superSugar bool // +: (ignore if kind != astObjectField*)
|
|
methodSugar bool // f(x, y, z): ... (ignore if kind == astObjectAssert)
|
|
expr1 astNode // Not in scope of the object
|
|
id *identifier
|
|
ids identifiers // If methodSugar == true then holds the params.
|
|
trailingComma bool // If methodSugar == true then remembers the trailing comma
|
|
expr2, expr3 astNode // In scope of the object (can see self).
|
|
}
|
|
|
|
// TODO(jbeda): Add the remaining constructor helpers here
|
|
|
|
func astObjectFieldLocal(methodSugar bool, id *identifier, ids identifiers, trailingComma bool, body astNode) astObjectField {
|
|
return astObjectField{astObjectLocal, astObjectFieldVisible, false, methodSugar, nil, id, ids, trailingComma, body, nil}
|
|
}
|
|
|
|
func astObjectFieldLocalNoMethod(id *identifier, body astNode) astObjectField {
|
|
return astObjectField{astObjectLocal, astObjectFieldVisible, false, false, nil, id, identifiers{}, false, body, nil}
|
|
}
|
|
|
|
type astObjectFields []astObjectField
|
|
|
|
// astObject represents object constructors { f: e ... }.
|
|
//
|
|
// The trailing comma is only allowed if len(fields) > 0. Converted to
|
|
// DesugaredObject during desugaring.
|
|
type astObject struct {
|
|
astNodeBase
|
|
fields astObjectFields
|
|
trailingComma bool
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type astDesugaredObjectField struct {
|
|
hide astObjectFieldHide
|
|
name astNode
|
|
body astNode
|
|
}
|
|
type astDesugaredObjectFields []astDesugaredObjectField
|
|
|
|
// astDesugaredObject represents object constructors { f: e ... } after
|
|
// desugaring.
|
|
//
|
|
// The assertions either return true or raise an error.
|
|
type astDesugaredObject struct {
|
|
astNodeBase
|
|
asserts astNodes
|
|
fields astDesugaredObjectFields
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astObjectComp represents object comprehension
|
|
// { [e]: e for x in e for.. if... }.
|
|
type astObjectComp struct {
|
|
astNodeBase
|
|
fields astObjectFields
|
|
trailingComma bool
|
|
specs astCompSpecs
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astObjectComprehensionSimple represents post-desugaring object
|
|
// comprehension { [e]: e for x in e }.
|
|
type astObjectComprehensionSimple struct {
|
|
astNodeBase
|
|
field astNode
|
|
value astNode
|
|
id identifier
|
|
array astNode
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astSelf represents the self keyword.
|
|
type astSelf struct{ astNodeBase }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astSuperIndex represents the super[e] and super.f constructs.
|
|
//
|
|
// Either index or identifier will be set before desugaring. After desugaring, id will be
|
|
// nil.
|
|
type astSuperIndex struct {
|
|
astNodeBase
|
|
index astNode
|
|
id *identifier
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type unaryOp int
|
|
|
|
const (
|
|
uopNot unaryOp = iota
|
|
uopBitwiseNot
|
|
uopPlus
|
|
uopMinus
|
|
)
|
|
|
|
var uopStrings = []string{
|
|
uopNot: "!",
|
|
uopBitwiseNot: "~",
|
|
uopPlus: "+",
|
|
uopMinus: "-",
|
|
}
|
|
|
|
var uopMap = map[string]unaryOp{
|
|
"!": uopNot,
|
|
"~": uopBitwiseNot,
|
|
"+": uopPlus,
|
|
"-": uopMinus,
|
|
}
|
|
|
|
func (u unaryOp) String() string {
|
|
if u < 0 || int(u) >= len(uopStrings) {
|
|
panic(fmt.Sprintf("INTERNAL ERROR: Unrecognised unary operator: %d", u))
|
|
}
|
|
return uopStrings[u]
|
|
}
|
|
|
|
// astUnary represents unary operators.
|
|
type astUnary struct {
|
|
astNodeBase
|
|
op unaryOp
|
|
expr astNode
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// astVar represents variables.
|
|
type astVar struct {
|
|
astNodeBase
|
|
id identifier
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|