mirror of
https://github.com/google/go-jsonnet.git
synced 2025-09-29 01:11:02 +02:00
Support of +:, in, in super
Also profiling
This commit is contained in:
parent
93bcf64454
commit
0c86c9e109
@ -453,9 +453,10 @@ type Object struct {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
type DesugaredObjectField struct {
|
type DesugaredObjectField struct {
|
||||||
Hide ObjectFieldHide
|
Hide ObjectFieldHide
|
||||||
Name Node
|
Name Node
|
||||||
Body Node
|
Body Node
|
||||||
|
PlusSuper bool
|
||||||
}
|
}
|
||||||
type DesugaredObjectFields []DesugaredObjectField
|
type DesugaredObjectFields []DesugaredObjectField
|
||||||
|
|
||||||
|
14
builtins.go
14
builtins.go
@ -219,7 +219,7 @@ func builtinLength(e *evaluator, xp potentialValue) (value, error) {
|
|||||||
var num int
|
var num int
|
||||||
switch x := x.(type) {
|
switch x := x.(type) {
|
||||||
case valueObject:
|
case valueObject:
|
||||||
num = len(objectFields(x, true))
|
num = len(objectFields(x, withoutHidden))
|
||||||
case *valueArray:
|
case *valueArray:
|
||||||
num = len(x.elements)
|
num = len(x.elements)
|
||||||
case *valueString:
|
case *valueString:
|
||||||
@ -511,7 +511,7 @@ func builtinObjectFieldsEx(e *evaluator, objp potentialValue, includeHiddenP pot
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fields := objectFields(obj, !includeHidden.value)
|
fields := objectFields(obj, withHiddenFromBool(includeHidden.value))
|
||||||
sort.Strings(fields)
|
sort.Strings(fields)
|
||||||
elems := []potentialValue{}
|
elems := []potentialValue{}
|
||||||
for _, fieldname := range fields {
|
for _, fieldname := range fields {
|
||||||
@ -533,12 +533,9 @@ func builtinObjectHasEx(e *evaluator, objp potentialValue, fnamep potentialValue
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, fieldname := range objectFields(obj, !includeHidden.value) {
|
h := withHiddenFromBool(includeHidden.value)
|
||||||
if fieldname == string(fname.value) {
|
fieldp := tryObjectIndex(objectBinding(obj), string(fname.value), h)
|
||||||
return makeValueBoolean(true), nil
|
return makeValueBoolean(fieldp != nil), nil
|
||||||
}
|
|
||||||
}
|
|
||||||
return makeValueBoolean(false), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinPow(e *evaluator, basep potentialValue, expp potentialValue) (value, error) {
|
func builtinPow(e *evaluator, basep potentialValue, expp potentialValue) (value, error) {
|
||||||
@ -657,6 +654,7 @@ var desugaredBop = map[ast.BinaryOp]ast.Identifier{
|
|||||||
ast.BopPercent: "mod",
|
ast.BopPercent: "mod",
|
||||||
ast.BopManifestEqual: "equals",
|
ast.BopManifestEqual: "equals",
|
||||||
ast.BopManifestUnequal: "notEquals", // Special case
|
ast.BopManifestUnequal: "notEquals", // Special case
|
||||||
|
ast.BopIn: "objectHasAll",
|
||||||
}
|
}
|
||||||
|
|
||||||
var bopBuiltins = []*BinaryBuiltin{
|
var bopBuiltins = []*BinaryBuiltin{
|
||||||
|
BIN
compat/libgojsonnet.so
Normal file
BIN
compat/libgojsonnet.so
Normal file
Binary file not shown.
17
desugarer.go
17
desugarer.go
@ -191,19 +191,6 @@ func desugarFields(location ast.LocationRange, fields *ast.ObjectFields, objLeve
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove +:
|
|
||||||
// TODO(dcunnin): this
|
|
||||||
for _, field := range *fields {
|
|
||||||
if !field.SuperSugar {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
AST *super_f = alloc->make<SuperIndex>(field.expr1->location, field.expr1, nil)
|
|
||||||
field.expr2 = alloc->make<ast.Binary>(ast->location, super_f, BOP_PLUS, field.expr2)
|
|
||||||
field.superSugar = false
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +294,7 @@ func buildDesugaredObject(nodeBase ast.NodeBase, fields ast.ObjectFields) *ast.D
|
|||||||
if field.Kind == ast.ObjectAssert {
|
if field.Kind == ast.ObjectAssert {
|
||||||
newAsserts = append(newAsserts, field.Expr2)
|
newAsserts = append(newAsserts, field.Expr2)
|
||||||
} else if field.Kind == ast.ObjectFieldExpr {
|
} else if field.Kind == ast.ObjectFieldExpr {
|
||||||
newFields = append(newFields, ast.DesugaredObjectField{field.Hide, field.Expr1, field.Expr2})
|
newFields = append(newFields, ast.DesugaredObjectField{field.Hide, field.Expr1, field.Expr2, field.SuperSugar})
|
||||||
} else {
|
} else {
|
||||||
panic(fmt.Sprintf("INTERNAL ERROR: field should have been desugared: %s", field.Kind))
|
panic(fmt.Sprintf("INTERNAL ERROR: field should have been desugared: %s", field.Kind))
|
||||||
}
|
}
|
||||||
@ -400,6 +387,8 @@ func desugar(astPtr *ast.Node, objLevel int) (err error) {
|
|||||||
Op: ast.UopNot,
|
Op: ast.UopNot,
|
||||||
Expr: buildStdCall(desugaredBop[ast.BopManifestEqual], node.Left, node.Right),
|
Expr: buildStdCall(desugaredBop[ast.BopManifestEqual], node.Left, node.Right),
|
||||||
}
|
}
|
||||||
|
} else if node.Op == ast.BopIn {
|
||||||
|
*astPtr = buildStdCall(funcname, node.Right, node.Left)
|
||||||
} else {
|
} else {
|
||||||
*astPtr = buildStdCall(funcname, node.Left, node.Right)
|
*astPtr = buildStdCall(funcname, node.Left, node.Right)
|
||||||
}
|
}
|
||||||
|
@ -324,7 +324,11 @@ func (i *interpreter) evaluate(a ast.Node, context *TraceContext) (value, error)
|
|||||||
if _, ok := fields[fieldName]; ok {
|
if _, ok := fields[fieldName]; ok {
|
||||||
return nil, e.Error(duplicateFieldNameErrMsg(fieldName))
|
return nil, e.Error(duplicateFieldNameErrMsg(fieldName))
|
||||||
}
|
}
|
||||||
fields[fieldName] = valueSimpleObjectField{field.Hide, &codeUnboundField{field.Body}}
|
var f unboundField = &codeUnboundField{field.Body}
|
||||||
|
if field.PlusSuper {
|
||||||
|
f = &PlusSuperUnboundField{f}
|
||||||
|
}
|
||||||
|
fields[fieldName] = valueSimpleObjectField{field.Hide, f}
|
||||||
}
|
}
|
||||||
var asserts []unboundField
|
var asserts []unboundField
|
||||||
for _, assert := range ast.Asserts {
|
for _, assert := range ast.Asserts {
|
||||||
@ -422,7 +426,19 @@ func (i *interpreter) evaluate(a ast.Node, context *TraceContext) (value, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return superIndex(e, i.stack.getSelfBinding(), indexStr.getString())
|
return objectIndex(e, i.stack.getSelfBinding().super(), indexStr.getString())
|
||||||
|
|
||||||
|
case *ast.InSuper:
|
||||||
|
index, err := e.evalInCurrentContext(ast.Index)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
indexStr, err := e.getString(index)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
field := tryObjectIndex(i.stack.getSelfBinding().super(), indexStr.getString(), withHidden)
|
||||||
|
return makeValueBoolean(field != nil), nil
|
||||||
|
|
||||||
case *ast.Function:
|
case *ast.Function:
|
||||||
return &valueFunction{
|
return &valueFunction{
|
||||||
@ -567,9 +583,7 @@ func (i *interpreter) manifestJSON(trace *TraceElement, v value, multiline bool,
|
|||||||
buf.WriteString("null")
|
buf.WriteString("null")
|
||||||
|
|
||||||
case valueObject:
|
case valueObject:
|
||||||
// TODO(dcunnin): Run invariants (object-level assertions).
|
fieldNames := objectFields(v, withoutHidden)
|
||||||
|
|
||||||
fieldNames := objectFields(v, true)
|
|
||||||
sort.Strings(fieldNames)
|
sort.Strings(fieldNames)
|
||||||
|
|
||||||
err := checkAssertions(e, v)
|
err := checkAssertions(e, v)
|
||||||
|
@ -19,7 +19,9 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime/pprof"
|
||||||
|
|
||||||
"github.com/google/go-jsonnet"
|
"github.com/google/go-jsonnet"
|
||||||
)
|
)
|
||||||
@ -29,6 +31,17 @@ func usage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// https://blog.golang.org/profiling-go-programs
|
||||||
|
var cpuprofile = os.Getenv("JSONNET_CPU_PROFILE")
|
||||||
|
if cpuprofile != "" {
|
||||||
|
f, err := os.Create(cpuprofile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
pprof.StartCPUProfile(f)
|
||||||
|
defer pprof.StopCPUProfile()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(sbarzowski) Be consistent about error codes with C++ maybe
|
// TODO(sbarzowski) Be consistent about error codes with C++ maybe
|
||||||
vm := jsonnet.MakeVM()
|
vm := jsonnet.MakeVM()
|
||||||
if len(os.Args) != 2 {
|
if len(os.Args) != 2 {
|
||||||
|
@ -76,6 +76,11 @@ func analyzeVisit(a ast.Node, inObject bool, vars ast.IdentifierSet) error {
|
|||||||
//nothing to do here
|
//nothing to do here
|
||||||
case *ast.ImportStr:
|
case *ast.ImportStr:
|
||||||
//nothing to do here
|
//nothing to do here
|
||||||
|
case *ast.InSuper:
|
||||||
|
if !inObject {
|
||||||
|
return parser.MakeStaticError("Can't use super outside of an object.", *a.Loc())
|
||||||
|
}
|
||||||
|
visitNext(a.Index, inObject, vars, s)
|
||||||
case *ast.SuperIndex:
|
case *ast.SuperIndex:
|
||||||
if !inObject {
|
if !inObject {
|
||||||
return parser.MakeStaticError("Can't use super outside of an object.", *a.Loc())
|
return parser.MakeStaticError("Can't use super outside of an object.", *a.Loc())
|
||||||
|
1
testdata/in.golden
vendored
Normal file
1
testdata/in.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
true
|
1
testdata/in.jsonnet
vendored
Normal file
1
testdata/in.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"x" in { "x": 42 }
|
1
testdata/in2.golden
vendored
Normal file
1
testdata/in2.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
false
|
1
testdata/in2.jsonnet
vendored
Normal file
1
testdata/in2.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"x" in { }
|
1
testdata/in3.golden
vendored
Normal file
1
testdata/in3.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
true
|
1
testdata/in3.jsonnet
vendored
Normal file
1
testdata/in3.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"x" in { x: 42, y: 42 }
|
1
testdata/in4.golden
vendored
Normal file
1
testdata/in4.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
false
|
1
testdata/in4.jsonnet
vendored
Normal file
1
testdata/in4.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"x" in { assert false }
|
3
testdata/insuper.golden
vendored
Normal file
3
testdata/insuper.golden
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"x": 42
|
||||||
|
}
|
1
testdata/insuper.jsonnet
vendored
Normal file
1
testdata/insuper.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ x: 42 } { assert "x" in super }
|
1
testdata/insuper2.golden
vendored
Normal file
1
testdata/insuper2.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
testdata/insuper2:1:11-13 Expected token OPERATOR but got (in, "in")
|
1
testdata/insuper2.jsonnet
vendored
Normal file
1
testdata/insuper2.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ } { "x" in super }
|
3
testdata/insuper3.golden
vendored
Normal file
3
testdata/insuper3.golden
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"false": false
|
||||||
|
}
|
1
testdata/insuper3.jsonnet
vendored
Normal file
1
testdata/insuper3.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ "false": "x" in super }
|
1
testdata/insuper4.golden
vendored
Normal file
1
testdata/insuper4.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
testdata/insuper4:1:2-13 Can't use super outside of an object.
|
1
testdata/insuper4.jsonnet
vendored
Normal file
1
testdata/insuper4.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"x" in super
|
6
testdata/insuper5.golden
vendored
Normal file
6
testdata/insuper5.golden
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"x": 42,
|
||||||
|
"y": {
|
||||||
|
"false": false
|
||||||
|
}
|
||||||
|
}
|
1
testdata/insuper5.jsonnet
vendored
Normal file
1
testdata/insuper5.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ x: 42 } { y: { "false": "x" in super } }
|
1
testdata/insuper6.golden
vendored
Normal file
1
testdata/insuper6.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
testdata/insuper6:1:10-20 Unknown variable: undeclared
|
1
testdata/insuper6.jsonnet
vendored
Normal file
1
testdata/insuper6.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ assert undeclared in super }
|
1
testdata/insuper7.golden
vendored
Normal file
1
testdata/insuper7.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"object"
|
1
testdata/insuper7.jsonnet
vendored
Normal file
1
testdata/insuper7.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
std.type({ assert false } { x: "x" in super })
|
6
testdata/supersugar.golden
vendored
Normal file
6
testdata/supersugar.golden
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"x": {
|
||||||
|
"a": 1,
|
||||||
|
"b": 2
|
||||||
|
}
|
||||||
|
}
|
1
testdata/supersugar.jsonnet
vendored
Normal file
1
testdata/supersugar.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ x: {a: 1} } { x +: {b: 2} }
|
3
testdata/supersugar2.golden
vendored
Normal file
3
testdata/supersugar2.golden
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"x": 1
|
||||||
|
}
|
1
testdata/supersugar2.jsonnet
vendored
Normal file
1
testdata/supersugar2.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ x :+ 1 }
|
3
testdata/supersugar3.golden
vendored
Normal file
3
testdata/supersugar3.golden
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"x": true
|
||||||
|
}
|
1
testdata/supersugar3.jsonnet
vendored
Normal file
1
testdata/supersugar3.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ assert self.x } { x +: true }
|
4
testdata/supersugar4.golden
vendored
Normal file
4
testdata/supersugar4.golden
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"x": true,
|
||||||
|
"y": 43
|
||||||
|
}
|
1
testdata/supersugar4.jsonnet
vendored
Normal file
1
testdata/supersugar4.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ assert self.x, y: 42 } { x: true, y +: 1 }
|
1
testdata/supersugar5.golden
vendored
Normal file
1
testdata/supersugar5.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
42
|
1
testdata/supersugar5.jsonnet
vendored
Normal file
1
testdata/supersugar5.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
({ } { x +: function(x) x }).x(42)
|
3
testdata/supersugar6.golden
vendored
Normal file
3
testdata/supersugar6.golden
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"x": "hello, world"
|
||||||
|
}
|
1
testdata/supersugar6.jsonnet
vendored
Normal file
1
testdata/supersugar6.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ x: "hello, " } { x +: "world" }
|
10
testdata/supersugar7.golden
vendored
Normal file
10
testdata/supersugar7.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"x": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6
|
||||||
|
]
|
||||||
|
}
|
1
testdata/supersugar7.jsonnet
vendored
Normal file
1
testdata/supersugar7.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ x: [1, 2, 3] } { x +: [4, 5, 6] }
|
1
testdata/supersugar8.golden
vendored
Normal file
1
testdata/supersugar8.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
RUNTIME ERROR: Object assertion failed.
|
1
testdata/supersugar8.jsonnet
vendored
Normal file
1
testdata/supersugar8.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ assert self.x } { x +: false }
|
1
testdata/supersugar9.golden
vendored
Normal file
1
testdata/supersugar9.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"object"
|
2
testdata/supersugar9.jsonnet
vendored
Normal file
2
testdata/supersugar9.jsonnet
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
local infloop() = infloop();
|
||||||
|
std.type({ assert infloop, x: 1 } { x +: 1 })
|
25
thunks.go
25
thunks.go
@ -34,7 +34,7 @@ func (rv *readyValue) getValue(i *interpreter, t *TraceElement) (value, error) {
|
|||||||
return rv.content, nil
|
return rv.content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rv *readyValue) bindToObject(sb selfBinding, origBinding bindingFrame) potentialValue {
|
func (rv *readyValue) bindToObject(sb selfBinding, origBinding bindingFrame, fieldName string) potentialValue {
|
||||||
return rv
|
return rv
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +78,10 @@ func makeCallThunk(ec evalCallable, args callArguments) potentialValue {
|
|||||||
return makeCachedThunk(&callThunk{function: ec, args: args})
|
return makeCachedThunk(&callThunk{function: ec, args: args})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func call(ec evalCallable, arguments ...potentialValue) potentialValue {
|
||||||
|
return makeCallThunk(ec, args(arguments...))
|
||||||
|
}
|
||||||
|
|
||||||
func (th *callThunk) getValue(i *interpreter, trace *TraceElement) (value, error) {
|
func (th *callThunk) getValue(i *interpreter, trace *TraceElement) (value, error) {
|
||||||
evaluator := makeEvaluator(i, trace)
|
evaluator := makeEvaluator(i, trace)
|
||||||
err := checkArguments(evaluator, th.args, th.function.Parameters())
|
err := checkArguments(evaluator, th.args, th.function.Parameters())
|
||||||
@ -134,7 +138,7 @@ type codeUnboundField struct {
|
|||||||
body ast.Node
|
body ast.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *codeUnboundField) bindToObject(sb selfBinding, origBindings bindingFrame) potentialValue {
|
func (f *codeUnboundField) bindToObject(sb selfBinding, origBindings bindingFrame, fieldName string) potentialValue {
|
||||||
// TODO(sbarzowski) better object names (perhaps include a field name too?)
|
// TODO(sbarzowski) better object names (perhaps include a field name too?)
|
||||||
return makeThunk("object_field", makeEnvironment(origBindings, sb), f.body)
|
return makeThunk("object_field", makeEnvironment(origBindings, sb), f.body)
|
||||||
}
|
}
|
||||||
@ -146,7 +150,7 @@ type bindingsUnboundField struct {
|
|||||||
bindings bindingFrame
|
bindings bindingFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *bindingsUnboundField) bindToObject(sb selfBinding, origBindings bindingFrame) potentialValue {
|
func (f *bindingsUnboundField) bindToObject(sb selfBinding, origBindings bindingFrame, fieldName string) potentialValue {
|
||||||
var upValues bindingFrame
|
var upValues bindingFrame
|
||||||
upValues = make(bindingFrame)
|
upValues = make(bindingFrame)
|
||||||
for variable, pvalue := range origBindings {
|
for variable, pvalue := range origBindings {
|
||||||
@ -155,7 +159,20 @@ func (f *bindingsUnboundField) bindToObject(sb selfBinding, origBindings binding
|
|||||||
for variable, pvalue := range f.bindings {
|
for variable, pvalue := range f.bindings {
|
||||||
upValues[variable] = pvalue
|
upValues[variable] = pvalue
|
||||||
}
|
}
|
||||||
return f.inner.bindToObject(sb, upValues)
|
return f.inner.bindToObject(sb, upValues, fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlusSuperUnboundField struct {
|
||||||
|
inner unboundField
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *PlusSuperUnboundField) bindToObject(sb selfBinding, origBinding bindingFrame, fieldName string) potentialValue {
|
||||||
|
left := tryObjectIndex(sb.super(), fieldName, withHidden)
|
||||||
|
right := f.inner.bindToObject(sb, origBinding, fieldName)
|
||||||
|
if left != nil {
|
||||||
|
return call(bopBuiltins[ast.BopPlus], left, right)
|
||||||
|
}
|
||||||
|
return right
|
||||||
}
|
}
|
||||||
|
|
||||||
// evalCallables
|
// evalCallables
|
||||||
|
54
value.go
54
value.go
@ -311,6 +311,29 @@ func makeUnboundSelfBinding() selfBinding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func objectBinding(obj valueObject) selfBinding {
|
||||||
|
return selfBinding{self: obj, superDepth: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb selfBinding) super() selfBinding {
|
||||||
|
return selfBinding{self: sb.self, superDepth: sb.superDepth + 1}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hidden int
|
||||||
|
|
||||||
|
const (
|
||||||
|
withHidden Hidden = iota
|
||||||
|
withoutHidden
|
||||||
|
)
|
||||||
|
|
||||||
|
func withHiddenFromBool(with bool) Hidden {
|
||||||
|
if with {
|
||||||
|
return withHidden
|
||||||
|
} else {
|
||||||
|
return withoutHidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Hack - we need to distinguish not-checked-yet and no error situations
|
// Hack - we need to distinguish not-checked-yet and no error situations
|
||||||
// so we have a special value for no error and nil means that we don't know yet.
|
// so we have a special value for no error and nil means that we don't know yet.
|
||||||
var errNoErrorInObjectInvariants = errors.New("No error - assertions passed")
|
var errNoErrorInObjectInvariants = errors.New("No error - assertions passed")
|
||||||
@ -377,7 +400,7 @@ func checkAssertionsHelper(e *evaluator, obj valueObject, curr valueObject, supe
|
|||||||
return nil
|
return nil
|
||||||
case *valueSimpleObject:
|
case *valueSimpleObject:
|
||||||
for _, assert := range curr.asserts {
|
for _, assert := range curr.asserts {
|
||||||
_, err := e.evaluate(assert.bindToObject(selfBinding{self: obj, superDepth: superDepth}, curr.upValues))
|
_, err := e.evaluate(assert.bindToObject(selfBinding{self: obj, superDepth: superDepth}, curr.upValues, ""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -400,7 +423,7 @@ func checkAssertions(e *evaluator, obj valueObject) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *valueSimpleObject) index(e *evaluator, field string) (value, error) {
|
func (o *valueSimpleObject) index(e *evaluator, field string) (value, error) {
|
||||||
return objectIndex(e, selfBinding{self: o, superDepth: 0}, field)
|
return objectIndex(e, objectBinding(o), field)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*valueSimpleObject) inheritanceSize() int {
|
func (*valueSimpleObject) inheritanceSize() int {
|
||||||
@ -426,7 +449,7 @@ type valueSimpleObjectField struct {
|
|||||||
|
|
||||||
// unboundField is a field that doesn't know yet in which object it is.
|
// unboundField is a field that doesn't know yet in which object it is.
|
||||||
type unboundField interface {
|
type unboundField interface {
|
||||||
bindToObject(sb selfBinding, origBinding bindingFrame) potentialValue
|
bindToObject(sb selfBinding, origBinding bindingFrame, fieldName string) potentialValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// valueExtendedObject represents an object created through inheritence (left + right).
|
// valueExtendedObject represents an object created through inheritence (left + right).
|
||||||
@ -455,7 +478,7 @@ type valueExtendedObject struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *valueExtendedObject) index(e *evaluator, field string) (value, error) {
|
func (o *valueExtendedObject) index(e *evaluator, field string) (value, error) {
|
||||||
return objectIndex(e, selfBinding{self: o, superDepth: 0}, field)
|
return objectIndex(e, objectBinding(o), field)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *valueExtendedObject) inheritanceSize() int {
|
func (o *valueExtendedObject) inheritanceSize() int {
|
||||||
@ -498,23 +521,26 @@ func findField(curr value, minSuperDepth int, f string) (*valueSimpleObjectField
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func superIndex(e *evaluator, currentSB selfBinding, field string) (value, error) {
|
|
||||||
superSB := selfBinding{self: currentSB.self, superDepth: currentSB.superDepth + 1}
|
|
||||||
return objectIndex(e, superSB, field)
|
|
||||||
}
|
|
||||||
|
|
||||||
func objectIndex(e *evaluator, sb selfBinding, fieldName string) (value, error) {
|
func objectIndex(e *evaluator, sb selfBinding, fieldName string) (value, error) {
|
||||||
err := checkAssertions(e, sb.self)
|
err := checkAssertions(e, sb.self)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
field, upValues, foundAt := findField(sb.self, sb.superDepth, fieldName)
|
objp := tryObjectIndex(sb, fieldName, withHidden)
|
||||||
if field == nil {
|
if objp == nil {
|
||||||
return nil, e.Error(fmt.Sprintf("Field does not exist: %s", fieldName))
|
return nil, e.Error(fmt.Sprintf("Field does not exist: %s", fieldName))
|
||||||
}
|
}
|
||||||
|
return e.evaluate(objp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryObjectIndex(sb selfBinding, fieldName string, h Hidden) potentialValue {
|
||||||
|
field, upValues, foundAt := findField(sb.self, sb.superDepth, fieldName)
|
||||||
|
if field == nil || (h == withoutHidden && field.hide == ast.ObjectFieldHidden) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
fieldSelfBinding := selfBinding{self: sb.self, superDepth: foundAt}
|
fieldSelfBinding := selfBinding{self: sb.self, superDepth: foundAt}
|
||||||
|
|
||||||
return e.evaluate(field.field.bindToObject(fieldSelfBinding, upValues))
|
return field.field.bindToObject(fieldSelfBinding, upValues, fieldName)
|
||||||
}
|
}
|
||||||
|
|
||||||
type fieldHideMap map[string]ast.ObjectFieldHide
|
type fieldHideMap map[string]ast.ObjectFieldHide
|
||||||
@ -544,10 +570,10 @@ func objectFieldsVisibility(obj valueObject) fieldHideMap {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func objectFields(obj valueObject, manifesting bool) []string {
|
func objectFields(obj valueObject, h Hidden) []string {
|
||||||
var r []string
|
var r []string
|
||||||
for fieldName, hide := range objectFieldsVisibility(obj) {
|
for fieldName, hide := range objectFieldsVisibility(obj) {
|
||||||
if !manifesting || hide != ast.ObjectFieldHidden {
|
if h == withHidden || hide != ast.ObjectFieldHidden {
|
||||||
r = append(r, fieldName)
|
r = append(r, fieldName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user