From 046b21b907bc12505b8695f9aac8fd28ec60b992 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Sat, 10 Nov 2018 22:19:28 -0500 Subject: [PATCH] lang: Refactor most functions to support modules This is a giant refactor to move functions into a hierarchial module layout. While this isn't entirely implemented yet, it should work correctly once all the import bits have landed. What's broken at the moment is the template function, which currently doesn't understand the period separator. --- docs/function-guide.md | 5 +- docs/language-guide.md | 3 +- examples/lang/answer.mcl | 4 +- examples/lang/contains0.mcl | 13 ++++-- examples/lang/datetime1.mcl | 4 +- examples/lang/datetime2.mcl | 7 ++- examples/lang/datetime3.mcl | 7 ++- examples/lang/env0.mcl | 15 +++--- examples/lang/env1.mcl | 9 ++-- examples/lang/exchange0.mcl | 4 +- examples/lang/history1.mcl | 4 +- examples/lang/hostname0.mcl | 6 ++- examples/lang/len0.mcl | 6 ++- examples/lang/load0.mcl | 7 ++- examples/lang/maplookup1.mcl | 6 ++- examples/lang/math1.mcl | 4 +- examples/lang/math2.mcl | 5 +- examples/lang/printf1.mcl | 6 ++- examples/lang/schedule0.mcl | 4 +- examples/lang/sendrecv3.mcl | 4 +- examples/lang/structlookup1.mcl | 6 ++- examples/lang/template0.mcl | 7 ++- lang/funcs/core/core.go | 27 +++++++++++ lang/funcs/core/coredatetime/coredatetime.go | 23 ++++++++++ .../coredatetime/now_fact.go} | 4 +- .../coredatetime/print_func.go} | 6 +-- .../coreexample}/answer_func.go | 5 +- lang/funcs/core/coreexample/coreexample.go | 23 ++++++++++ .../coreexample/errorbool_func.go} | 6 +-- .../coreexample}/flipflop_fact.go | 5 +- .../coreexample}/int2str_func.go | 5 +- .../core/{ => coreexample}/vumeter_func.go | 4 +- lang/funcs/core/corefmt/corefmt.go | 23 ++++++++++ .../printf_func.go} | 4 +- lang/funcs/core/coremath/coremath.go | 23 ++++++++++ .../coremath/pow_func.go} | 5 +- lang/funcs/core/coresys/coresys.go | 23 ++++++++++ .../{simple => core/coresys}/env_func.go | 11 +++-- .../core => core/coresys}/hostname_fact.go | 4 +- .../{facts/core => core/coresys}/load_fact.go | 4 +- .../core => core/coresys}/load_fact_darwin.go | 2 +- .../core => core/coresys}/load_fact_posix.go | 2 +- ...{exchange_polyfunc.go => exchange_func.go} | 0 .../len_polyfunc.go => core/len_func.go} | 5 +- ...{schedule_polyfunc.go => schedule_func.go} | 0 ...{template_polyfunc.go => template_func.go} | 2 + lang/funcs/facts/facts.go | 6 +++ lang/funcs/funcs.go | 46 ++++++++++++++++++- lang/funcs/simple/simple.go | 6 +++ lang/funcs/simplepoly/simplepoly.go | 6 +++ lang/interfaces/const.go | 24 ++++++++++ lang/lang.go | 5 +- lang/lang_test.go | 29 ++++++++---- test/shell/env0.mcl | 25 +++++----- test/shell/load0.sh | 7 ++- 55 files changed, 400 insertions(+), 106 deletions(-) create mode 100644 lang/funcs/core/core.go create mode 100644 lang/funcs/core/coredatetime/coredatetime.go rename lang/funcs/{facts/core/datetime_fact.go => core/coredatetime/now_fact.go} (94%) rename lang/funcs/{simple/datetime_print_func.go => core/coredatetime/print_func.go} (87%) rename lang/funcs/{simple => core/coreexample}/answer_func.go (89%) create mode 100644 lang/funcs/core/coreexample/coreexample.go rename lang/funcs/{simple/example_errorbool_func.go => core/coreexample/errorbool_func.go} (85%) rename lang/funcs/{facts/core => core/coreexample}/flipflop_fact.go (91%) rename lang/funcs/{simple => core/coreexample}/int2str_func.go (88%) rename lang/funcs/core/{ => coreexample}/vumeter_func.go (96%) create mode 100644 lang/funcs/core/corefmt/corefmt.go rename lang/funcs/core/{printf_polyfunc.go => corefmt/printf_func.go} (98%) create mode 100644 lang/funcs/core/coremath/coremath.go rename lang/funcs/{simple/math_func.go => core/coremath/pow_func.go} (92%) create mode 100644 lang/funcs/core/coresys/coresys.go rename lang/funcs/{simple => core/coresys}/env_func.go (87%) rename lang/funcs/{facts/core => core/coresys}/hostname_fact.go (92%) rename lang/funcs/{facts/core => core/coresys}/load_fact.go (94%) rename lang/funcs/{facts/core => core/coresys}/load_fact_darwin.go (94%) rename lang/funcs/{facts/core => core/coresys}/load_fact_posix.go (95%) rename lang/funcs/core/{exchange_polyfunc.go => exchange_func.go} (100%) rename lang/funcs/{simplepoly/len_polyfunc.go => core/len_func.go} (93%) rename lang/funcs/core/{schedule_polyfunc.go => schedule_func.go} (100%) rename lang/funcs/core/{template_polyfunc.go => template_func.go} (98%) create mode 100644 lang/interfaces/const.go diff --git a/docs/function-guide.md b/docs/function-guide.md index 8461814c..65184840 100644 --- a/docs/function-guide.md +++ b/docs/function-guide.md @@ -124,16 +124,15 @@ An example explains it best: ### Example ```golang -package simplepoly - import ( "fmt" "github.com/purpleidea/mgmt/lang/types" + "github.com/purpleidea/mgmt/lang/funcs/simplepoly" ) func init() { - Register("len", []*types.FuncValue{ + simplepoly.Register("len", []*types.FuncValue{ { T: types.NewType("func([]variant) int"), V: Len, diff --git a/docs/language-guide.md b/docs/language-guide.md index dbb75546..51946347 100644 --- a/docs/language-guide.md +++ b/docs/language-guide.md @@ -285,11 +285,12 @@ class baz($a str, $b) { Classes can also be nested within other classes. Here's a contrived example: ```mcl +import "fmt" class c1($a, $b) { # nested class definition class c2($c) { test $a { - stringptr => printf("%s is %d", $b, $c), + stringptr => fmt.printf("%s is %d", $b, $c), } } diff --git a/examples/lang/answer.mcl b/examples/lang/answer.mcl index 2096f728..0de5f3bf 100644 --- a/examples/lang/answer.mcl +++ b/examples/lang/answer.mcl @@ -1,4 +1,6 @@ # it was a lovely surprise to me, when i realized that mgmt had the answer! +import "fmt" +import "example" print "answer" { - msg => printf("the answer to life, the universe, and everything is: %d", answer()), + msg => fmt.printf("the answer to life, the universe, and everything is: %d", example.answer()), } diff --git a/examples/lang/contains0.mcl b/examples/lang/contains0.mcl index e434fc6a..374f8014 100644 --- a/examples/lang/contains0.mcl +++ b/examples/lang/contains0.mcl @@ -1,3 +1,6 @@ +import "fmt" +import "sys" + $set = ["a", "b", "c", "d",] $c1 = "x1" in ["x1", "x2", "x3",] @@ -5,18 +8,18 @@ $c2 = 42 in [4, 13, 42,] $c3 = "x" in $set $c4 = "b" in $set -$s = printf("1: %t, 2: %t, 3: %t, 4: %t\n", $c1, $c2, $c3, $c4) +$s = fmt.printf("1: %t, 2: %t, 3: %t, 4: %t\n", $c1, $c2, $c3, $c4) file "/tmp/mgmt/contains" { content => $s, } -$x = if hostname() in ["h1", "h3",] { - printf("i (%s) am one of the chosen few!\n", hostname()) +$x = if sys.hostname() in ["h1", "h3",] { + fmt.printf("i (%s) am one of the chosen few!\n", sys.hostname()) } else { - printf("i (%s) was not chosen :(\n", hostname()) + fmt.printf("i (%s) was not chosen :(\n", sys.hostname()) } -file "/tmp/mgmt/hello-${hostname()}" { +file "/tmp/mgmt/hello-${sys.hostname()}" { content => $x, } diff --git a/examples/lang/datetime1.mcl b/examples/lang/datetime1.mcl index e7654684..2d7f737b 100644 --- a/examples/lang/datetime1.mcl +++ b/examples/lang/datetime1.mcl @@ -1,4 +1,6 @@ -$d = datetime() +import "datetime" + +$d = datetime.now() file "/tmp/mgmt/datetime" { content => template("Hello! It is now: {{ datetime_print . }}\n", $d), } diff --git a/examples/lang/datetime2.mcl b/examples/lang/datetime2.mcl index 16a7e2d1..9ca66dbe 100644 --- a/examples/lang/datetime2.mcl +++ b/examples/lang/datetime2.mcl @@ -1,11 +1,14 @@ -$secplusone = datetime() + $ayear +import "datetime" +import "sys" + +$secplusone = datetime.now() + $ayear # note the order of the assignment (year can come later in the code) $ayear = 60 * 60 * 24 * 365 # is a year in seconds (31536000) $tmplvalues = struct{year => $secplusone, load => $theload,} -$theload = structlookup(load(), "x1") +$theload = structlookup(sys.load(), "x1") if 5 > 3 { file "/tmp/mgmt/datetime" { diff --git a/examples/lang/datetime3.mcl b/examples/lang/datetime3.mcl index a5591bdc..5667ad52 100644 --- a/examples/lang/datetime3.mcl +++ b/examples/lang/datetime3.mcl @@ -1,11 +1,14 @@ -$secplusone = datetime() + $ayear +import "datetime" +import "sys" + +$secplusone = datetime.now() + $ayear # note the order of the assignment (year can come later in the code) $ayear = 60 * 60 * 24 * 365 # is a year in seconds (31536000) $tmplvalues = struct{year => $secplusone, load => $theload, vumeter => $vumeter,} -$theload = structlookup(load(), "x1") +$theload = structlookup(sys.load(), "x1") $vumeter = vumeter("====", 10, 0.9) diff --git a/examples/lang/env0.mcl b/examples/lang/env0.mcl index 4e7c7ea6..18d9c276 100644 --- a/examples/lang/env0.mcl +++ b/examples/lang/env0.mcl @@ -1,20 +1,23 @@ # read and print environment variable # env TEST=123 EMPTY= ./mgmt run --tmp-prefix --lang=examples/lang/env0.mcl --converged-timeout=5 -$x = getenv("TEST", "321") +import "fmt" +import "sys" + +$x = sys.getenv("TEST", "321") print "print1" { - msg => printf("the value of the environment variable TEST is: %s", $x), + msg => fmt.printf("the value of the environment variable TEST is: %s", $x), } -$y = getenv("DOESNOTEXIT", "321") +$y = sys.getenv("DOESNOTEXIT", "321") print "print2" { - msg => printf("environment variable DOESNOTEXIT does not exist, defaulting to: %s", $y), + msg => fmt.printf("environment variable DOESNOTEXIT does not exist, defaulting to: %s", $y), } -$z = getenv("EMPTY", "456") +$z = sys.getenv("EMPTY", "456") print "print3" { - msg => printf("same goes for epmty variables like EMPTY: %s", $z), + msg => fmt.printf("same goes for epmty variables like EMPTY: %s", $z), } diff --git a/examples/lang/env1.mcl b/examples/lang/env1.mcl index 001223d0..aa57c2cb 100644 --- a/examples/lang/env1.mcl +++ b/examples/lang/env1.mcl @@ -1,9 +1,12 @@ -$env = env() +import "fmt" +import "sys" + +$env = sys.env() $m = maplookup($env, "GOPATH", "") print "print0" { - msg => if hasenv("GOPATH") { - printf("GOPATH is: %s", $m) + msg => if sys.hasenv("GOPATH") { + fmt.printf("GOPATH is: %s", $m) } else { "GOPATH is missing!" }, diff --git a/examples/lang/exchange0.mcl b/examples/lang/exchange0.mcl index 97af41e9..731a273c 100644 --- a/examples/lang/exchange0.mcl +++ b/examples/lang/exchange0.mcl @@ -5,9 +5,11 @@ # time ./mgmt run --lang examples/lang/exchange0.mcl --hostname h3 --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2383 --server-urls http://127.0.0.1:2384 --tmp-prefix --no-pgp # time ./mgmt run --lang examples/lang/exchange0.mcl --hostname h4 --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2385 --server-urls http://127.0.0.1:2386 --tmp-prefix --no-pgp +import "sys" + $rand = random1(8) $exchanged = exchange("keyns", $rand) -file "/tmp/mgmt/exchange-${hostname()}" { +file "/tmp/mgmt/exchange-${sys.hostname()}" { content => template("Found: {{ . }}\n", $exchanged), } diff --git a/examples/lang/history1.mcl b/examples/lang/history1.mcl index 541f328b..66c77fd9 100644 --- a/examples/lang/history1.mcl +++ b/examples/lang/history1.mcl @@ -1,4 +1,6 @@ -$dt = datetime() +import "datetime" + +$dt = datetime.now() $hystvalues = {"ix0" => $dt, "ix1" => $dt{1}, "ix2" => $dt{2}, "ix3" => $dt{3},} diff --git a/examples/lang/hostname0.mcl b/examples/lang/hostname0.mcl index 4538a350..264e410a 100644 --- a/examples/lang/hostname0.mcl +++ b/examples/lang/hostname0.mcl @@ -1,4 +1,6 @@ -file "/tmp/mgmt/${hostname()}" { - content => "hello from ${hostname()}!\n", +import "sys" + +file "/tmp/mgmt/${sys.hostname()}" { + content => "hello from ${sys.hostname()}!\n", state => "exists", } diff --git a/examples/lang/len0.mcl b/examples/lang/len0.mcl index 251ec3b1..72f320f5 100644 --- a/examples/lang/len0.mcl +++ b/examples/lang/len0.mcl @@ -1,9 +1,11 @@ +import "fmt" + $x1 = ["a", "b", "c", "d",] print "print4" { - msg => printf("length is: %d", len($x1)), + msg => fmt.printf("length is: %d", len($x1)), } $x2 = {"a" => 1, "b" => 2, "c" => 3,} print "print3" { - msg => printf("length is: %d", len($x2)), + msg => fmt.printf("length is: %d", len($x2)), } diff --git a/examples/lang/load0.mcl b/examples/lang/load0.mcl index 960fe4e5..8333b57e 100644 --- a/examples/lang/load0.mcl +++ b/examples/lang/load0.mcl @@ -1,9 +1,12 @@ -$theload = load() +import "fmt" +import "sys" + +$theload = sys.load() $x1 = structlookup($theload, "x1") $x5 = structlookup($theload, "x5") $x15 = structlookup($theload, "x15") print "print1" { - msg => printf("load average: %f, %f, %f", $x1, $x5, $x15), + msg => fmt.printf("load average: %f, %f, %f", $x1, $x5, $x15), } diff --git a/examples/lang/maplookup1.mcl b/examples/lang/maplookup1.mcl index 2a7aca28..12f1a366 100644 --- a/examples/lang/maplookup1.mcl +++ b/examples/lang/maplookup1.mcl @@ -1,13 +1,15 @@ +import "fmt" + $m = {"k1" => 42, "k2" => 13,} $found = maplookup($m, "k1", 99) print "print1" { - msg => printf("found value of: %d", $found), + msg => fmt.printf("found value of: %d", $found), } $notfound = maplookup($m, "k3", 99) print "print2" { - msg => printf("notfound value of: %d", $notfound), + msg => fmt.printf("notfound value of: %d", $notfound), } diff --git a/examples/lang/math1.mcl b/examples/lang/math1.mcl index 98a46331..518b9914 100644 --- a/examples/lang/math1.mcl +++ b/examples/lang/math1.mcl @@ -1,4 +1,6 @@ +import "fmt" + test "t1" { int64 => (4 + 32) * 15 - 8, - anotherstr => printf("the answer is: %d", 42), + anotherstr => fmt.printf("the answer is: %d", 42), } diff --git a/examples/lang/math2.mcl b/examples/lang/math2.mcl index 65bf9dfe..664d8582 100644 --- a/examples/lang/math2.mcl +++ b/examples/lang/math2.mcl @@ -1,3 +1,6 @@ +import "fmt" +import "math" + print "print0" { - msg => printf("13.0 ^ 4.2 is: %f", pow(13.0, 4.2)), + msg => fmt.printf("13.0 ^ 4.2 is: %f", math.pow(13.0, 4.2)), } diff --git a/examples/lang/printf1.mcl b/examples/lang/printf1.mcl index 9a3b9abf..ab8a97a9 100644 --- a/examples/lang/printf1.mcl +++ b/examples/lang/printf1.mcl @@ -1,8 +1,10 @@ +import "fmt" + test "printf-a" { - anotherstr => printf("the %s is: %d", "answer", 42), + anotherstr => fmt.printf("the %s is: %d", "answer", 42), } $format = "a %s is: %f" test "printf-b" { - anotherstr => printf($format, "cool number", 3.14159), + anotherstr => fmt.printf($format, "cool number", 3.14159), } diff --git a/examples/lang/schedule0.mcl b/examples/lang/schedule0.mcl index cbbc05f9..2b4979b9 100644 --- a/examples/lang/schedule0.mcl +++ b/examples/lang/schedule0.mcl @@ -1,3 +1,5 @@ +import "sys" + # here are all the possible options: #$opts = struct{strategy => "rr", max => 3, reuse => false, ttl => 10,} @@ -13,6 +15,6 @@ $set = schedule("xsched", $opts) # and if you want, you can omit the options entirely: #$set = schedule("xsched") -file "/tmp/mgmt/scheduled-${hostname()}" { +file "/tmp/mgmt/scheduled-${sys.hostname()}" { content => template("set: {{ . }}\n", $set), } diff --git a/examples/lang/sendrecv3.mcl b/examples/lang/sendrecv3.mcl index 898c1e98..17c998bf 100644 --- a/examples/lang/sendrecv3.mcl +++ b/examples/lang/sendrecv3.mcl @@ -1,3 +1,5 @@ +import "fmt" + $ns = "estate" $exchanged = kvlookup($ns) $state = maplookup($exchanged, $hostname, "default") @@ -16,6 +18,6 @@ Exec["exec0"].output -> Kv["kv0"].value if $state != "default" { file "/tmp/mgmt/state" { - content => printf("state: %s\n", $state), + content => fmt.printf("state: %s\n", $state), } } diff --git a/examples/lang/structlookup1.mcl b/examples/lang/structlookup1.mcl index c3812246..45690364 100644 --- a/examples/lang/structlookup1.mcl +++ b/examples/lang/structlookup1.mcl @@ -1,13 +1,15 @@ +import "fmt" + $st = struct{f1 => 42, f2 => true, f3 => 3.14,} $f1 = structlookup($st, "f1") print "print1" { - msg => printf("f1 field is: %d", $f1), + msg => fmt.printf("f1 field is: %d", $f1), } $f2 = structlookup($st, "f2") print "print2" { - msg => printf("f2 field is: %t", $f2), + msg => fmt.printf("f2 field is: %t", $f2), } diff --git a/examples/lang/template0.mcl b/examples/lang/template0.mcl index d4a1b09f..5106faa8 100644 --- a/examples/lang/template0.mcl +++ b/examples/lang/template0.mcl @@ -1,8 +1,11 @@ +import "fmt" +import "example" + $answer = 42 -$s = int2str($answer) +$s = example.int2str($answer) print "print1" { - msg => printf("an str is: %s", $s), + msg => fmt.printf("an str is: %s", $s), } print "print2" { diff --git a/lang/funcs/core/core.go b/lang/funcs/core/core.go new file mode 100644 index 00000000..68d8a48f --- /dev/null +++ b/lang/funcs/core/core.go @@ -0,0 +1,27 @@ +// 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 + +import ( + // import so the funcs register + _ "github.com/purpleidea/mgmt/lang/funcs/core/coredatetime" + _ "github.com/purpleidea/mgmt/lang/funcs/core/coreexample" + _ "github.com/purpleidea/mgmt/lang/funcs/core/corefmt" + _ "github.com/purpleidea/mgmt/lang/funcs/core/coremath" + _ "github.com/purpleidea/mgmt/lang/funcs/core/coresys" +) diff --git a/lang/funcs/core/coredatetime/coredatetime.go b/lang/funcs/core/coredatetime/coredatetime.go new file mode 100644 index 00000000..a6f2065a --- /dev/null +++ b/lang/funcs/core/coredatetime/coredatetime.go @@ -0,0 +1,23 @@ +// 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 coredatetime + +const ( + // moduleName is the prefix given to all the functions in this module. + moduleName = "datetime" +) diff --git a/lang/funcs/facts/core/datetime_fact.go b/lang/funcs/core/coredatetime/now_fact.go similarity index 94% rename from lang/funcs/facts/core/datetime_fact.go rename to lang/funcs/core/coredatetime/now_fact.go index 2b60bccf..22e8229a 100644 --- a/lang/funcs/facts/core/datetime_fact.go +++ b/lang/funcs/core/coredatetime/now_fact.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 core // TODO: should this be in its own individual package? +package coredatetime import ( "time" @@ -25,7 +25,7 @@ import ( ) func init() { - facts.Register("datetime", func() facts.Fact { return &DateTimeFact{} }) // must register the fact and name + facts.ModuleRegister(moduleName, "now", func() facts.Fact { return &DateTimeFact{} }) // must register the fact and name } // DateTimeFact is a fact which returns the current date and time. diff --git a/lang/funcs/simple/datetime_print_func.go b/lang/funcs/core/coredatetime/print_func.go similarity index 87% rename from lang/funcs/simple/datetime_print_func.go rename to lang/funcs/core/coredatetime/print_func.go index 9baf0ab3..3340afef 100644 --- a/lang/funcs/simple/datetime_print_func.go +++ b/lang/funcs/core/coredatetime/print_func.go @@ -15,19 +15,19 @@ // 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 coredatetime import ( "fmt" "time" + "github.com/purpleidea/mgmt/lang/funcs/simple" "github.com/purpleidea/mgmt/lang/types" ) func init() { - // TODO: should we support namespacing these, eg: datetime.print ? // FIXME: consider renaming this to printf, and add in a format string? - Register("datetime_print", &types.FuncValue{ + simple.ModuleRegister(moduleName, "print", &types.FuncValue{ T: types.NewType("func(a int) str"), V: func(input []types.Value) (types.Value, error) { epochDelta := input[0].Int() diff --git a/lang/funcs/simple/answer_func.go b/lang/funcs/core/coreexample/answer_func.go similarity index 89% rename from lang/funcs/simple/answer_func.go rename to lang/funcs/core/coreexample/answer_func.go index 081daaeb..4841c500 100644 --- a/lang/funcs/simple/answer_func.go +++ b/lang/funcs/core/coreexample/answer_func.go @@ -15,9 +15,10 @@ // 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 coreexample import ( + "github.com/purpleidea/mgmt/lang/funcs/simple" "github.com/purpleidea/mgmt/lang/types" ) @@ -25,7 +26,7 @@ import ( const Answer = 42 func init() { - Register("answer", &types.FuncValue{ + simple.ModuleRegister(moduleName, "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/core/coreexample/coreexample.go b/lang/funcs/core/coreexample/coreexample.go new file mode 100644 index 00000000..e90c5db0 --- /dev/null +++ b/lang/funcs/core/coreexample/coreexample.go @@ -0,0 +1,23 @@ +// 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 coreexample + +const ( + // moduleName is the prefix given to all the functions in this module. + moduleName = "example" +) diff --git a/lang/funcs/simple/example_errorbool_func.go b/lang/funcs/core/coreexample/errorbool_func.go similarity index 85% rename from lang/funcs/simple/example_errorbool_func.go rename to lang/funcs/core/coreexample/errorbool_func.go index b3e4905e..4fe01ab6 100644 --- a/lang/funcs/simple/example_errorbool_func.go +++ b/lang/funcs/core/coreexample/errorbool_func.go @@ -15,17 +15,17 @@ // 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 coreexample import ( "fmt" + "github.com/purpleidea/mgmt/lang/funcs/simple" "github.com/purpleidea/mgmt/lang/types" ) func init() { - // TODO: should we support namespacing these, eg: example.errorbool ? - Register("example_errorbool", &types.FuncValue{ + simple.ModuleRegister(moduleName, "errorbool", &types.FuncValue{ T: types.NewType("func(a bool) str"), V: func(input []types.Value) (types.Value, error) { if input[0].Bool() { diff --git a/lang/funcs/facts/core/flipflop_fact.go b/lang/funcs/core/coreexample/flipflop_fact.go similarity index 91% rename from lang/funcs/facts/core/flipflop_fact.go rename to lang/funcs/core/coreexample/flipflop_fact.go index e2c7a70b..cace4d2f 100644 --- a/lang/funcs/facts/core/flipflop_fact.go +++ b/lang/funcs/core/coreexample/flipflop_fact.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 core // TODO: should this be in its own individual package? +package coreexample import ( "time" @@ -25,8 +25,7 @@ import ( ) func init() { - // TODO: rename these `play` facts to start with a test_ prefix or similar - facts.Register("flipflop", func() facts.Fact { return &FlipFlopFact{} }) // must register the fact and name + facts.ModuleRegister(moduleName, "flipflop", func() facts.Fact { return &FlipFlopFact{} }) // must register the fact and name } // FlipFlopFact is a fact which flips a bool repeatedly. This is an example fact diff --git a/lang/funcs/simple/int2str_func.go b/lang/funcs/core/coreexample/int2str_func.go similarity index 88% rename from lang/funcs/simple/int2str_func.go rename to lang/funcs/core/coreexample/int2str_func.go index c7550f01..f3e007a4 100644 --- a/lang/funcs/simple/int2str_func.go +++ b/lang/funcs/core/coreexample/int2str_func.go @@ -15,16 +15,17 @@ // 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 coreexample import ( "fmt" + "github.com/purpleidea/mgmt/lang/funcs/simple" "github.com/purpleidea/mgmt/lang/types" ) func init() { - Register("int2str", &types.FuncValue{ + simple.ModuleRegister(moduleName, "int2str", &types.FuncValue{ T: types.NewType("func(a int) str"), V: func(input []types.Value) (types.Value, error) { return &types.StrValue{ diff --git a/lang/funcs/core/vumeter_func.go b/lang/funcs/core/coreexample/vumeter_func.go similarity index 96% rename from lang/funcs/core/vumeter_func.go rename to lang/funcs/core/coreexample/vumeter_func.go index f7b7209e..7032b166 100644 --- a/lang/funcs/core/vumeter_func.go +++ b/lang/funcs/core/coreexample/vumeter_func.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 core // TODO: should this be in its own individual package? +package coreexample import ( "fmt" @@ -34,7 +34,7 @@ import ( ) func init() { - funcs.Register("vumeter", func() interfaces.Func { return &VUMeterFunc{} }) // must register the func and name + funcs.ModuleRegister(moduleName, "vumeter", func() interfaces.Func { return &VUMeterFunc{} }) // must register the func and name } // VUMeterFunc is a gimmic function to display a vu meter from the microphone. diff --git a/lang/funcs/core/corefmt/corefmt.go b/lang/funcs/core/corefmt/corefmt.go new file mode 100644 index 00000000..84ece4c7 --- /dev/null +++ b/lang/funcs/core/corefmt/corefmt.go @@ -0,0 +1,23 @@ +// 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 corefmt + +const ( + // moduleName is the prefix given to all the functions in this module. + moduleName = "fmt" +) diff --git a/lang/funcs/core/printf_polyfunc.go b/lang/funcs/core/corefmt/printf_func.go similarity index 98% rename from lang/funcs/core/printf_polyfunc.go rename to lang/funcs/core/corefmt/printf_func.go index 2b4b9ddb..f6c796ea 100644 --- a/lang/funcs/core/printf_polyfunc.go +++ b/lang/funcs/core/corefmt/printf_func.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 core // TODO: should this be in its own individual package? +package corefmt import ( "fmt" @@ -29,7 +29,7 @@ import ( ) func init() { - funcs.Register("printf", func() interfaces.Func { return &PrintfFunc{} }) + funcs.ModuleRegister(moduleName, "printf", func() interfaces.Func { return &PrintfFunc{} }) } const ( diff --git a/lang/funcs/core/coremath/coremath.go b/lang/funcs/core/coremath/coremath.go new file mode 100644 index 00000000..89721ead --- /dev/null +++ b/lang/funcs/core/coremath/coremath.go @@ -0,0 +1,23 @@ +// 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 coremath + +const ( + // moduleName is the prefix given to all the functions in this module. + moduleName = "math" +) diff --git a/lang/funcs/simple/math_func.go b/lang/funcs/core/coremath/pow_func.go similarity index 92% rename from lang/funcs/simple/math_func.go rename to lang/funcs/core/coremath/pow_func.go index e29bef0a..6629d7d9 100644 --- a/lang/funcs/simple/math_func.go +++ b/lang/funcs/core/coremath/pow_func.go @@ -15,17 +15,18 @@ // 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 coremath import ( "fmt" "math" + "github.com/purpleidea/mgmt/lang/funcs/simple" "github.com/purpleidea/mgmt/lang/types" ) func init() { - Register("pow", &types.FuncValue{ + simple.ModuleRegister(moduleName, "pow", &types.FuncValue{ T: types.NewType("func(x float, y float) float"), V: Pow, }) diff --git a/lang/funcs/core/coresys/coresys.go b/lang/funcs/core/coresys/coresys.go new file mode 100644 index 00000000..d202bf0f --- /dev/null +++ b/lang/funcs/core/coresys/coresys.go @@ -0,0 +1,23 @@ +// 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 coresys + +const ( + // moduleName is the prefix given to all the functions in this module. + moduleName = "sys" +) diff --git a/lang/funcs/simple/env_func.go b/lang/funcs/core/coresys/env_func.go similarity index 87% rename from lang/funcs/simple/env_func.go rename to lang/funcs/core/coresys/env_func.go index 34d6a117..8e24dd3f 100644 --- a/lang/funcs/simple/env_func.go +++ b/lang/funcs/core/coresys/env_func.go @@ -15,29 +15,30 @@ // 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 coresys import ( "os" "strings" + "github.com/purpleidea/mgmt/lang/funcs/simple" "github.com/purpleidea/mgmt/lang/types" ) func init() { - Register("getenv", &types.FuncValue{ + simple.ModuleRegister(moduleName, "getenv", &types.FuncValue{ T: types.NewType("func(str) str"), V: GetEnv, }) - Register("defaultenv", &types.FuncValue{ + simple.ModuleRegister(moduleName, "defaultenv", &types.FuncValue{ T: types.NewType("func(str, str) str"), V: DefaultEnv, }) - Register("hasenv", &types.FuncValue{ + simple.ModuleRegister(moduleName, "hasenv", &types.FuncValue{ T: types.NewType("func(str) bool"), V: HasEnv, }) - Register("env", &types.FuncValue{ + simple.ModuleRegister(moduleName, "env", &types.FuncValue{ T: types.NewType("func() map{str: str}"), V: Env, }) diff --git a/lang/funcs/facts/core/hostname_fact.go b/lang/funcs/core/coresys/hostname_fact.go similarity index 92% rename from lang/funcs/facts/core/hostname_fact.go rename to lang/funcs/core/coresys/hostname_fact.go index 88926700..5ea555c7 100644 --- a/lang/funcs/facts/core/hostname_fact.go +++ b/lang/funcs/core/coresys/hostname_fact.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 core // TODO: should this be in its own individual package? +package coresys import ( "github.com/purpleidea/mgmt/lang/funcs/facts" @@ -23,7 +23,7 @@ import ( ) func init() { - facts.Register("hostname", func() facts.Fact { return &HostnameFact{} }) // must register the fact and name + facts.ModuleRegister(moduleName, "hostname", func() facts.Fact { return &HostnameFact{} }) // must register the fact and name } // HostnameFact is a function that returns the hostname. diff --git a/lang/funcs/facts/core/load_fact.go b/lang/funcs/core/coresys/load_fact.go similarity index 94% rename from lang/funcs/facts/core/load_fact.go rename to lang/funcs/core/coresys/load_fact.go index 2402bcee..1a687df3 100644 --- a/lang/funcs/facts/core/load_fact.go +++ b/lang/funcs/core/coresys/load_fact.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 core // TODO: should this be in its own individual package? +package coresys import ( "time" @@ -31,7 +31,7 @@ const ( ) func init() { - facts.Register("load", func() facts.Fact { return &LoadFact{} }) // must register the fact and name + facts.ModuleRegister(moduleName, "load", func() facts.Fact { return &LoadFact{} }) // must register the fact and name } // LoadFact is a fact which returns the current system load. diff --git a/lang/funcs/facts/core/load_fact_darwin.go b/lang/funcs/core/coresys/load_fact_darwin.go similarity index 94% rename from lang/funcs/facts/core/load_fact_darwin.go rename to lang/funcs/core/coresys/load_fact_darwin.go index f546c2e5..6d27b484 100644 --- a/lang/funcs/facts/core/load_fact_darwin.go +++ b/lang/funcs/core/coresys/load_fact_darwin.go @@ -17,7 +17,7 @@ // +build darwin -package core // TODO: should this be in its own individual package? +package coresys /* #include diff --git a/lang/funcs/facts/core/load_fact_posix.go b/lang/funcs/core/coresys/load_fact_posix.go similarity index 95% rename from lang/funcs/facts/core/load_fact_posix.go rename to lang/funcs/core/coresys/load_fact_posix.go index f80c2d99..d9e80720 100644 --- a/lang/funcs/facts/core/load_fact_posix.go +++ b/lang/funcs/core/coresys/load_fact_posix.go @@ -17,7 +17,7 @@ // +build !darwin -package core // TODO: should this be in its own individual package? +package coresys import ( "syscall" diff --git a/lang/funcs/core/exchange_polyfunc.go b/lang/funcs/core/exchange_func.go similarity index 100% rename from lang/funcs/core/exchange_polyfunc.go rename to lang/funcs/core/exchange_func.go diff --git a/lang/funcs/simplepoly/len_polyfunc.go b/lang/funcs/core/len_func.go similarity index 93% rename from lang/funcs/simplepoly/len_polyfunc.go rename to lang/funcs/core/len_func.go index 59632a07..16f43be7 100644 --- a/lang/funcs/simplepoly/len_polyfunc.go +++ b/lang/funcs/core/len_func.go @@ -15,16 +15,17 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package simplepoly // TODO: should this be in its own individual package? +package core import ( "fmt" + "github.com/purpleidea/mgmt/lang/funcs/simplepoly" "github.com/purpleidea/mgmt/lang/types" ) func init() { - Register("len", []*types.FuncValue{ + simplepoly.Register("len", []*types.FuncValue{ { T: types.NewType("func([]variant) int"), V: Len, diff --git a/lang/funcs/core/schedule_polyfunc.go b/lang/funcs/core/schedule_func.go similarity index 100% rename from lang/funcs/core/schedule_polyfunc.go rename to lang/funcs/core/schedule_func.go diff --git a/lang/funcs/core/template_polyfunc.go b/lang/funcs/core/template_func.go similarity index 98% rename from lang/funcs/core/template_polyfunc.go rename to lang/funcs/core/template_func.go index 22f88c4d..3a9a3ee9 100644 --- a/lang/funcs/core/template_polyfunc.go +++ b/lang/funcs/core/template_func.go @@ -188,6 +188,8 @@ func (obj *TemplateFunc) run(templateText string, vars types.Value) (string, err // FIXME: should we do this once in init() instead, or in the Register // function in the simple package? // TODO: loop through this map in a sorted, deterministic order + // XXX: should this use the scope instead (so imports are used properly) ? + // XXX: dots are not valid here, so maybe replace dots with underscores so we can do fmt_print??? for name, fn := range simple.RegisteredFuncs { if _, exists := funcMap[name]; exists { obj.init.Logf("warning, existing function named: `%s` exists", name) diff --git a/lang/funcs/facts/facts.go b/lang/funcs/facts/facts.go index fb01a61d..7a94bce6 100644 --- a/lang/funcs/facts/facts.go +++ b/lang/funcs/facts/facts.go @@ -47,6 +47,12 @@ func Register(name string, fn func() Fact) { RegisteredFacts[name] = fn } +// ModuleRegister is exactly like Register, except that it registers within a +// named module. This is a helper function. +func ModuleRegister(module, name string, fn func() Fact) { + Register(module+funcs.ModuleSep+name, fn) +} + // Info is a static representation of some information about the fact. It is // used for static analysis and type checking. If you break this contract, you // might cause a panic. diff --git a/lang/funcs/funcs.go b/lang/funcs/funcs.go index ea79509d..21518f90 100644 --- a/lang/funcs/funcs.go +++ b/lang/funcs/funcs.go @@ -20,10 +20,18 @@ package funcs import ( "fmt" + "strings" "github.com/purpleidea/mgmt/lang/interfaces" ) +const ( + // ModuleSep is the character used for the module scope separation. For + // example when using `fmt.printf` or `math.sin` this is the char used. + // It is included here for convenience when importing this package. + ModuleSep = interfaces.ModuleSep +) + // registeredFuncs is a global map of all possible funcs which can be used. You // should never touch this map directly. Use methods like Register instead. It // includes implementations which also satisfy PolyFunc as well. @@ -32,15 +40,33 @@ var registeredFuncs = make(map[string]func() interfaces.Func) // must initialize // Register takes a func and its name and makes it available for use. It is // commonly called in the init() method of the func at program startup. There is // no matching Unregister function. You may also register functions which -// satisfy the PolyFunc interface. +// satisfy the PolyFunc interface. To register a function which lives in a +// module, you must join the module name to the function name with the ModuleSep +// character. It is defined as a const and is probably the period character. func Register(name string, fn func() interfaces.Func) { if _, exists := registeredFuncs[name]; exists { panic(fmt.Sprintf("a func named %s is already registered", name)) } + + // can't contain more than one period in a row + if strings.Index(name, ModuleSep+ModuleSep) >= 0 { + panic(fmt.Sprintf("a func named %s is invalid", name)) + } + // can't start or end with a period + if strings.HasPrefix(name, ModuleSep) || strings.HasSuffix(name, ModuleSep) { + panic(fmt.Sprintf("a func named %s is invalid", name)) + } + //gob.Register(fn()) registeredFuncs[name] = fn } +// ModuleRegister is exactly like Register, except that it registers within a +// named module. +func ModuleRegister(module, name string, fn func() interfaces.Func) { + Register(module+ModuleSep+name, fn) +} + // Lookup returns a pointer to the function's struct. It may be convertible to a // PolyFunc if the particular function implements those additional methods. func Lookup(name string) (interfaces.Func, error) { @@ -50,3 +76,21 @@ func Lookup(name string) (interfaces.Func, error) { } return f(), nil } + +// LookupPrefix returns a map of names to functions that start with a module +// prefix. This search automatically adds the period separator. So if you want +// functions in the `fmt` package, search for `fmt`, not `fmt.` and it will find +// all the correctly registered functions. This removes that prefix from the +// result in the map keys that it returns. +func LookupPrefix(prefix string) (map[string]interfaces.Func, error) { + result := make(map[string]interfaces.Func) + for name, f := range registeredFuncs { + sep := prefix + ModuleSep + if !strings.HasPrefix(name, sep) { + continue + } + s := strings.TrimPrefix(name, sep) // TODO: is it okay to remove the prefix? + result[s] = f() // build + } + return result, nil +} diff --git a/lang/funcs/simple/simple.go b/lang/funcs/simple/simple.go index 37fba1e2..079e9f96 100644 --- a/lang/funcs/simple/simple.go +++ b/lang/funcs/simple/simple.go @@ -42,6 +42,12 @@ func Register(name string, fn *types.FuncValue) { funcs.Register(name, func() interfaces.Func { return &simpleFunc{Fn: fn} }) } +// ModuleRegister is exactly like Register, except that it registers within a +// named module. This is a helper function. +func ModuleRegister(module, name string, fn *types.FuncValue) { + Register(module+funcs.ModuleSep+name, fn) +} + // simpleFunc is a scaffolding function struct which fulfills the boiler-plate // for the function API, but that can run a very simple, static, pure function. type simpleFunc struct { diff --git a/lang/funcs/simplepoly/simplepoly.go b/lang/funcs/simplepoly/simplepoly.go index 87211403..cce234ed 100644 --- a/lang/funcs/simplepoly/simplepoly.go +++ b/lang/funcs/simplepoly/simplepoly.go @@ -62,6 +62,12 @@ func Register(name string, fns []*types.FuncValue) { funcs.Register(name, func() interfaces.Func { return &simplePolyFunc{Fns: fns} }) } +// ModuleRegister is exactly like Register, except that it registers within a +// named module. This is a helper function. +func ModuleRegister(module, name string, fns []*types.FuncValue) { + Register(module+funcs.ModuleSep+name, fns) +} + // simplePolyFunc is a scaffolding function struct which fulfills the // boiler-plate for the function API, but that can run a very simple, static, // pure, polymorphic function. diff --git a/lang/interfaces/const.go b/lang/interfaces/const.go new file mode 100644 index 00000000..38d2687d --- /dev/null +++ b/lang/interfaces/const.go @@ -0,0 +1,24 @@ +// 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 interfaces + +const ( + // ModuleSep is the character used for the module scope separation. For + // example when using `fmt.printf` or `math.sin` this is the char used. + ModuleSep = "." +) diff --git a/lang/lang.go b/lang/lang.go index 500ff5c9..83cccc49 100644 --- a/lang/lang.go +++ b/lang/lang.go @@ -24,10 +24,7 @@ import ( "github.com/purpleidea/mgmt/engine" "github.com/purpleidea/mgmt/lang/funcs" - _ "github.com/purpleidea/mgmt/lang/funcs/core" // import so the funcs register - _ "github.com/purpleidea/mgmt/lang/funcs/facts/core" // import so the facts register - _ "github.com/purpleidea/mgmt/lang/funcs/simple" // import so the funcs register - _ "github.com/purpleidea/mgmt/lang/funcs/simplepoly" // import so the funcs register + _ "github.com/purpleidea/mgmt/lang/funcs/core" // import so the funcs register "github.com/purpleidea/mgmt/lang/interfaces" "github.com/purpleidea/mgmt/lang/unification" "github.com/purpleidea/mgmt/pgraph" diff --git a/lang/lang_test.go b/lang/lang_test.go index 49ddf6f0..c729b4f4 100644 --- a/lang/lang_test.go +++ b/lang/lang_test.go @@ -26,7 +26,7 @@ import ( "github.com/purpleidea/mgmt/engine" "github.com/purpleidea/mgmt/engine/resources" - _ "github.com/purpleidea/mgmt/lang/funcs/facts/core" // load facts + _ "github.com/purpleidea/mgmt/lang/funcs/core" // import so the funcs register "github.com/purpleidea/mgmt/pgraph" "github.com/purpleidea/mgmt/util" @@ -497,9 +497,10 @@ func TestInterpretMany(t *testing.T) { // values = append(values, test{ // name: "double include different printf types allowed", // code: ` + // import "fmt" // class c1($a, $b) { // test $a { - // stringptr => printf("value is: %v", $b), + // stringptr => fmt.printf("value is: %v", $b), // } // } // include c1("t1", "hello") @@ -667,6 +668,8 @@ func TestInterpretMany(t *testing.T) { values = append(values, test{ name: "nested classes 1", code: ` + import "fmt" + include c1("t1", "hello") # test["t1"] -> hello is 42 include c1("t2", "world") # test["t2"] -> world is 13 @@ -674,7 +677,7 @@ func TestInterpretMany(t *testing.T) { # nested class definition class c2($c) { test $a { - stringptr => printf("%s is %d", $b, $c), + stringptr => fmt.printf("%s is %d", $b, $c), } } @@ -693,6 +696,8 @@ func TestInterpretMany(t *testing.T) { values = append(values, test{ name: "nested classes out of scope 1", code: ` + import "fmt" + include c1("t1", "hello") # test["t1"] -> hello is 42 include c2(99) # out of scope @@ -700,7 +705,7 @@ func TestInterpretMany(t *testing.T) { # nested class definition class c2($c) { test $a { - stringptr => printf("%s is %d", $b, $c), + stringptr => fmt.printf("%s is %d", $b, $c), } } @@ -742,13 +747,14 @@ func TestInterpretMany(t *testing.T) { // values = append(values, test{ // name: "recursive classes 1", // code: ` + // import "fmt" // $max = 3 // include c1(0) # start at zero // # test["done"] -> count is 3 // class c1($count) { // if $count == $max { // test "done" { - // stringptr => printf("count is %d", $count), + // stringptr => fmt.printf("count is %d", $count), // } // } else { // include c1($count + 1) @@ -779,6 +785,7 @@ func TestInterpretMany(t *testing.T) { // values = append(values, test{ // name: "recursive classes 2", // code: ` + // import "fmt" // include c1("ix", 3) // # test["ix:3"] -> count is 3 // # test["ix:2"] -> count is 2 @@ -787,12 +794,12 @@ func TestInterpretMany(t *testing.T) { // class c1($name, $count) { // if $count == 0 { // test "zero" { - // stringptr => printf("count is %d", $count), + // stringptr => fmt.printf("count is %d", $count), // } // } else { // include c1($name, $count - 1) // test "${name}:${count}" { - // stringptr => printf("count is %d", $count), + // stringptr => fmt.printf("count is %d", $count), // } // } // } @@ -806,12 +813,13 @@ func TestInterpretMany(t *testing.T) { values = append(values, test{ name: "recursive classes fail 1", code: ` + import "fmt" $max = 3 include c1(0) # start at zero class c1($count) { if $count == $max { test "done" { - stringptr => printf("count is %d", $count), + stringptr => fmt.printf("count is %d", $count), } } else { include c1($count + 1) # recursion not supported atm @@ -826,12 +834,13 @@ func TestInterpretMany(t *testing.T) { values = append(values, test{ name: "recursive classes fail 2", code: ` + import "fmt" $max = 5 include c1(0) # start at zero class c1($count) { if $count == $max { test "done" { - stringptr => printf("count is %d", $count), + stringptr => fmt.printf("count is %d", $count), } } else { include c2($count + 1) # recursion not supported atm @@ -840,7 +849,7 @@ func TestInterpretMany(t *testing.T) { class c2($count) { if $count == $max { test "done" { - stringptr => printf("count is %d", $count), + stringptr => fmt.printf("count is %d", $count), } } else { include c1($count + 1) # recursion not supported atm diff --git a/test/shell/env0.mcl b/test/shell/env0.mcl index 530d16e4..baa22dfc 100644 --- a/test/shell/env0.mcl +++ b/test/shell/env0.mcl @@ -1,19 +1,22 @@ -$tmpdir = defaultenv("TMPDIR", "/tmp") +import "fmt" +import "sys" -$x = getenv("TEST") -$y = getenv("DOESNOTEXIST") -$z = getenv("EMPTY") +$tmpdir = sys.defaultenv("TMPDIR", "/tmp") -$a = defaultenv("TEST", "321") -$b = defaultenv("DOESNOTEXIST", "321") -$c = defaultenv("EMPTY", "456") +$x = sys.getenv("TEST") +$y = sys.getenv("DOESNOTEXIST") +$z = sys.getenv("EMPTY") -$t = hasenv("TEST") -$f = hasenv("DOESNOTEXIST") +$a = sys.defaultenv("TEST", "321") +$b = sys.defaultenv("DOESNOTEXIST", "321") +$c = sys.defaultenv("EMPTY", "456") -$env = env() +$t = sys.hasenv("TEST") +$f = sys.hasenv("DOESNOTEXIST") + +$env = sys.env() $m = maplookup($env, "TEST", "321") file "${tmpdir}/environ" { - content => printf("%s,%s,%s:%s,%s,%s:%t,%t:%s", $x, $y, $z, $a, $b, $c, $t, $f, $m), + content => fmt.printf("%s,%s,%s:%s,%s,%s:%t,%t:%s", $x, $y, $z, $a, $b, $c, $t, $f, $m), } diff --git a/test/shell/load0.sh b/test/shell/load0.sh index 169306d4..94b0eadd 100755 --- a/test/shell/load0.sh +++ b/test/shell/load0.sh @@ -25,14 +25,17 @@ if [[ ! "$tmpdir" =~ "/tmp" ]]; then fi cat > "$tmpdir/load0.mcl" < printf("load average: %f, %f, %f", \$x1, \$x5, \$x15), + content => fmt.printf("load average: %f, %f, %f", \$x1, \$x5, \$x15), state => "exists", } EOF