mirror of
https://github.com/google/go-jsonnet.git
synced 2025-08-08 07:17:12 +02:00
Refactor to allow interleaving optional and positional params
This commit is contained in:
parent
7cad41e894
commit
0e67cc3c68
15
ast/ast.go
15
ast/ast.go
@ -371,15 +371,17 @@ type Error struct {
|
||||
type Function struct {
|
||||
NodeBase
|
||||
ParenLeftFodder Fodder
|
||||
Parameters Parameters
|
||||
Parameters []Parameter
|
||||
// Always false if there were no parameters.
|
||||
TrailingComma bool
|
||||
ParenRightFodder Fodder
|
||||
Body Node
|
||||
}
|
||||
|
||||
// NamedParameter represents an optional named parameter of a function.
|
||||
type NamedParameter struct {
|
||||
// Parameter represents a parameter of function.
|
||||
// If DefaultArg is set, it's an optional named parameter.
|
||||
// Otherwise, it's a positional parameter and EqFodder is not used.
|
||||
type Parameter struct {
|
||||
NameFodder Fodder
|
||||
Name Identifier
|
||||
EqFodder Fodder
|
||||
@ -395,13 +397,6 @@ type CommaSeparatedID struct {
|
||||
CommaFodder Fodder
|
||||
}
|
||||
|
||||
// Parameters represents the required positional parameters and optional named
|
||||
// parameters to a function definition.
|
||||
type Parameters struct {
|
||||
Required []CommaSeparatedID
|
||||
Optional []NamedParameter
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Import represents import "file".
|
||||
|
18
ast/clone.go
18
ast/clone.go
@ -35,17 +35,6 @@ func cloneForSpec(specPtr *ForSpec) {
|
||||
}
|
||||
}
|
||||
|
||||
// Updates fields of params to point to deep clones.
|
||||
func cloneParameters(params *Parameters) {
|
||||
if params == nil {
|
||||
return
|
||||
}
|
||||
params.Optional = append(make([]NamedParameter, 0), params.Optional...)
|
||||
for i := range params.Optional {
|
||||
clone(¶ms.Optional[i].DefaultArg)
|
||||
}
|
||||
}
|
||||
|
||||
// Updates fields of field to point to deep clones.
|
||||
func cloneField(field *ObjectField) {
|
||||
if field.Method != nil {
|
||||
@ -158,7 +147,12 @@ func clone(astPtr *Node) {
|
||||
r := new(Function)
|
||||
*astPtr = r
|
||||
*r = *node
|
||||
cloneParameters(&r.Parameters)
|
||||
if r.Parameters != nil {
|
||||
r.Parameters = append(make([]Parameter, 0), r.Parameters...)
|
||||
for i := range r.Parameters {
|
||||
clone(&r.Parameters[i].DefaultArg)
|
||||
}
|
||||
}
|
||||
clone(&r.Body)
|
||||
|
||||
case *Import:
|
||||
|
10402
astgen/stdast.go
10402
astgen/stdast.go
File diff suppressed because it is too large
Load Diff
110
builtins.go
110
builtins.go
@ -182,7 +182,11 @@ func builtinLength(i *interpreter, trace traceElement, x value) (value, error) {
|
||||
case valueString:
|
||||
num = x.length()
|
||||
case *valueFunction:
|
||||
num = len(x.Parameters().required)
|
||||
for _, param := range x.Parameters() {
|
||||
if param.defaultArg == nil {
|
||||
num++
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, i.typeErrorGeneral(x, trace)
|
||||
}
|
||||
@ -352,15 +356,15 @@ func builtinReverse(i *interpreter, trace traceElement, arrv value) (value, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lenArr := len(arr.elements) // lenx holds the original array length
|
||||
reversed_array := make([]*cachedThunk, lenArr) // creates a slice that refer to a new array of length lenx
|
||||
lenArr := len(arr.elements) // lenx holds the original array length
|
||||
reversedArray := make([]*cachedThunk, lenArr) // creates a slice that refer to a new array of length lenx
|
||||
|
||||
for i := 0; i < lenArr; i++ {
|
||||
j := lenArr - (i + 1) // j initially holds (lenx - 1) and decreases to 0 while i initially holds 0 and increase to (lenx - 1)
|
||||
reversed_array[i] = arr.elements[j]
|
||||
reversedArray[i] = arr.elements[j]
|
||||
}
|
||||
|
||||
return makeValueArray(reversed_array), nil
|
||||
return makeValueArray(reversedArray), nil
|
||||
}
|
||||
|
||||
func builtinFilter(i *interpreter, trace traceElement, funcv, arrv value) (value, error) {
|
||||
@ -1197,25 +1201,24 @@ type builtin interface {
|
||||
Name() ast.Identifier
|
||||
}
|
||||
|
||||
func flattenArgs(args callArguments, params parameters, defaults []value) []*cachedThunk {
|
||||
func flattenArgs(args callArguments, params []namedParameter, defaults []value) []*cachedThunk {
|
||||
positions := make(map[ast.Identifier]int)
|
||||
for i := 0; i < len(params.required); i++ {
|
||||
positions[params.required[i]] = i
|
||||
}
|
||||
for i := 0; i < len(params.optional); i++ {
|
||||
positions[params.optional[i].name] = i + len(params.required)
|
||||
for i, param := range params {
|
||||
positions[param.name] = i
|
||||
}
|
||||
|
||||
flatArgs := make([]*cachedThunk, len(params.required)+len(params.optional))
|
||||
flatArgs := make([]*cachedThunk, len(params))
|
||||
|
||||
// Bind positional arguments
|
||||
copy(flatArgs, args.positional)
|
||||
// Bind named arguments
|
||||
for _, arg := range args.named {
|
||||
flatArgs[positions[arg.name]] = arg.pv
|
||||
}
|
||||
for i := 0; i < len(params.optional); i++ {
|
||||
pos := len(params.required) + i
|
||||
if flatArgs[pos] == nil {
|
||||
flatArgs[pos] = readyThunk(defaults[i])
|
||||
// Bind defaults for unsatisfied named parameters
|
||||
for i := range params {
|
||||
if flatArgs[i] == nil {
|
||||
flatArgs[i] = readyThunk(defaults[i])
|
||||
}
|
||||
}
|
||||
return flatArgs
|
||||
@ -1244,8 +1247,12 @@ func (b *unaryBuiltin) evalCall(args callArguments, i *interpreter, trace traceE
|
||||
return b.function(i, builtinTrace, x)
|
||||
}
|
||||
|
||||
func (b *unaryBuiltin) Parameters() parameters {
|
||||
return parameters{required: b.parameters}
|
||||
func (b *unaryBuiltin) Parameters() []namedParameter {
|
||||
ret := make([]namedParameter, len(b.parameters))
|
||||
for i := range ret {
|
||||
ret[i].name = b.parameters[i]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *unaryBuiltin) Name() ast.Identifier {
|
||||
@ -1274,8 +1281,12 @@ func (b *binaryBuiltin) evalCall(args callArguments, i *interpreter, trace trace
|
||||
return b.function(i, builtinTrace, x, y)
|
||||
}
|
||||
|
||||
func (b *binaryBuiltin) Parameters() parameters {
|
||||
return parameters{required: b.parameters}
|
||||
func (b *binaryBuiltin) Parameters() []namedParameter {
|
||||
ret := make([]namedParameter, len(b.parameters))
|
||||
for i := range ret {
|
||||
ret[i].name = b.parameters[i]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *binaryBuiltin) Name() ast.Identifier {
|
||||
@ -1308,8 +1319,12 @@ func (b *ternaryBuiltin) evalCall(args callArguments, i *interpreter, trace trac
|
||||
return b.function(i, builtinTrace, x, y, z)
|
||||
}
|
||||
|
||||
func (b *ternaryBuiltin) Parameters() parameters {
|
||||
return parameters{required: b.parameters}
|
||||
func (b *ternaryBuiltin) Parameters() []namedParameter {
|
||||
ret := make([]namedParameter, len(b.parameters))
|
||||
for i := range ret {
|
||||
ret[i].name = b.parameters[i]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *ternaryBuiltin) Name() ast.Identifier {
|
||||
@ -1318,25 +1333,44 @@ func (b *ternaryBuiltin) Name() ast.Identifier {
|
||||
|
||||
type generalBuiltinFunc func(*interpreter, traceElement, []value) (value, error)
|
||||
|
||||
// generalBuiltin covers cases that other builtin structures do not,
|
||||
// in particular it can have any number of parameters. It can also
|
||||
// have optional parameters.
|
||||
type generalBuiltin struct {
|
||||
name ast.Identifier
|
||||
required ast.Identifiers
|
||||
optional ast.Identifiers
|
||||
type generalBuiltinParameter struct {
|
||||
name ast.Identifier
|
||||
// Note that the defaults are passed as values rather than AST nodes like in Parameters.
|
||||
// This spares us unnecessary evaluation.
|
||||
defaultValues []value
|
||||
function generalBuiltinFunc
|
||||
defaultValue value
|
||||
}
|
||||
|
||||
func (b *generalBuiltin) Parameters() parameters {
|
||||
optional := make([]namedParameter, len(b.optional))
|
||||
for i := range optional {
|
||||
optional[i] = namedParameter{name: b.optional[i]}
|
||||
// generalBuiltin covers cases that other builtin structures do not,
|
||||
// in particular it can have any number of parameters. It can also
|
||||
// have optional parameters. The optional ones have non-nil defaultValues
|
||||
// at the same index.
|
||||
type generalBuiltin struct {
|
||||
name ast.Identifier
|
||||
parameters []generalBuiltinParameter
|
||||
function generalBuiltinFunc
|
||||
}
|
||||
|
||||
func (b *generalBuiltin) Parameters() []namedParameter {
|
||||
ret := make([]namedParameter, len(b.parameters))
|
||||
for i := range ret {
|
||||
ret[i].name = b.parameters[i].name
|
||||
if b.parameters[i].defaultValue != nil {
|
||||
// This is not actually used because the defaultValue is used instead.
|
||||
// The only reason we don't leave it nil is because the checkArguments
|
||||
// function uses the non-nil status to indicate that the parameter
|
||||
// is optional.
|
||||
ret[i].defaultArg = &ast.LiteralNull{}
|
||||
}
|
||||
}
|
||||
return parameters{required: b.required, optional: optional}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *generalBuiltin) defaultValues() []value {
|
||||
ret := make([]value, len(b.parameters))
|
||||
for i := range ret {
|
||||
ret[i] = b.parameters[i].defaultValue
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *generalBuiltin) Name() ast.Identifier {
|
||||
@ -1344,7 +1378,7 @@ func (b *generalBuiltin) Name() ast.Identifier {
|
||||
}
|
||||
|
||||
func (b *generalBuiltin) evalCall(args callArguments, i *interpreter, trace traceElement) (value, error) {
|
||||
flatArgs := flattenArgs(args, b.Parameters(), b.defaultValues)
|
||||
flatArgs := flattenArgs(args, b.Parameters(), b.defaultValues())
|
||||
builtinTrace := getBuiltinTrace(trace, b.name)
|
||||
values := make([]value, len(flatArgs))
|
||||
for j := 0; j < len(values); j++ {
|
||||
@ -1445,7 +1479,7 @@ var funcBuiltins = buildBuiltinMap([]builtin{
|
||||
&unaryBuiltin{name: "base64", function: builtinBase64, parameters: ast.Identifiers{"input"}},
|
||||
&unaryBuiltin{name: "encodeUTF8", function: builtinEncodeUTF8, parameters: ast.Identifiers{"str"}},
|
||||
&unaryBuiltin{name: "decodeUTF8", function: builtinDecodeUTF8, parameters: ast.Identifiers{"arr"}},
|
||||
&generalBuiltin{name: "sort", function: builtinSort, required: ast.Identifiers{"arr"}, optional: ast.Identifiers{"keyF"}, defaultValues: []value{functionID}},
|
||||
&generalBuiltin{name: "sort", function: builtinSort, parameters: []generalBuiltinParameter{{name: "arr"}, {name: "keyF", defaultValue: functionID}}},
|
||||
&unaryBuiltin{name: "native", function: builtinNative, parameters: ast.Identifiers{"x"}},
|
||||
|
||||
// internal
|
||||
|
@ -298,8 +298,10 @@ func specialChildren(node ast.Node) []ast.Node {
|
||||
return nil
|
||||
case *ast.Function:
|
||||
children := []ast.Node{node.Body}
|
||||
for _, child := range node.Parameters.Optional {
|
||||
children = append(children, child.DefaultArg)
|
||||
for _, child := range node.Parameters {
|
||||
if child.DefaultArg != nil {
|
||||
children = append(children, child.DefaultArg)
|
||||
}
|
||||
}
|
||||
return children
|
||||
case *ast.Import:
|
||||
@ -389,9 +391,11 @@ func addContext(node ast.Node, context *string, bind string) {
|
||||
case *ast.Function:
|
||||
funContext := functionContext(bind)
|
||||
addContext(node.Body, funContext, anonymous)
|
||||
for i := range node.Parameters.Optional {
|
||||
// Default arguments have the same context as the function body.
|
||||
addContext(node.Parameters.Optional[i].DefaultArg, funContext, anonymous)
|
||||
for i := range node.Parameters {
|
||||
if node.Parameters[i].DefaultArg != nil {
|
||||
// Default arguments have the same context as the function body.
|
||||
addContext(node.Parameters[i].DefaultArg, funContext, anonymous)
|
||||
}
|
||||
}
|
||||
case *ast.Object:
|
||||
// TODO(sbarzowski) include fieldname, maybe even chains
|
||||
|
@ -210,25 +210,25 @@ func (p *parser) parseArguments(elementKind string) (*token, *ast.Arguments, boo
|
||||
}
|
||||
|
||||
// TODO(sbarzowski) - this returned bool is weird
|
||||
func (p *parser) parseParameters(elementKind string) (*token, *ast.Parameters, bool, error) {
|
||||
func (p *parser) parseParameters(elementKind string) (*token, []ast.Parameter, bool, error) {
|
||||
parenR, args, trailingComma, err := p.parseArguments(elementKind)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
var params ast.Parameters
|
||||
var params []ast.Parameter
|
||||
for _, arg := range args.Positional {
|
||||
idFodder, id, ok := astVarToIdentifier(arg.Expr)
|
||||
if !ok {
|
||||
return nil, nil, false, errors.MakeStaticError(fmt.Sprintf("Expected simple identifier but got a complex expression."), *arg.Expr.Loc())
|
||||
}
|
||||
params.Required = append(params.Required, ast.CommaSeparatedID{
|
||||
params = append(params, ast.Parameter{
|
||||
NameFodder: idFodder,
|
||||
Name: *id,
|
||||
CommaFodder: arg.CommaFodder,
|
||||
})
|
||||
}
|
||||
for _, arg := range args.Named {
|
||||
params.Optional = append(params.Optional, ast.NamedParameter{
|
||||
params = append(params, ast.Parameter{
|
||||
NameFodder: arg.NameFodder,
|
||||
Name: arg.Name,
|
||||
EqFodder: arg.EqFodder,
|
||||
@ -236,7 +236,7 @@ func (p *parser) parseParameters(elementKind string) (*token, *ast.Parameters, b
|
||||
CommaFodder: arg.CommaFodder,
|
||||
})
|
||||
}
|
||||
return parenR, ¶ms, trailingComma, nil
|
||||
return parenR, params, trailingComma, nil
|
||||
}
|
||||
|
||||
// TODO(sbarzowski) add location to all individual binds
|
||||
@ -260,7 +260,7 @@ func (p *parser) parseBind(binds *ast.LocalBinds) (*token, error) {
|
||||
}
|
||||
fun = &ast.Function{
|
||||
ParenLeftFodder: parenL.fodder,
|
||||
Parameters: *params,
|
||||
Parameters: params,
|
||||
TrailingComma: gotComma,
|
||||
ParenRightFodder: parenR.fodder,
|
||||
// Body gets filled in later.
|
||||
@ -423,7 +423,7 @@ func (p *parser) parseObjectRemainderField(literalFields *LiteralFieldSet, tok *
|
||||
methComma := false
|
||||
var parenL *token
|
||||
var parenR *token
|
||||
var params *ast.Parameters
|
||||
var params []ast.Parameter
|
||||
if p.peek().kind == tokenParenL {
|
||||
parenL = p.pop()
|
||||
var err error
|
||||
@ -460,7 +460,7 @@ func (p *parser) parseObjectRemainderField(literalFields *LiteralFieldSet, tok *
|
||||
if isMethod {
|
||||
method = &ast.Function{
|
||||
ParenLeftFodder: parenL.fodder,
|
||||
Parameters: *params,
|
||||
Parameters: params,
|
||||
TrailingComma: methComma,
|
||||
ParenRightFodder: parenR.fodder,
|
||||
Body: body,
|
||||
@ -505,7 +505,7 @@ func (p *parser) parseObjectRemainderLocal(binds *ast.IdentifierSet, tok *token,
|
||||
funcComma := false
|
||||
var parenL *token
|
||||
var parenR *token
|
||||
var params *ast.Parameters
|
||||
var params []ast.Parameter
|
||||
if p.peek().kind == tokenParenL {
|
||||
parenL = p.pop()
|
||||
isMethod = true
|
||||
@ -528,7 +528,7 @@ func (p *parser) parseObjectRemainderLocal(binds *ast.IdentifierSet, tok *token,
|
||||
if isMethod {
|
||||
method = &ast.Function{
|
||||
ParenLeftFodder: parenL.fodder,
|
||||
Parameters: *params,
|
||||
Parameters: params,
|
||||
ParenRightFodder: parenR.fodder,
|
||||
TrailingComma: funcComma,
|
||||
Body: body,
|
||||
@ -1050,7 +1050,7 @@ func (p *parser) parse(prec precedence) (ast.Node, error) {
|
||||
return &ast.Function{
|
||||
NodeBase: ast.NewNodeBaseLoc(locFromTokenAST(begin, body), begin.fodder),
|
||||
ParenLeftFodder: next.fodder,
|
||||
Parameters: *params,
|
||||
Parameters: params,
|
||||
TrailingComma: gotComma,
|
||||
ParenRightFodder: parenR.fodder,
|
||||
Body: body,
|
||||
|
@ -192,10 +192,8 @@ func desugarFields(nodeBase ast.NodeBase, fields *ast.ObjectFields, objLevel int
|
||||
|
||||
func simpleLambda(body ast.Node, paramName ast.Identifier) ast.Node {
|
||||
return &ast.Function{
|
||||
Body: body,
|
||||
Parameters: ast.Parameters{
|
||||
Required: []ast.CommaSeparatedID{{Name: paramName}},
|
||||
},
|
||||
Body: body,
|
||||
Parameters: []ast.Parameter{{Name: paramName}},
|
||||
}
|
||||
}
|
||||
|
||||
@ -431,11 +429,13 @@ func desugar(astPtr *ast.Node, objLevel int) (err error) {
|
||||
}
|
||||
|
||||
case *ast.Function:
|
||||
for i := range node.Parameters.Optional {
|
||||
param := &node.Parameters.Optional[i]
|
||||
err = desugar(¶m.DefaultArg, objLevel)
|
||||
if err != nil {
|
||||
return
|
||||
for i := range node.Parameters {
|
||||
param := &node.Parameters[i]
|
||||
if param.DefaultArg != nil {
|
||||
err = desugar(¶m.DefaultArg, objLevel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
err = desugar(&node.Body, objLevel)
|
||||
|
@ -76,21 +76,17 @@ func analyzeVisit(a ast.Node, inObject bool, vars ast.IdentifierSet) error {
|
||||
visitNext(a.Expr, inObject, vars, s)
|
||||
case *ast.Function:
|
||||
newVars := vars.Clone()
|
||||
for _, param := range a.Parameters.Required {
|
||||
for _, param := range a.Parameters {
|
||||
newVars.Add(param.Name)
|
||||
}
|
||||
for _, param := range a.Parameters.Optional {
|
||||
newVars.Add(param.Name)
|
||||
}
|
||||
for _, param := range a.Parameters.Optional {
|
||||
visitNext(param.DefaultArg, inObject, newVars, s)
|
||||
for _, param := range a.Parameters {
|
||||
if param.DefaultArg != nil {
|
||||
visitNext(param.DefaultArg, inObject, newVars, s)
|
||||
}
|
||||
}
|
||||
visitNext(a.Body, inObject, newVars, s)
|
||||
// Parameters are free inside the body, but not visible here or outside
|
||||
for _, param := range a.Parameters.Required {
|
||||
s.freeVars.Remove(param.Name)
|
||||
}
|
||||
for _, param := range a.Parameters.Optional {
|
||||
for _, param := range a.Parameters {
|
||||
s.freeVars.Remove(param.Name)
|
||||
}
|
||||
case *ast.Import:
|
||||
|
@ -27,14 +27,13 @@ func cloneScope(oldScope vScope) vScope {
|
||||
}
|
||||
|
||||
func findVariablesInFunc(node *ast.Function, info *LintingInfo, scope vScope) {
|
||||
for _, param := range node.Parameters.Required {
|
||||
for _, param := range node.Parameters {
|
||||
addVar(param.Name, node, info, scope, true)
|
||||
}
|
||||
for _, param := range node.Parameters.Optional {
|
||||
addVar(param.Name, node, info, scope, true)
|
||||
}
|
||||
for _, param := range node.Parameters.Optional {
|
||||
findVariables(param.DefaultArg, info, scope)
|
||||
for _, param := range node.Parameters {
|
||||
if param.DefaultArg != nil {
|
||||
findVariables(param.DefaultArg, info, scope)
|
||||
}
|
||||
}
|
||||
findVariables(node.Body, info, scope)
|
||||
}
|
||||
|
44
thunks.go
44
thunks.go
@ -16,7 +16,9 @@ limitations under the License.
|
||||
|
||||
package jsonnet
|
||||
|
||||
import "github.com/google/go-jsonnet/ast"
|
||||
import (
|
||||
"github.com/google/go-jsonnet/ast"
|
||||
)
|
||||
|
||||
// readyValue
|
||||
// -------------------------------------
|
||||
@ -147,7 +149,7 @@ type closure struct {
|
||||
// arguments should be added to it, before executing it
|
||||
env environment
|
||||
function *ast.Function
|
||||
params parameters
|
||||
params []namedParameter
|
||||
}
|
||||
|
||||
func forceThunks(i *interpreter, trace traceElement, args *bindingFrame) error {
|
||||
@ -164,13 +166,7 @@ func (closure *closure) evalCall(arguments callArguments, i *interpreter, trace
|
||||
argThunks := make(bindingFrame)
|
||||
parameters := closure.Parameters()
|
||||
for i, arg := range arguments.positional {
|
||||
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
|
||||
argThunks[parameters[i].name] = arg
|
||||
}
|
||||
|
||||
for _, arg := range arguments.named {
|
||||
@ -179,8 +175,7 @@ func (closure *closure) evalCall(arguments callArguments, i *interpreter, trace
|
||||
|
||||
var calledEnvironment environment
|
||||
|
||||
for i := range parameters.optional {
|
||||
param := ¶meters.optional[i]
|
||||
for _, param := range parameters {
|
||||
if _, exists := argThunks[param.name]; !exists {
|
||||
argThunks[param.name] = &cachedThunk{
|
||||
// Default arguments are evaluated in the same environment as function body
|
||||
@ -204,27 +199,20 @@ func (closure *closure) evalCall(arguments callArguments, i *interpreter, trace
|
||||
return i.EvalInCleanEnv(trace, &calledEnvironment, closure.function.Body, arguments.tailstrict)
|
||||
}
|
||||
|
||||
func (closure *closure) Parameters() parameters {
|
||||
func (closure *closure) Parameters() []namedParameter {
|
||||
return closure.params
|
||||
|
||||
}
|
||||
|
||||
func prepareClosureParameters(params ast.Parameters, env environment) parameters {
|
||||
optionalParameters := make([]namedParameter, 0, len(params.Optional))
|
||||
for _, named := range params.Optional {
|
||||
optionalParameters = append(optionalParameters, namedParameter{
|
||||
func prepareClosureParameters(params []ast.Parameter, env environment) []namedParameter {
|
||||
preparedParams := make([]namedParameter, 0, len(params))
|
||||
for _, named := range params {
|
||||
preparedParams = append(preparedParams, namedParameter{
|
||||
name: named.Name,
|
||||
defaultArg: named.DefaultArg,
|
||||
})
|
||||
}
|
||||
requiredParameters := make([]ast.Identifier, 0, len(params.Required))
|
||||
for _, required := range params.Required {
|
||||
requiredParameters = append(requiredParameters, required.Name)
|
||||
}
|
||||
return parameters{
|
||||
required: requiredParameters,
|
||||
optional: optionalParameters,
|
||||
}
|
||||
return preparedParams
|
||||
}
|
||||
|
||||
func makeClosure(env environment, function *ast.Function) *closure {
|
||||
@ -265,8 +253,12 @@ func (native *NativeFunction) evalCall(arguments callArguments, i *interpreter,
|
||||
}
|
||||
|
||||
// Parameters returns a NativeFunction's parameters.
|
||||
func (native *NativeFunction) Parameters() parameters {
|
||||
return parameters{required: native.Params}
|
||||
func (native *NativeFunction) Parameters() []namedParameter {
|
||||
ret := make([]namedParameter, len(native.Params))
|
||||
for i := range ret {
|
||||
ret[i].name = native.Params[i]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
|
37
value.go
37
value.go
@ -348,7 +348,7 @@ type valueFunction struct {
|
||||
// TODO(sbarzowski) better name?
|
||||
type evalCallable interface {
|
||||
evalCall(args callArguments, i *interpreter, trace traceElement) (value, error)
|
||||
Parameters() parameters
|
||||
Parameters() []namedParameter
|
||||
}
|
||||
|
||||
func (f *valueFunction) call(i *interpreter, trace traceElement, args callArguments) (value, error) {
|
||||
@ -359,37 +359,30 @@ func (f *valueFunction) call(i *interpreter, trace traceElement, args callArgume
|
||||
return f.ec.evalCall(args, i, trace)
|
||||
}
|
||||
|
||||
func (f *valueFunction) Parameters() parameters {
|
||||
func (f *valueFunction) Parameters() []namedParameter {
|
||||
return f.ec.Parameters()
|
||||
}
|
||||
|
||||
func checkArguments(i *interpreter, trace traceElement, args callArguments, params parameters) error {
|
||||
received := make(map[ast.Identifier]bool)
|
||||
accepted := make(map[ast.Identifier]bool)
|
||||
func checkArguments(i *interpreter, trace traceElement, args callArguments, params []namedParameter) error {
|
||||
|
||||
numPassed := len(args.positional)
|
||||
numExpected := len(params.required) + len(params.optional)
|
||||
maxExpected := len(params)
|
||||
|
||||
if numPassed > numExpected {
|
||||
return i.Error(fmt.Sprintf("function expected %v positional argument(s), but got %v", numExpected, numPassed), trace)
|
||||
if numPassed > maxExpected {
|
||||
return i.Error(fmt.Sprintf("function expected %v positional argument(s), but got %v", maxExpected, numPassed), trace)
|
||||
}
|
||||
|
||||
for _, param := range params.required {
|
||||
accepted[param] = true
|
||||
}
|
||||
|
||||
for _, param := range params.optional {
|
||||
// Parameter names the function will accept.
|
||||
accepted := make(map[ast.Identifier]bool)
|
||||
for _, param := range params {
|
||||
accepted[param.name] = true
|
||||
}
|
||||
|
||||
// Parameter names the call will bind.
|
||||
received := make(map[ast.Identifier]bool)
|
||||
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
|
||||
}
|
||||
received[params[i].name] = true
|
||||
}
|
||||
|
||||
for _, arg := range args.named {
|
||||
if _, present := received[arg.name]; present {
|
||||
return i.Error(fmt.Sprintf("Argument %v already provided", arg.name), trace)
|
||||
@ -400,9 +393,9 @@ func checkArguments(i *interpreter, trace traceElement, args callArguments, para
|
||||
received[arg.name] = true
|
||||
}
|
||||
|
||||
for _, param := range params.required {
|
||||
if _, present := received[param]; !present {
|
||||
return i.Error(fmt.Sprintf("Missing argument: %v", param), trace)
|
||||
for _, param := range params {
|
||||
if _, present := received[param.name]; !present && param.defaultArg == nil {
|
||||
return i.Error(fmt.Sprintf("Missing argument: %v", param.name), trace)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user