diff --git a/docs/language-guide.md b/docs/language-guide.md index 7aa44148..647ad57f 100644 --- a/docs/language-guide.md +++ b/docs/language-guide.md @@ -291,7 +291,7 @@ If you'd like to create a built-in, core function, you'll need to implement the function API interface named `Func`. It can be found in [lang/interfaces/func.go](https://github.com/purpleidea/mgmt/tree/master/lang/interfaces/func.go). Your function must have a specific type. For example, a simple math function -might have a signature of `func(x int, x int) int`. As you can see, all the +might have a signature of `func(x int, y int) int`. As you can see, all the types are known _before_ compile time. A separate discussion on this matter can be found in the [function guide](function-guide.md). diff --git a/examples/lang/env1.mcl b/examples/lang/env1.mcl new file mode 100644 index 00000000..001223d0 --- /dev/null +++ b/examples/lang/env1.mcl @@ -0,0 +1,10 @@ +$env = env() +$m = maplookup($env, "GOPATH", "") + +print "print0" { + msg => if hasenv("GOPATH") { + printf("GOPATH is: %s", $m) + } else { + "GOPATH is missing!" + }, +} diff --git a/examples/lang/math2.mcl b/examples/lang/math2.mcl new file mode 100644 index 00000000..65bf9dfe --- /dev/null +++ b/examples/lang/math2.mcl @@ -0,0 +1,3 @@ +print "print0" { + msg => printf("13.0 ^ 4.2 is: %f", pow(13.0, 4.2)), +} diff --git a/examples/lang/pkg1.mcl b/examples/lang/pkg1.mcl new file mode 100644 index 00000000..10c115aa --- /dev/null +++ b/examples/lang/pkg1.mcl @@ -0,0 +1,3 @@ +pkg "cowsay" { + state => "installed", +} diff --git a/lang/funcs/simple/math_func.go b/lang/funcs/simple/math_func.go new file mode 100644 index 00000000..e29bef0a --- /dev/null +++ b/lang/funcs/simple/math_func.go @@ -0,0 +1,53 @@ +// 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 ( + "fmt" + "math" + + "github.com/purpleidea/mgmt/lang/types" +) + +func init() { + Register("pow", &types.FuncValue{ + T: types.NewType("func(x float, y float) float"), + V: Pow, + }) +} + +// Pow returns x ^ y, the base-x exponential of y. +func Pow(input []types.Value) (types.Value, error) { + x, y := input[0].Float(), input[1].Float() + // FIXME: check for overflow + z := math.Pow(x, y) + if math.IsNaN(z) { + return nil, fmt.Errorf("result is not a number") + } + if math.IsInf(z, 1) { + return nil, fmt.Errorf("result is positive infinity") + } + if math.IsInf(z, -1) { + return nil, fmt.Errorf("result is negative infinity") + } + // TODO: consider only returning floats, and adding isinf and + // isnan functions so that users can decide for themselves... + return &types.FloatValue{ + V: z, + }, nil +} diff --git a/lang/lexparse_test.go b/lang/lexparse_test.go index c4477549..43fa1dc7 100644 --- a/lang/lexparse_test.go +++ b/lang/lexparse_test.go @@ -80,7 +80,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "bad escaping", code: ` - test "n1" { + test "t1" { str => "he\ llo", # incorrect escaping } `, @@ -91,7 +91,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "int overflow", code: ` - test "n1" { + test "t1" { int => 888888888888888888888888, # overflows } `, @@ -102,7 +102,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "overflow after lexer", code: ` - test "n1" { + test "t1" { uint8 => 128, # does not overflow at lexer stage } `, @@ -114,7 +114,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "one res", code: ` - test "n1" { + test "t1" { int16 => 01134, # some comment } `, @@ -262,7 +262,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "res with floats", code: ` - test "n1" { + test "t1" { float32 => -25.38789, # some float float64 => 53.393908945, # some float } @@ -279,7 +279,7 @@ func TestLexParse0(t *testing.T) { // values = append(values, test{ // name: "overflowing float", // code: ` - // test "n1" { + // test "t1" { // float32 => -457643875645764387564578645457864525457643875645764387564578645457864525.457643875645764387564578645457864525387899898753459879587574928798759863965, # overflow // } // `, @@ -290,7 +290,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "res and addition", code: ` - test "n1" { + test "t1" { float32 => -25.38789 + 32.6, } `, @@ -304,7 +304,7 @@ func TestLexParse0(t *testing.T) { &StmtRes{ Kind: "test", Name: &ExprStr{ - V: "n1", + V: "t1", }, Fields: []*StmtResField{ { @@ -331,7 +331,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "addition", code: ` - test "n1" { + test "t1" { int64ptr => 13 + 42, } `, @@ -345,7 +345,7 @@ func TestLexParse0(t *testing.T) { &StmtRes{ Kind: "test", Name: &ExprStr{ - V: "n1", + V: "t1", }, Fields: []*StmtResField{ { @@ -383,7 +383,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "multiple float addition", code: ` - test "n1" { + test "t1" { float32 => -25.38789 + 32.6 + 13.7, } `, @@ -397,7 +397,7 @@ func TestLexParse0(t *testing.T) { &StmtRes{ Kind: "test", Name: &ExprStr{ - V: "n1", + V: "t1", }, Fields: []*StmtResField{ { @@ -435,7 +435,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "order of operations lucky", code: ` - test "n1" { + test "t1" { int64ptr => 4 + 3 * 12, # 40, not 84 } `, @@ -449,7 +449,7 @@ func TestLexParse0(t *testing.T) { &StmtRes{ Kind: "test", Name: &ExprStr{ - V: "n1", + V: "t1", }, Fields: []*StmtResField{ { @@ -487,7 +487,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "order of operations needs left precedence", code: ` - test "n1" { + test "t1" { int64ptr => 3 * 12 + 4, # 40, not 48 } `, @@ -501,7 +501,7 @@ func TestLexParse0(t *testing.T) { &StmtRes{ Kind: "test", Name: &ExprStr{ - V: "n1", + V: "t1", }, Fields: []*StmtResField{ { @@ -539,7 +539,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "order of operations parens", code: ` - test "n1" { + test "t1" { int64ptr => 3 * (12 + 4), # 48, not 40 } `, @@ -553,7 +553,7 @@ func TestLexParse0(t *testing.T) { &StmtRes{ Kind: "test", Name: &ExprStr{ - V: "n1", + V: "t1", }, Fields: []*StmtResField{ { @@ -591,7 +591,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "order of operations bools", code: ` - test "n1" { + test "t1" { boolptr => 3 + 4 > 5, # should be true } `, @@ -605,7 +605,7 @@ func TestLexParse0(t *testing.T) { &StmtRes{ Kind: "test", Name: &ExprStr{ - V: "n1", + V: "t1", }, Fields: []*StmtResField{ { @@ -643,7 +643,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "order of operations bools reversed", code: ` - test "n1" { + test "t1" { boolptr => 3 > 4 + 5, # should be false } `, @@ -657,7 +657,7 @@ func TestLexParse0(t *testing.T) { &StmtRes{ Kind: "test", Name: &ExprStr{ - V: "n1", + V: "t1", }, Fields: []*StmtResField{ { @@ -692,7 +692,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "order of operations with not", code: ` - test "n1" { + test "t1" { boolptr => ! 3 > 4, # should parse, but not compile } `, @@ -706,7 +706,7 @@ func TestLexParse0(t *testing.T) { &StmtRes{ Kind: "test", Name: &ExprStr{ - V: "n1", + V: "t1", }, Fields: []*StmtResField{ { @@ -744,7 +744,7 @@ func TestLexParse0(t *testing.T) { values = append(values, test{ name: "order of operations logical", code: ` - test "n1" { + test "t1" { boolptr => 7 < 4 && true, # should be false } `, @@ -879,7 +879,7 @@ func TestLexParse1(t *testing.T) { } # hello # world - test "n1" {} + test "t1" {} ` // error str := strings.NewReader(code) _, err := LexParse(str) diff --git a/test.sh b/test.sh index 6f9b34e8..8c796e8b 100755 --- a/test.sh +++ b/test.sh @@ -89,7 +89,7 @@ if [[ -n "$failures" ]]; then echo echo 'You can rerun a single suite like so:' echo - echo 'make test-gofmt' + echo '`make test-gofmt` or `make test-shell-`' exit 1 fi echo 'ALL PASSED' diff --git a/test/test-gotest.sh b/test/test-gotest.sh index ae442d62..17cad7e7 100755 --- a/test/test-gotest.sh +++ b/test/test-gotest.sh @@ -1,6 +1,6 @@ #!/bin/bash -echo "running test-gotest.sh $1" +echo running "$0" "$@" #ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )" # dir! ROOT=$(dirname "${BASH_SOURCE}")/..