mirror of
https://github.com/google/go-jsonnet.git
synced 2025-09-29 09:21:03 +02:00
parent
f0f70419f8
commit
ba0f236b14
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@ coverage.out
|
|||||||
/tests_path.source
|
/tests_path.source
|
||||||
/jsonnet/jsonnet
|
/jsonnet/jsonnet
|
||||||
*.so
|
*.so
|
||||||
|
*.prof
|
||||||
|
@ -322,8 +322,8 @@ type NamedParameter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Parameters struct {
|
type Parameters struct {
|
||||||
Positional Identifiers
|
Required Identifiers
|
||||||
Named []NamedParameter
|
Optional []NamedParameter
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
56
builtins.go
56
builtins.go
@ -224,7 +224,7 @@ func builtinLength(e *evaluator, xp potentialValue) (value, error) {
|
|||||||
case *valueString:
|
case *valueString:
|
||||||
num = x.length()
|
num = x.length()
|
||||||
case *valueFunction:
|
case *valueFunction:
|
||||||
num = len(x.parameters())
|
num = len(x.parameters().required)
|
||||||
default:
|
default:
|
||||||
return nil, e.typeErrorGeneral(x)
|
return nil, e.typeErrorGeneral(x)
|
||||||
}
|
}
|
||||||
@ -614,13 +614,12 @@ func getBuiltinEvaluator(e *evaluator, name ast.Identifier) *evaluator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *UnaryBuiltin) EvalCall(args callArguments, e *evaluator) (value, error) {
|
func (b *UnaryBuiltin) EvalCall(args callArguments, e *evaluator) (value, error) {
|
||||||
|
flatArgs := flattenArgs(args, b.Parameters())
|
||||||
// TODO check args
|
return b.function(getBuiltinEvaluator(e, b.name), flatArgs[0])
|
||||||
return b.function(getBuiltinEvaluator(e, b.name), args.positional[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *UnaryBuiltin) Parameters() ast.Identifiers {
|
func (b *UnaryBuiltin) Parameters() Parameters {
|
||||||
return b.parameters
|
return Parameters{required: b.parameters}
|
||||||
}
|
}
|
||||||
|
|
||||||
type BinaryBuiltin struct {
|
type BinaryBuiltin struct {
|
||||||
@ -629,13 +628,38 @@ type BinaryBuiltin struct {
|
|||||||
parameters ast.Identifiers
|
parameters ast.Identifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BinaryBuiltin) EvalCall(args callArguments, e *evaluator) (value, error) {
|
// flattenArgs transforms all arguments to a simple array of positional arguments.
|
||||||
// TODO check args
|
// It's needed, because it's possible to use named arguments for required parameters.
|
||||||
return b.function(getBuiltinEvaluator(e, b.name), args.positional[0], args.positional[1])
|
// For example both `toString("x")` and `toString(a="x")` are allowed.
|
||||||
|
// It assumes that we have already checked for duplicates.
|
||||||
|
func flattenArgs(args callArguments, params Parameters) []potentialValue {
|
||||||
|
if len(args.named) == 0 {
|
||||||
|
return args.positional
|
||||||
|
}
|
||||||
|
if len(params.optional) != 0 {
|
||||||
|
panic("Can't normalize arguments if optional parameters are present")
|
||||||
|
}
|
||||||
|
needed := make(map[ast.Identifier]int)
|
||||||
|
|
||||||
|
for i := len(args.positional); i < len(params.required); i++ {
|
||||||
|
needed[params.required[i]] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
flatArgs := make([]potentialValue, len(params.required))
|
||||||
|
copy(flatArgs, args.positional)
|
||||||
|
for _, arg := range args.named {
|
||||||
|
flatArgs[needed[arg.name]] = arg.pv
|
||||||
|
}
|
||||||
|
return flatArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BinaryBuiltin) Parameters() ast.Identifiers {
|
func (b *BinaryBuiltin) EvalCall(args callArguments, e *evaluator) (value, error) {
|
||||||
return b.parameters
|
flatArgs := flattenArgs(args, b.Parameters())
|
||||||
|
return b.function(getBuiltinEvaluator(e, b.name), flatArgs[0], flatArgs[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BinaryBuiltin) Parameters() Parameters {
|
||||||
|
return Parameters{required: b.parameters}
|
||||||
}
|
}
|
||||||
|
|
||||||
type TernaryBuiltin struct {
|
type TernaryBuiltin struct {
|
||||||
@ -645,12 +669,12 @@ type TernaryBuiltin struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *TernaryBuiltin) EvalCall(args callArguments, e *evaluator) (value, error) {
|
func (b *TernaryBuiltin) EvalCall(args callArguments, e *evaluator) (value, error) {
|
||||||
// TODO check args
|
flatArgs := flattenArgs(args, b.Parameters())
|
||||||
return b.function(getBuiltinEvaluator(e, b.name), args.positional[0], args.positional[1], args.positional[2])
|
return b.function(getBuiltinEvaluator(e, b.name), flatArgs[0], flatArgs[1], flatArgs[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *TernaryBuiltin) Parameters() ast.Identifiers {
|
func (b *TernaryBuiltin) Parameters() Parameters {
|
||||||
return b.parameters
|
return Parameters{required: b.parameters}
|
||||||
}
|
}
|
||||||
|
|
||||||
var desugaredBop = map[ast.BinaryOp]ast.Identifier{
|
var desugaredBop = map[ast.BinaryOp]ast.Identifier{
|
||||||
@ -698,7 +722,7 @@ var uopBuiltins = []*UnaryBuiltin{
|
|||||||
var funcBuiltins = map[string]evalCallable{
|
var funcBuiltins = map[string]evalCallable{
|
||||||
"extVar": &UnaryBuiltin{name: "extVar", function: builtinExtVar, parameters: ast.Identifiers{"x"}},
|
"extVar": &UnaryBuiltin{name: "extVar", function: builtinExtVar, parameters: ast.Identifiers{"x"}},
|
||||||
"length": &UnaryBuiltin{name: "length", function: builtinLength, parameters: ast.Identifiers{"x"}},
|
"length": &UnaryBuiltin{name: "length", function: builtinLength, parameters: ast.Identifiers{"x"}},
|
||||||
"toString": &UnaryBuiltin{name: "toString", function: builtinToString, parameters: ast.Identifiers{"x"}},
|
"toString": &UnaryBuiltin{name: "toString", function: builtinToString, parameters: ast.Identifiers{"a"}},
|
||||||
"makeArray": &BinaryBuiltin{name: "makeArray", function: builtinMakeArray, parameters: ast.Identifiers{"sz", "func"}},
|
"makeArray": &BinaryBuiltin{name: "makeArray", function: builtinMakeArray, parameters: ast.Identifiers{"sz", "func"}},
|
||||||
"flatMap": &BinaryBuiltin{name: "flatMap", function: builtinFlatMap, parameters: ast.Identifiers{"func", "arr"}},
|
"flatMap": &BinaryBuiltin{name: "flatMap", function: builtinFlatMap, parameters: ast.Identifiers{"func", "arr"}},
|
||||||
"filter": &BinaryBuiltin{name: "filter", function: builtinFilter, parameters: ast.Identifiers{"func", "arr"}},
|
"filter": &BinaryBuiltin{name: "filter", function: builtinFilter, parameters: ast.Identifiers{"func", "arr"}},
|
||||||
|
64
desugarer.go
64
desugarer.go
@ -86,28 +86,6 @@ func stringUnescape(loc *ast.LocationRange, s string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func desugarFields(location ast.LocationRange, fields *ast.ObjectFields, objLevel int) error {
|
func desugarFields(location ast.LocationRange, fields *ast.ObjectFields, objLevel int) error {
|
||||||
|
|
||||||
// Desugar children
|
|
||||||
for i := range *fields {
|
|
||||||
field := &((*fields)[i])
|
|
||||||
if field.Expr1 != nil {
|
|
||||||
err := desugar(&field.Expr1, objLevel)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err := desugar(&field.Expr2, objLevel+1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field.Expr3 != nil {
|
|
||||||
err := desugar(&field.Expr3, objLevel+1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simplify asserts
|
// Simplify asserts
|
||||||
for i := range *fields {
|
for i := range *fields {
|
||||||
field := &(*fields)[i]
|
field := &(*fields)[i]
|
||||||
@ -187,7 +165,7 @@ func desugarFields(location ast.LocationRange, fields *ast.ObjectFields, objLeve
|
|||||||
func simpleLambda(body ast.Node, paramName ast.Identifier) ast.Node {
|
func simpleLambda(body ast.Node, paramName ast.Identifier) ast.Node {
|
||||||
return &ast.Function{
|
return &ast.Function{
|
||||||
Body: body,
|
Body: body,
|
||||||
Parameters: ast.Parameters{Positional: ast.Identifiers{paramName}},
|
Parameters: ast.Parameters{Required: ast.Identifiers{paramName}},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,8 +211,6 @@ func desugarObjectComp(comp *ast.ObjectComp, objLevel int) (ast.Node, error) {
|
|||||||
comp.Fields = append(comp.Fields, ast.ObjectFieldLocalNoMethod(&dollar, &ast.Self{}))
|
comp.Fields = append(comp.Fields, ast.ObjectFieldLocalNoMethod(&dollar, &ast.Self{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(sbarzowski) find a consistent convention to prevent desugaring the same thing twice
|
|
||||||
// here we deeply desugar fields and it will happen again
|
|
||||||
err := desugarFields(*comp.Loc(), &comp.Fields, objLevel+1)
|
err := desugarFields(*comp.Loc(), &comp.Fields, objLevel+1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -383,6 +359,7 @@ func desugar(astPtr *ast.Node, objLevel int) (err error) {
|
|||||||
Expr: buildStdCall(desugaredBop[ast.BopManifestEqual], node.Left, node.Right),
|
Expr: buildStdCall(desugaredBop[ast.BopManifestEqual], node.Left, node.Right),
|
||||||
}
|
}
|
||||||
} else if node.Op == ast.BopIn {
|
} else if node.Op == ast.BopIn {
|
||||||
|
// reversed order of arguments
|
||||||
*astPtr = buildStdCall(funcname, node.Right, node.Left)
|
*astPtr = buildStdCall(funcname, node.Right, node.Left)
|
||||||
} else {
|
} else {
|
||||||
*astPtr = buildStdCall(funcname, node.Left, node.Right)
|
*astPtr = buildStdCall(funcname, node.Left, node.Right)
|
||||||
@ -429,6 +406,13 @@ func desugar(astPtr *ast.Node, objLevel int) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case *ast.Function:
|
case *ast.Function:
|
||||||
|
for i := range node.Parameters.Optional {
|
||||||
|
param := &node.Parameters.Optional[i]
|
||||||
|
err = desugar(¶m.DefaultArg, objLevel)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
err = desugar(&node.Body, objLevel)
|
err = desugar(&node.Body, objLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -476,7 +460,10 @@ func desugar(astPtr *ast.Node, objLevel int) (err error) {
|
|||||||
node.Step = &ast.LiteralNull{}
|
node.Step = &ast.LiteralNull{}
|
||||||
}
|
}
|
||||||
*astPtr = buildStdCall("slice", node.Target, node.BeginIndex, node.EndIndex, node.Step)
|
*astPtr = buildStdCall("slice", node.Target, node.BeginIndex, node.EndIndex, node.Step)
|
||||||
desugar(astPtr, objLevel)
|
err = desugar(astPtr, objLevel)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
case *ast.Local:
|
case *ast.Local:
|
||||||
for i := range node.Binds {
|
for i := range node.Binds {
|
||||||
@ -529,9 +516,32 @@ func desugar(astPtr *ast.Node, objLevel int) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
*astPtr = buildDesugaredObject(node.NodeBase, node.Fields)
|
*astPtr = buildDesugaredObject(node.NodeBase, node.Fields)
|
||||||
|
err = desugar(astPtr, objLevel)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
case *ast.DesugaredObject:
|
case *ast.DesugaredObject:
|
||||||
return nil
|
for i := range node.Fields {
|
||||||
|
field := &((node.Fields)[i])
|
||||||
|
if field.Name != nil {
|
||||||
|
err := desugar(&field.Name, objLevel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := desugar(&field.Body, objLevel+1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range node.Asserts {
|
||||||
|
assert := &((node.Asserts)[i])
|
||||||
|
err := desugar(assert, objLevel+1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case *ast.ObjectComp:
|
case *ast.ObjectComp:
|
||||||
comp, err := desugarObjectComp(node, objLevel)
|
comp, err := desugarObjectComp(node, objLevel)
|
||||||
|
@ -464,11 +464,16 @@ func (i *interpreter) evaluate(a ast.Node) (value, error) {
|
|||||||
|
|
||||||
arguments := callArguments{
|
arguments := callArguments{
|
||||||
positional: make([]potentialValue, len(ast.Arguments.Positional)),
|
positional: make([]potentialValue, len(ast.Arguments.Positional)),
|
||||||
|
named: make([]namedCallArgument, len(ast.Arguments.Named)),
|
||||||
}
|
}
|
||||||
for i, arg := range ast.Arguments.Positional {
|
for i, arg := range ast.Arguments.Positional {
|
||||||
arguments.positional[i] = makeThunk(argEnv, arg)
|
arguments.positional[i] = makeThunk(argEnv, arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i, arg := range ast.Arguments.Named {
|
||||||
|
arguments.named[i] = namedCallArgument{name: arg.Name, pv: makeThunk(argEnv, arg.Arg)}
|
||||||
|
}
|
||||||
|
|
||||||
return e.evaluate(function.call(arguments))
|
return e.evaluate(function.call(arguments))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
20
main_test.go
20
main_test.go
@ -23,7 +23,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
"github.com/sergi/go-diff/diffmatchpatch"
|
||||||
)
|
)
|
||||||
@ -40,25 +39,6 @@ type mainTest struct {
|
|||||||
golden string
|
golden string
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeExcessiveWhitespace(s string) string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
separated := true
|
|
||||||
for i, w := 0, 0; i < len(s); i += w {
|
|
||||||
runeValue, width := utf8.DecodeRuneInString(s[i:])
|
|
||||||
if runeValue == '\n' || runeValue == ' ' {
|
|
||||||
if !separated {
|
|
||||||
buf.WriteString(" ")
|
|
||||||
separated = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buf.WriteRune(runeValue)
|
|
||||||
separated = false
|
|
||||||
}
|
|
||||||
w = width
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setExtVars(vm *VM) {
|
func setExtVars(vm *VM) {
|
||||||
// TODO(sbarzowski) extract, so that it's possible to define extvars per-test
|
// TODO(sbarzowski) extract, so that it's possible to define extvars per-test
|
||||||
// Check that it doesn't get evaluated.
|
// Check that it doesn't get evaluated.
|
||||||
|
1
mutually_recursive_defaults.input
Normal file
1
mutually_recursive_defaults.input
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(a=[1, b[1]], b=[a[0], 2]) [a, b])()
|
@ -313,9 +313,9 @@ func addContext(node ast.Node, context *string, bind string) {
|
|||||||
case *ast.Function:
|
case *ast.Function:
|
||||||
funContext := functionContext(bind)
|
funContext := functionContext(bind)
|
||||||
addContext(node.Body, funContext, anonymous)
|
addContext(node.Body, funContext, anonymous)
|
||||||
for i := range node.Parameters.Named {
|
for i := range node.Parameters.Optional {
|
||||||
// Default arguments have the same context as the function body.
|
// Default arguments have the same context as the function body.
|
||||||
addContext(node.Parameters.Named[i].DefaultArg, funContext, anonymous)
|
addContext(node.Parameters.Optional[i].DefaultArg, funContext, anonymous)
|
||||||
}
|
}
|
||||||
case *ast.Object:
|
case *ast.Object:
|
||||||
// TODO(sbarzowski) include fieldname, maybe even chains
|
// TODO(sbarzowski) include fieldname, maybe even chains
|
||||||
|
@ -200,10 +200,10 @@ func (p *parser) parseParameters(elementKind string) (*ast.Parameters, bool, err
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, false, MakeStaticError(fmt.Sprintf("Expected simple identifier but got a complex expression."), *arg.Loc())
|
return nil, false, MakeStaticError(fmt.Sprintf("Expected simple identifier but got a complex expression."), *arg.Loc())
|
||||||
}
|
}
|
||||||
params.Positional = append(params.Positional, *id)
|
params.Required = append(params.Required, *id)
|
||||||
}
|
}
|
||||||
for _, arg := range args.Named {
|
for _, arg := range args.Named {
|
||||||
params.Named = append(params.Named, ast.NamedParameter{Name: arg.Name, DefaultArg: arg.Arg})
|
params.Optional = append(params.Optional, ast.NamedParameter{Name: arg.Name, DefaultArg: arg.Arg})
|
||||||
}
|
}
|
||||||
return ¶ms, trailingComma, nil
|
return ¶ms, trailingComma, nil
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,9 @@ func analyzeVisit(a ast.Node, inObject bool, vars ast.IdentifierSet) error {
|
|||||||
for _, arg := range a.Arguments.Positional {
|
for _, arg := range a.Arguments.Positional {
|
||||||
visitNext(arg, inObject, vars, s)
|
visitNext(arg, inObject, vars, s)
|
||||||
}
|
}
|
||||||
|
for _, arg := range a.Arguments.Named {
|
||||||
|
visitNext(arg.Arg, inObject, vars, s)
|
||||||
|
}
|
||||||
case *ast.Array:
|
case *ast.Array:
|
||||||
for _, elem := range a.Elements {
|
for _, elem := range a.Elements {
|
||||||
visitNext(elem, inObject, vars, s)
|
visitNext(elem, inObject, vars, s)
|
||||||
@ -60,18 +63,24 @@ func analyzeVisit(a ast.Node, inObject bool, vars ast.IdentifierSet) error {
|
|||||||
case *ast.Error:
|
case *ast.Error:
|
||||||
visitNext(a.Expr, inObject, vars, s)
|
visitNext(a.Expr, inObject, vars, s)
|
||||||
case *ast.Function:
|
case *ast.Function:
|
||||||
// TODO(sbarzowski) check duplicate function parameters
|
|
||||||
// or maybe somewhere else as it doesn't require any context
|
|
||||||
newVars := vars.Clone()
|
newVars := vars.Clone()
|
||||||
for _, param := range a.Parameters.Positional {
|
for _, param := range a.Parameters.Required {
|
||||||
newVars.Add(param)
|
newVars.Add(param)
|
||||||
}
|
}
|
||||||
|
for _, param := range a.Parameters.Optional {
|
||||||
|
newVars.Add(param.Name)
|
||||||
|
}
|
||||||
|
for _, param := range a.Parameters.Optional {
|
||||||
|
visitNext(param.DefaultArg, inObject, newVars, s)
|
||||||
|
}
|
||||||
visitNext(a.Body, inObject, newVars, s)
|
visitNext(a.Body, inObject, newVars, s)
|
||||||
// Parameters are free inside the body, but not visible here or outside
|
// Parameters are free inside the body, but not visible here or outside
|
||||||
for _, param := range a.Parameters.Positional {
|
for _, param := range a.Parameters.Required {
|
||||||
s.freeVars.Remove(param)
|
s.freeVars.Remove(param)
|
||||||
}
|
}
|
||||||
// TODO(sbarzowski) when we have default values of params check them
|
for _, param := range a.Parameters.Optional {
|
||||||
|
s.freeVars.Remove(param.Name)
|
||||||
|
}
|
||||||
case *ast.Import:
|
case *ast.Import:
|
||||||
//nothing to do here
|
//nothing to do here
|
||||||
case *ast.ImportStr:
|
case *ast.ImportStr:
|
||||||
|
1
std.thisFile.jsonnet
Normal file
1
std.thisFile.jsonnet
Normal file
@ -0,0 +1 @@
|
|||||||
|
std.thisFile
|
2
testdata/bad_function_call.golden
vendored
2
testdata/bad_function_call.golden
vendored
@ -1,4 +1,4 @@
|
|||||||
RUNTIME ERROR: function expected 1 argument(s), but got 0
|
RUNTIME ERROR: Missing argument: x
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
testdata/bad_function_call:1:1-18 $
|
testdata/bad_function_call:1:1-18 $
|
||||||
|
|
||||||
|
2
testdata/bad_function_call2.golden
vendored
2
testdata/bad_function_call2.golden
vendored
@ -1,4 +1,4 @@
|
|||||||
RUNTIME ERROR: function expected 1 argument(s), but got 2
|
RUNTIME ERROR: Function expected 1 positional argument(s), but got 2
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
testdata/bad_function_call2:1:1-22 $
|
testdata/bad_function_call2:1:1-22 $
|
||||||
|
|
||||||
|
2
testdata/bad_function_call_and_error.golden
vendored
2
testdata/bad_function_call_and_error.golden
vendored
@ -1,4 +1,4 @@
|
|||||||
RUNTIME ERROR: function expected 1 argument(s), but got 2
|
RUNTIME ERROR: Function expected 1 positional argument(s), but got 2
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
testdata/bad_function_call_and_error:1:1-38 $
|
testdata/bad_function_call_and_error:1:1-38 $
|
||||||
|
|
||||||
|
1
testdata/optional_args.golden
vendored
Normal file
1
testdata/optional_args.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
42
|
1
testdata/optional_args.jsonnet
vendored
Normal file
1
testdata/optional_args.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
local foo(x=42) = x; foo()
|
1
testdata/optional_args10.golden
vendored
Normal file
1
testdata/optional_args10.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
2
|
1
testdata/optional_args10.jsonnet
vendored
Normal file
1
testdata/optional_args10.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(x, y) x)(y=1, x=2)
|
10
testdata/optional_args11.golden
vendored
Normal file
10
testdata/optional_args11.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
RUNTIME ERROR: Argument x already provided
|
||||||
|
-------------------------------------------------
|
||||||
|
testdata/optional_args11:1:1-30 $
|
||||||
|
|
||||||
|
(function(x, y) 42)(42, x=42)
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
During evaluation
|
||||||
|
|
||||||
|
|
1
testdata/optional_args11.jsonnet
vendored
Normal file
1
testdata/optional_args11.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(x, y) 42)(42, x=42)
|
1
testdata/optional_args12.golden
vendored
Normal file
1
testdata/optional_args12.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
42
|
1
testdata/optional_args12.jsonnet
vendored
Normal file
1
testdata/optional_args12.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(x=42, y=42) 42)(y=42)
|
10
testdata/optional_args13.golden
vendored
Normal file
10
testdata/optional_args13.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
RUNTIME ERROR: Missing argument: y
|
||||||
|
-------------------------------------------------
|
||||||
|
testdata/optional_args13:1:1-26 $
|
||||||
|
|
||||||
|
(function(x, y) 42)(x=42)
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
During evaluation
|
||||||
|
|
||||||
|
|
1
testdata/optional_args13.jsonnet
vendored
Normal file
1
testdata/optional_args13.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(x, y) 42)(x=42)
|
1
testdata/optional_args14.golden
vendored
Normal file
1
testdata/optional_args14.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
42
|
1
testdata/optional_args14.jsonnet
vendored
Normal file
1
testdata/optional_args14.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(a=b, b=a) 42)()
|
1
testdata/optional_args15.golden
vendored
Normal file
1
testdata/optional_args15.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
42
|
1
testdata/optional_args15.jsonnet
vendored
Normal file
1
testdata/optional_args15.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ g: 42, f(a=self.g): a }.f()
|
1
testdata/optional_args16.golden
vendored
Normal file
1
testdata/optional_args16.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
42
|
1
testdata/optional_args16.jsonnet
vendored
Normal file
1
testdata/optional_args16.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(x=17) x)(42)
|
1
testdata/optional_args17.golden
vendored
Normal file
1
testdata/optional_args17.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
3
|
1
testdata/optional_args17.jsonnet
vendored
Normal file
1
testdata/optional_args17.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(x, y=42) x + y)(y=1, x=2)
|
6
testdata/optional_args18.golden
vendored
Normal file
6
testdata/optional_args18.golden
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
42,
|
||||||
|
43,
|
||||||
|
44,
|
||||||
|
45
|
||||||
|
]
|
1
testdata/optional_args18.jsonnet
vendored
Normal file
1
testdata/optional_args18.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(a1=1, a2=a1+1, a3=a2+1, a4=a3+1) [a1, a2, a3, a4])(a1=42)
|
6
testdata/optional_args19.golden
vendored
Normal file
6
testdata/optional_args19.golden
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
45,
|
||||||
|
44,
|
||||||
|
43,
|
||||||
|
42
|
||||||
|
]
|
1
testdata/optional_args19.jsonnet
vendored
Normal file
1
testdata/optional_args19.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(a1=a2+1, a2=a3+1, a3=a4+1, a4=1) [a1, a2, a3, a4])(a4=42)
|
5
testdata/optional_args2.golden
vendored
Normal file
5
testdata/optional_args2.golden
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"x": 2,
|
||||||
|
"y": 4,
|
||||||
|
"z": 2
|
||||||
|
}
|
4
testdata/optional_args2.jsonnet
vendored
Normal file
4
testdata/optional_args2.jsonnet
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
local x = 1;
|
||||||
|
local foo(x=2, y=3, z=x) = {x: x, y: y, z: z};
|
||||||
|
local x = 4;
|
||||||
|
foo(y=x)
|
18
testdata/optional_args20.golden
vendored
Normal file
18
testdata/optional_args20.golden
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[
|
||||||
|
[
|
||||||
|
42,
|
||||||
|
49
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43,
|
||||||
|
48
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44,
|
||||||
|
47
|
||||||
|
],
|
||||||
|
[
|
||||||
|
45,
|
||||||
|
46
|
||||||
|
]
|
||||||
|
]
|
1
testdata/optional_args20.jsonnet
vendored
Normal file
1
testdata/optional_args20.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(x, a1=[x, a2[1] + 1], a2=[a1[0] + 1, a3[1] + 1], a3=[a2[0] + 1, a4[1] + 1], a4=[a3[0] + 1, a4[0] + 1]) [a1, a2, a3, a4])(x=42)
|
1
testdata/optional_args21.golden
vendored
Normal file
1
testdata/optional_args21.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
2
|
2
testdata/optional_args21.jsonnet
vendored
Normal file
2
testdata/optional_args21.jsonnet
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
local foo(x=2, z=x) = x;
|
||||||
|
foo()
|
10
testdata/optional_args22.golden
vendored
Normal file
10
testdata/optional_args22.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[
|
||||||
|
[
|
||||||
|
42,
|
||||||
|
42
|
||||||
|
],
|
||||||
|
[
|
||||||
|
42,
|
||||||
|
17
|
||||||
|
]
|
||||||
|
]
|
2
testdata/optional_args22.jsonnet
vendored
Normal file
2
testdata/optional_args22.jsonnet
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
local foo(x) = local bar(y=x) = [x, y]; bar;
|
||||||
|
[foo(42)(), foo(42)(17)]
|
5
testdata/optional_args3.golden
vendored
Normal file
5
testdata/optional_args3.golden
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"x": 2,
|
||||||
|
"y": 1,
|
||||||
|
"z": 2
|
||||||
|
}
|
3
testdata/optional_args3.jsonnet
vendored
Normal file
3
testdata/optional_args3.jsonnet
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
local x = 1;
|
||||||
|
local foo(x=2, y=3, z=x) = {x: x, y: y, z: z};
|
||||||
|
foo(y=x)
|
5
testdata/optional_args4.golden
vendored
Normal file
5
testdata/optional_args4.golden
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"x": 5,
|
||||||
|
"y": 4,
|
||||||
|
"z": 5
|
||||||
|
}
|
4
testdata/optional_args4.jsonnet
vendored
Normal file
4
testdata/optional_args4.jsonnet
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
local x = 1;
|
||||||
|
local foo(x=2, y=3, z=x) = {x: x, y: y, z: z};
|
||||||
|
local x = 4;
|
||||||
|
foo(x=5, y=x)
|
1
testdata/optional_args5.golden
vendored
Normal file
1
testdata/optional_args5.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
42
|
2
testdata/optional_args5.jsonnet
vendored
Normal file
2
testdata/optional_args5.jsonnet
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
local foo(x, y=x) = y;
|
||||||
|
foo(42)
|
1
testdata/optional_args6.golden
vendored
Normal file
1
testdata/optional_args6.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
42
|
2
testdata/optional_args6.jsonnet
vendored
Normal file
2
testdata/optional_args6.jsonnet
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
local foo(x, y=x) = y;
|
||||||
|
foo(x=42)
|
1
testdata/optional_args7.golden
vendored
Normal file
1
testdata/optional_args7.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
42
|
2
testdata/optional_args7.jsonnet
vendored
Normal file
2
testdata/optional_args7.jsonnet
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
local foo(x) = x;
|
||||||
|
foo(x=42)
|
10
testdata/optional_args8.golden
vendored
Normal file
10
testdata/optional_args8.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
RUNTIME ERROR: Function has no parameter y
|
||||||
|
-------------------------------------------------
|
||||||
|
testdata/optional_args8:2:1-10 $
|
||||||
|
|
||||||
|
foo(y=17)
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
During evaluation
|
||||||
|
|
||||||
|
|
2
testdata/optional_args8.jsonnet
vendored
Normal file
2
testdata/optional_args8.jsonnet
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
local foo(x=42) = x;
|
||||||
|
foo(y=17)
|
10
testdata/optional_args9.golden
vendored
Normal file
10
testdata/optional_args9.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
RUNTIME ERROR: Argument x already provided
|
||||||
|
-------------------------------------------------
|
||||||
|
testdata/optional_args9:1:1-26 $
|
||||||
|
|
||||||
|
(function(x) x)(42, x=42)
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
During evaluation
|
||||||
|
|
||||||
|
|
1
testdata/optional_args9.jsonnet
vendored
Normal file
1
testdata/optional_args9.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(x) x)(42, x=42)
|
7
testdata/std.makeArrayNamed.golden
vendored
Normal file
7
testdata/std.makeArrayNamed.golden
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4
|
||||||
|
]
|
1
testdata/std.makeArrayNamed.jsonnet
vendored
Normal file
1
testdata/std.makeArrayNamed.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
std.makeArray(sz=5, func=function(i) i)
|
7
testdata/std.makeArrayNamed2.golden
vendored
Normal file
7
testdata/std.makeArrayNamed2.golden
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4
|
||||||
|
]
|
1
testdata/std.makeArrayNamed2.jsonnet
vendored
Normal file
1
testdata/std.makeArrayNamed2.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
std.makeArray(func=function(i) i, sz=5)
|
10
testdata/std.makeArrayNamed3.golden
vendored
Normal file
10
testdata/std.makeArrayNamed3.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
RUNTIME ERROR: Function has no parameter blahblah
|
||||||
|
-------------------------------------------------
|
||||||
|
testdata/std.makeArrayNamed3:1:1-54 $
|
||||||
|
|
||||||
|
std.makeArray(blahblah=5, blahblahblah=function(i) i)
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
During evaluation
|
||||||
|
|
||||||
|
|
1
testdata/std.makeArrayNamed3.jsonnet
vendored
Normal file
1
testdata/std.makeArrayNamed3.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
std.makeArray(blahblah=5, blahblahblah=function(i) i)
|
5
testdata/std.makeArrayNamed4.golden
vendored
Normal file
5
testdata/std.makeArrayNamed4.golden
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
]
|
1
testdata/std.makeArrayNamed4.jsonnet
vendored
Normal file
1
testdata/std.makeArrayNamed4.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
std.makeArray(3, func=function(i) i)
|
1
testdata/std.toString8.golden
vendored
Normal file
1
testdata/std.toString8.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"42"
|
1
testdata/std.toString8.jsonnet
vendored
Normal file
1
testdata/std.toString8.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
std.toString(a=42)
|
10
testdata/too_many_arguments.golden
vendored
Normal file
10
testdata/too_many_arguments.golden
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
RUNTIME ERROR: Function expected 3 positional argument(s), but got 4
|
||||||
|
-------------------------------------------------
|
||||||
|
testdata/too_many_arguments:1:1-35 $
|
||||||
|
|
||||||
|
(function(x, y, z) 42)(1, 2, 3, 4)
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
During evaluation
|
||||||
|
|
||||||
|
|
1
testdata/too_many_arguments.jsonnet
vendored
Normal file
1
testdata/too_many_arguments.jsonnet
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(x, y, z) 42)(1, 2, 3, 4)
|
73
thunks.go
73
thunks.go
@ -96,6 +96,18 @@ func (th *callThunk) getValue(i *interpreter, trace *TraceElement) (value, error
|
|||||||
return th.function.EvalCall(th.args, evaluator)
|
return th.function.EvalCall(th.args, evaluator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deferredThunk allows deferring creation of evaluable until it's actually needed.
|
||||||
|
// It's useful for self-recursive structures.
|
||||||
|
type deferredThunk func() evaluable
|
||||||
|
|
||||||
|
func (th deferredThunk) getValue(i *interpreter, trace *TraceElement) (value, error) {
|
||||||
|
return th().getValue(i, trace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeDeferredThunk(th deferredThunk) potentialValue {
|
||||||
|
return makeCachedThunk(th)
|
||||||
|
}
|
||||||
|
|
||||||
// cachedThunk is a wrapper that caches the value of a potentialValue after
|
// cachedThunk is a wrapper that caches the value of a potentialValue after
|
||||||
// the first evaluation.
|
// the first evaluation.
|
||||||
// Note: All potentialValues are required to provide the same value every time,
|
// Note: All potentialValues are required to provide the same value every time,
|
||||||
@ -190,28 +202,81 @@ type closure struct {
|
|||||||
// arguments should be added to it, before executing it
|
// arguments should be added to it, before executing it
|
||||||
env environment
|
env environment
|
||||||
function *ast.Function
|
function *ast.Function
|
||||||
|
params Parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
func (closure *closure) EvalCall(arguments callArguments, e *evaluator) (value, error) {
|
func (closure *closure) EvalCall(arguments callArguments, e *evaluator) (value, error) {
|
||||||
argThunks := make(bindingFrame)
|
argThunks := make(bindingFrame)
|
||||||
|
parameters := closure.Parameters()
|
||||||
for i, arg := range arguments.positional {
|
for i, arg := range arguments.positional {
|
||||||
argThunks[closure.function.Parameters.Positional[i]] = arg
|
var name ast.Identifier
|
||||||
|
if i < len(parameters.required) {
|
||||||
|
name = parameters.required[i]
|
||||||
|
} else {
|
||||||
|
name = parameters.optional[i-len(parameters.required)].name
|
||||||
|
}
|
||||||
|
argThunks[name] = arg
|
||||||
}
|
}
|
||||||
|
|
||||||
calledEnvironment := makeEnvironment(
|
for _, arg := range arguments.named {
|
||||||
|
argThunks[arg.name] = arg.pv
|
||||||
|
}
|
||||||
|
|
||||||
|
var calledEnvironment environment
|
||||||
|
|
||||||
|
for i := range parameters.optional {
|
||||||
|
param := ¶meters.optional[i]
|
||||||
|
if _, exists := argThunks[param.name]; !exists {
|
||||||
|
argThunks[param.name] = makeDeferredThunk(func() evaluable {
|
||||||
|
// Default arguments are evaluated in the same environment as function body
|
||||||
|
return param.defaultArg.inEnv(&calledEnvironment)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calledEnvironment = makeEnvironment(
|
||||||
addBindings(closure.env.upValues, argThunks),
|
addBindings(closure.env.upValues, argThunks),
|
||||||
closure.env.sb,
|
closure.env.sb,
|
||||||
)
|
)
|
||||||
return e.evalInCleanEnv(&calledEnvironment, closure.function.Body)
|
return e.evalInCleanEnv(&calledEnvironment, closure.function.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (closure *closure) Parameters() ast.Identifiers {
|
func (closure *closure) Parameters() Parameters {
|
||||||
return closure.function.Parameters.Positional
|
return closure.params
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareClosureParameters(parameters ast.Parameters, env environment) Parameters {
|
||||||
|
optionalParameters := make([]namedParameter, 0, len(parameters.Optional))
|
||||||
|
for _, named := range parameters.Optional {
|
||||||
|
optionalParameters = append(optionalParameters, namedParameter{
|
||||||
|
name: named.Name,
|
||||||
|
defaultArg: &defaultArgument{
|
||||||
|
body: named.DefaultArg,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return Parameters{
|
||||||
|
required: parameters.Required,
|
||||||
|
optional: optionalParameters,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeClosure(env environment, function *ast.Function) *closure {
|
func makeClosure(env environment, function *ast.Function) *closure {
|
||||||
return &closure{
|
return &closure{
|
||||||
env: env,
|
env: env,
|
||||||
function: function,
|
function: function,
|
||||||
|
params: prepareClosureParameters(function.Parameters, env),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// partialPotentialValue
|
||||||
|
// -------------------------------------
|
||||||
|
|
||||||
|
type defaultArgument struct {
|
||||||
|
body ast.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *defaultArgument) inEnv(env *environment) potentialValue {
|
||||||
|
return makeThunk(*env, da.body)
|
||||||
|
}
|
||||||
|
75
value.go
75
value.go
@ -235,24 +235,64 @@ type valueFunction struct {
|
|||||||
// TODO(sbarzowski) better name?
|
// TODO(sbarzowski) better name?
|
||||||
type evalCallable interface {
|
type evalCallable interface {
|
||||||
EvalCall(args callArguments, e *evaluator) (value, error)
|
EvalCall(args callArguments, e *evaluator) (value, error)
|
||||||
Parameters() ast.Identifiers
|
Parameters() Parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
type partialPotentialValue interface {
|
||||||
|
inEnv(env *environment) potentialValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *valueFunction) call(args callArguments) potentialValue {
|
func (f *valueFunction) call(args callArguments) potentialValue {
|
||||||
return makeCallThunk(f.ec, args)
|
return makeCallThunk(f.ec, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *valueFunction) parameters() ast.Identifiers {
|
func (f *valueFunction) parameters() Parameters {
|
||||||
return f.ec.Parameters()
|
return f.ec.Parameters()
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkArguments(e *evaluator, args callArguments, params ast.Identifiers) error {
|
func checkArguments(e *evaluator, args callArguments, params Parameters) error {
|
||||||
// TODO(sbarzowski) this will get much more complicated with named params
|
received := make(map[ast.Identifier]bool)
|
||||||
|
accepted := make(map[ast.Identifier]bool)
|
||||||
|
|
||||||
numPassed := len(args.positional)
|
numPassed := len(args.positional)
|
||||||
numExpected := len(params)
|
numExpected := len(params.required) + len(params.optional)
|
||||||
if numPassed != numExpected {
|
|
||||||
return e.Error(fmt.Sprintf("function expected %v argument(s), but got %v", numExpected, numPassed))
|
if numPassed > numExpected {
|
||||||
|
return e.Error(fmt.Sprintf("Function expected %v positional argument(s), but got %v", numExpected, numPassed))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, param := range params.required {
|
||||||
|
accepted[param] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, param := range params.optional {
|
||||||
|
accepted[param.name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range args.positional {
|
||||||
|
if i < len(params.required) {
|
||||||
|
received[params.required[i]] = true
|
||||||
|
} else {
|
||||||
|
received[params.optional[i-len(params.required)].name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range args.named {
|
||||||
|
if _, present := received[arg.name]; present {
|
||||||
|
return e.Error(fmt.Sprintf("Argument %v already provided", arg.name))
|
||||||
|
}
|
||||||
|
if _, present := accepted[arg.name]; !present {
|
||||||
|
return e.Error(fmt.Sprintf("Function has no parameter %v", arg.name))
|
||||||
|
}
|
||||||
|
received[arg.name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, param := range params.required {
|
||||||
|
if _, present := received[param]; !present {
|
||||||
|
return e.Error(fmt.Sprintf("Missing argument: %v", param))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,9 +300,28 @@ func (f *valueFunction) typename() string {
|
|||||||
return "function"
|
return "function"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Parameters struct {
|
||||||
|
required ast.Identifiers
|
||||||
|
optional []namedParameter
|
||||||
|
}
|
||||||
|
|
||||||
|
type namedParameter struct {
|
||||||
|
name ast.Identifier
|
||||||
|
defaultArg potentialValueInEnv
|
||||||
|
}
|
||||||
|
|
||||||
|
type potentialValueInEnv interface {
|
||||||
|
inEnv(env *environment) potentialValue
|
||||||
|
}
|
||||||
|
|
||||||
type callArguments struct {
|
type callArguments struct {
|
||||||
positional []potentialValue
|
positional []potentialValue
|
||||||
// TODO named arguments
|
named []namedCallArgument
|
||||||
|
}
|
||||||
|
|
||||||
|
type namedCallArgument struct {
|
||||||
|
name ast.Identifier
|
||||||
|
pv potentialValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func args(xs ...potentialValue) callArguments {
|
func args(xs ...potentialValue) callArguments {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user