diff --git a/lang/funcs/core/concat_func.go b/lang/funcs/core/concat_func.go new file mode 100644 index 00000000..08f8eb83 --- /dev/null +++ b/lang/funcs/core/concat_func.go @@ -0,0 +1,43 @@ +// Mgmt +// Copyright (C) 2013-2023+ 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 ( + "github.com/purpleidea/mgmt/lang/funcs" + "github.com/purpleidea/mgmt/lang/funcs/simple" + "github.com/purpleidea/mgmt/lang/types" +) + +const ( + // ConcatFuncName is the name this function is registered as. + ConcatFuncName = funcs.ConcatFuncName +) + +func init() { + simple.Register(ConcatFuncName, &types.FuncValue{ + T: types.NewType("func(a str, b str) str"), + V: Concat, + }) +} + +// Concat concatenates two strings together. +func Concat(input []types.Value) (types.Value, error) { + return &types.StrValue{ + V: input[0].Str() + input[1].Str(), + }, nil +} diff --git a/lang/funcs/funcs.go b/lang/funcs/funcs.go index ec2c0e10..5d77527b 100644 --- a/lang/funcs/funcs.go +++ b/lang/funcs/funcs.go @@ -44,6 +44,11 @@ const ( // CoreDir is the directory prefix where core mcl code is embedded. CoreDir = "core/" + + // ConcatFuncName is the name the concat function is registered as. It + // is listed here because it needs a well-known name that can be used by + // the string interpolation code. + ConcatFuncName = "concat" ) // registeredFuncs is a global map of all possible funcs which can be used. You diff --git a/lang/interpolate/interpolate.go b/lang/interpolate/interpolate.go index 56f213d1..b24c80fb 100644 --- a/lang/interpolate/interpolate.go +++ b/lang/interpolate/interpolate.go @@ -36,6 +36,10 @@ const ( // This can't properly escape a $ in the standard way. It's here in case // someone wants to play with it and examine how the AST stuff worked... UseHilInterpolation = false + + // UseOptimizedConcat uses a simpler to unify concat operator instead of + // the normal + operator which uses fancy polymorphic type unification. + UseOptimizedConcat = true ) // StrInterpolate interpolates a string and returns the representative AST. @@ -312,6 +316,20 @@ func concatExprListIntoCall(exprs []interfaces.Expr) (interfaces.Expr, error) { return nil, err } + // Faster variant, but doesn't allow potential future more exotic string + // interpolation which would need a more expressive plus operator. I do + // not think we'll ever need that, but leave it in for now as a const. + if UseOptimizedConcat { + return &ast.ExprCall{ + // NOTE: if we don't set the data field we need Init() called on it! + Name: funcs.ConcatFuncName, // concatenate the two strings with concat function + Args: []interfaces.Expr{ + head, // string arg + grouped, // nested function call which returns a string + }, + }, nil + } + return &ast.ExprCall{ // NOTE: if we don't set the data field we need Init() called on it! Name: funcs.OperatorFuncName, // concatenate the two strings with + operator diff --git a/lang/interpolate/interpolate_test.go b/lang/interpolate/interpolate_test.go index 18495e67..2af43a1d 100644 --- a/lang/interpolate/interpolate_test.go +++ b/lang/interpolate/interpolate_test.go @@ -90,11 +90,8 @@ func TestInterpolate0(t *testing.T) { } { fieldName := &ast.ExprCall{ - Name: funcs.OperatorFuncName, + Name: funcs.ConcatFuncName, Args: []interfaces.Expr{ - &ast.ExprStr{ - V: "+", - }, &ast.ExprStr{ V: "foo-", }, @@ -365,11 +362,8 @@ func TestInterpolateBasicStmt(t *testing.T) { }, } resName := &ast.ExprCall{ - Name: funcs.OperatorFuncName, + Name: funcs.ConcatFuncName, Args: []interfaces.Expr{ - &ast.ExprStr{ - V: "+", - }, &ast.ExprStr{ V: "t", }, @@ -421,12 +415,9 @@ func TestInterpolateBasicStmt(t *testing.T) { }, } resName := &ast.ExprCall{ - Name: funcs.OperatorFuncName, + Name: funcs.ConcatFuncName, // incorrect sig for this function, and now invalid interpolation Args: []interfaces.Expr{ - &ast.ExprStr{ - V: "+", - }, &ast.ExprStr{ V: "t", }, @@ -561,11 +552,8 @@ func TestInterpolateBasicExpr(t *testing.T) { V: "hello ${person_name}", } exp := &ast.ExprCall{ - Name: funcs.OperatorFuncName, + Name: funcs.ConcatFuncName, Args: []interfaces.Expr{ - &ast.ExprStr{ - V: "+", - }, &ast.ExprStr{ V: "hello ", }, @@ -611,11 +599,8 @@ func TestInterpolateBasicExpr(t *testing.T) { V: "sweetie${3.14159}", // invalid } exp := &ast.ExprCall{ - Name: funcs.OperatorFuncName, + Name: funcs.ConcatFuncName, Args: []interfaces.Expr{ - &ast.ExprStr{ - V: "+", - }, &ast.ExprStr{ V: "sweetie", }, @@ -636,11 +621,8 @@ func TestInterpolateBasicExpr(t *testing.T) { V: "i am: ${sys.hostname()}", } exp := &ast.ExprCall{ - Name: funcs.OperatorFuncName, + Name: funcs.ConcatFuncName, Args: []interfaces.Expr{ - &ast.ExprStr{ - V: "+", - }, &ast.ExprStr{ V: "i am: ", }, @@ -662,11 +644,8 @@ func TestInterpolateBasicExpr(t *testing.T) { V: "i am: ${blah(21, 12.3)}", } exp := &ast.ExprCall{ - Name: funcs.OperatorFuncName, + Name: funcs.ConcatFuncName, Args: []interfaces.Expr{ - &ast.ExprStr{ - V: "+", - }, &ast.ExprStr{ V: "i am: ", }, @@ -696,12 +675,9 @@ func TestInterpolateBasicExpr(t *testing.T) { // V: "i am: ${blah(21, -12.3)}", // } // exp := &ast.ExprCall{ - // Name: funcs.OperatorFuncName, + // Name: funcs.ConcatFuncName, // Args: []interfaces.Expr{ // &ast.ExprStr{ - // V: "+", - // }, - // &ast.ExprStr{ // V: "i am: ", // }, // &ast.ExprCall{ @@ -730,12 +706,9 @@ func TestInterpolateBasicExpr(t *testing.T) { // V: "sweetie${-3.14159}", // FIXME: only the negative breaks this // } // exp := &ast.ExprCall{ - // Name: funcs.OperatorFuncName, + // Name: funcs.ConcatFuncName, // Args: []interfaces.Expr{ // &ast.ExprStr{ - // V: "+", - // }, - // &ast.ExprStr{ // V: "sweetie", // }, // &ast.ExprFloat{ @@ -756,12 +729,9 @@ func TestInterpolateBasicExpr(t *testing.T) { // V: `i am: ${blah(42, "${foo}")}`, // } // exp := &ast.ExprCall{ - // Name: funcs.OperatorFuncName, + // Name: funcs.ConcatFuncName, // Args: []interfaces.Expr{ // &ast.ExprStr{ - // V: "+", - // }, - // &ast.ExprStr{ // V: "i am: ", // }, // &ast.ExprCall{