From b0911c6d703e3c707c4537a473386c300ca78f63 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Thu, 22 Feb 2018 19:10:15 -0500 Subject: [PATCH] lang: funcs: simple: Don't block on simple, pure, static functions I forgot to handle the special case of a function using this API that received no inputs. It was waiting for the first input to come in, and as a result was never producing any output. Remember that functions like this should *almost* be thought of as constants of the system. You would expect their output to never change during the lifetime of a particular program invocation. --- examples/lang/answer.mcl | 4 ++ lang/funcs/facts/core/answer_fact.go | 76 ---------------------------- lang/funcs/simple/answer_func.go | 34 +++++++++++++ lang/funcs/simple/simple.go | 27 ++++++---- 4 files changed, 55 insertions(+), 86 deletions(-) create mode 100644 examples/lang/answer.mcl delete mode 100644 lang/funcs/facts/core/answer_fact.go create mode 100644 lang/funcs/simple/answer_func.go diff --git a/examples/lang/answer.mcl b/examples/lang/answer.mcl new file mode 100644 index 00000000..2096f728 --- /dev/null +++ b/examples/lang/answer.mcl @@ -0,0 +1,4 @@ +# it was a lovely surprise to me, when i realized that mgmt had the answer! +print "answer" { + msg => printf("the answer to life, the universe, and everything is: %d", answer()), +} diff --git a/lang/funcs/facts/core/answer_fact.go b/lang/funcs/facts/core/answer_fact.go deleted file mode 100644 index 005568e3..00000000 --- a/lang/funcs/facts/core/answer_fact.go +++ /dev/null @@ -1,76 +0,0 @@ -// Mgmt -// Copyright (C) 2013-2018+ James Shubin and the project contributors -// Written by James Shubin and the project contributors -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package core // TODO: should this be in its own individual package? - -import ( - "github.com/purpleidea/mgmt/lang/funcs/facts" - "github.com/purpleidea/mgmt/lang/types" -) - -func init() { - facts.Register("answer", func() facts.Fact { return &AnswerFact{} }) // must register the fact and name -} - -// Answer is the Answer to Life, the Universe and Everything. -const Answer = 42 - -// AnswerFact returns the Answer to Life, the Universe and Everything. -type AnswerFact struct { - init *facts.Init - closeChan chan struct{} -} - -// Validate makes sure we've built our struct properly. It is usually unused for -// normal facts that users can use directly. -//func (obj *AnswerFact) Validate() error { -// return nil -//} - -// Info returns some static info about itself. -func (obj *AnswerFact) Info() *facts.Info { - return &facts.Info{ - Output: types.NewType("int"), - } -} - -// Init runs some startup code for this fact. -func (obj *AnswerFact) Init(init *facts.Init) error { - obj.init = init - obj.closeChan = make(chan struct{}) - return nil -} - -// Stream returns the changing values that this fact has over time. In this -// case, it returns a single value and then exits, since this is a static fact. -func (obj *AnswerFact) Stream() error { - select { - case obj.init.Output <- &types.IntValue{ - V: Answer, - }: - case <-obj.closeChan: - return nil - } - close(obj.init.Output) // signal that we're done sending - return nil -} - -// Close runs some shutdown code for this fact and turns off the stream. -func (obj *AnswerFact) Close() error { - close(obj.closeChan) - return nil -} diff --git a/lang/funcs/simple/answer_func.go b/lang/funcs/simple/answer_func.go new file mode 100644 index 00000000..081daaeb --- /dev/null +++ b/lang/funcs/simple/answer_func.go @@ -0,0 +1,34 @@ +// Mgmt +// Copyright (C) 2013-2018+ James Shubin and the project contributors +// Written by James Shubin and the project contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package simple // TODO: should this be in its own individual package? + +import ( + "github.com/purpleidea/mgmt/lang/types" +) + +// Answer is the Answer to Life, the Universe and Everything. +const Answer = 42 + +func init() { + Register("answer", &types.FuncValue{ + T: types.NewType("func() int"), + V: func([]types.Value) (types.Value, error) { + return &types.IntValue{V: Answer}, nil + }, + }) +} diff --git a/lang/funcs/simple/simple.go b/lang/funcs/simple/simple.go index 4dc63a7b..37fba1e2 100644 --- a/lang/funcs/simple/simple.go +++ b/lang/funcs/simple/simple.go @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package simple // TODO: should this be in its own individual package? +package simple import ( "fmt" @@ -30,7 +30,7 @@ import ( // RegisteredFuncs maps a function name to the corresponding static, pure func. var RegisteredFuncs = make(map[string]*types.FuncValue) // must initialize -// Register registers a simple, static, pure function. It is easier to use that +// Register registers a simple, static, pure function. It is easier to use than // the raw function API, but also limits you to simple, static, pure functions. func Register(name string, fn *types.FuncValue) { if _, exists := RegisteredFuncs[name]; exists { @@ -88,16 +88,21 @@ func (obj *simpleFunc) Stream() error { select { case input, ok := <-obj.init.Input: if !ok { - return nil // can't output any more + if len(obj.Fn.Type().Ord) > 0 { + return nil // can't output any more + } + // no inputs were expected, pass through once } - //if err := input.Type().Cmp(obj.Info().Sig.Input); err != nil { - // return errwrap.Wrapf(err, "wrong function input") - //} + if ok { + //if err := input.Type().Cmp(obj.Info().Sig.Input); err != nil { + // return errwrap.Wrapf(err, "wrong function input") + //} - if obj.last != nil && input.Cmp(obj.last) == nil { - continue // value didn't change, skip it + if obj.last != nil && input.Cmp(obj.last) == nil { + continue // value didn't change, skip it + } + obj.last = input // store for next } - obj.last = input // store for next values := []types.Value{} for _, name := range obj.Fn.Type().Ord { @@ -121,7 +126,9 @@ func (obj *simpleFunc) Stream() error { select { case obj.init.Output <- obj.result: // send - // pass + if len(obj.Fn.Type().Ord) == 0 { + return nil // no more values, we're a pure func + } case <-obj.closeChan: return nil }