7 Commits

14 changed files with 193 additions and 58 deletions
+29
View File
@@ -0,0 +1,29 @@
Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Celestino Amoroso, nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+36 -20
View File
@@ -1,3 +1,6 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// dict-set-context.go
package text
@@ -41,30 +44,43 @@ func NewDictSetContextV(flags DictSetFlag, varStores []map[string]string) *DictS
return ctx
}
func (self *DictSetContext) GetVar(name string) (value string, exists bool) {
if value, exists = self.localStore[name]; !exists {
value, exists = findInStores(name, self.varStores)
func (ctx *DictSetContext) GetVar(name string) (value string, exists bool) {
if value, exists = ctx.localStore[name]; !exists {
value, exists = ctx.findInStores(name)
}
return
}
func (self *DictSetContext) SetVar(name string, value string) (repeatedValue string) {
self.localStore[name] = value
func (ctx *DictSetContext) SetVar(name string, value string) (repeatedValue string) {
ctx.localStore[name] = value
return value
}
func findInStores(key string, stores []map[string]string) (value string, found bool) {
for _, vars := range stores {
func (ctx *DictSetContext) findInStores(key string) (value string, found bool) {
if value, found = ctx.localStore[key]; !found {
for _, vars := range ctx.varStores {
if vars != nil {
if value, found = vars[key]; found {
break
}
}
}
}
return
}
func (self *DictSetContext) Handle(spec string, scanFlags ScannerFlag) (value string, err error) {
// func findInStores(key string, stores []map[string]string) (value string, found bool) {
// for _, vars := range stores {
// if vars != nil {
// if value, found = vars[key]; found {
// break
// }
// }
// }
// return
// }
func (ctx *DictSetContext) Handle(spec string, scanFlags ScannerFlag) (value string, err error) {
var found bool
var rawValue string
@@ -78,30 +94,30 @@ func (self *DictSetContext) Handle(spec string, scanFlags ScannerFlag) (value st
rawValue = name
found = true
} else {
rawValue, found = findInStores(name, self.varStores)
rawValue, found = ctx.findInStores(name)
}
}
self.SetVar(pipeNameKey, name) // NOTE Maybe this line can be deleted
ctx.SetVar(pipeNameKey, name) // NOTE Maybe this line can be deleted
origVarFound := found
for _, part := range parts {
var expandedPart string
self.varStores[0][pipeValueKey] = rawValue
self.SetVar(pipeValueKey, rawValue)
if expandedPart, err = Scan(self, part); err != nil {
// ctx.varStores[0][pipeValueKey] = rawValue
ctx.SetVar(pipeValueKey, rawValue)
if expandedPart, err = Scan(ctx, part); err != nil {
break
}
self.SetVar(pipeNameKey, name)
if rawValue, err = self.evalContent(origVarFound, rawValue, expandedPart); err != nil {
ctx.SetVar(pipeNameKey, name)
if rawValue, err = ctx.evalContent(origVarFound, rawValue, expandedPart); err != nil {
break
}
origVarFound = true
}
if err == nil {
if len(rawValue) == 0 && (self.flags&KeepVar) != 0 {
if len(rawValue) == 0 && (ctx.flags&KeepVar) != 0 {
value = Var(name)
} else {
value, err = Scan(self, rawValue)
value, err = Scan(ctx, rawValue)
//value = rawValue
}
}
@@ -113,7 +129,7 @@ func (self *DictSetContext) Handle(spec string, scanFlags ScannerFlag) (value st
return
}
func (self *DictSetContext) evalContent(varFound bool, rawValue string, alternateValue string) (value string, err error) {
func (ctx *DictSetContext) evalContent(varFound bool, rawValue string, alternateValue string) (value string, err error) {
var args []string
if args, err = sq.Split(alternateValue); err != nil {
return
@@ -124,8 +140,8 @@ func (self *DictSetContext) evalContent(varFound bool, rawValue string, alternat
op := args[0]
if strings.HasPrefix(op, "__") {
var f ExpanderFunc
if f, err = self.GetFunc(op); err == nil {
value, err = f(self, rawValue, args[1:])
if f, err = ctx.GetFunc(op); err == nil {
value, err = f(ctx, rawValue, args[1:])
}
} else if varFound {
value = rawValue
+10 -4
View File
@@ -1,3 +1,6 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// dict-set-context_test.go
package text
@@ -30,13 +33,17 @@ func TestNewDictSetContext(t *testing.T) {
err = fmt.Errorf("unsupported '%%%c' time specifier at %d", ch, i)
*/
now := time.Now()
yday := now.Add(-24*time.Hour)
yday := now.Add(-24 * time.Hour)
inputs := []inputType{
{ctx1, "${|__default ' abc cba '|__trim}", "abc cba", nil},
{ctx1, "${|__default two|__map ':' 'one:1' 'two:2' 'six:6'}", "2", nil},
{ctx1, "${|__default two|__map ':' 'one:1' 'TWO:2' 'six:6'}", "", nil},
{ctx1, "${|__default two|__map-nc ':' 'one:1' 'TWO:2' 'six:6'}", "2", nil},
{ctx2, `form: ${|__time-fmt now ""}`, fmt.Sprintf("form: %d-%02d-%02d %02d:%02d:%02d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second()), nil},
{ctx2, `form: ${|__time-fmt now "%-D"}`, fmt.Sprintf("form: %d-%d-%d", now.Year(), now.Month(), now.Day()), nil},
{ctx2, `form: ${|__time-fmt now "%:T"}`, fmt.Sprintf("form: %d:%d:%d", now.Hour(), now.Minute(), now.Second()), nil},
{ctx2, `form: ${|__time-fmt now "%02:T"}`,"", errors.New("invalid time specifier format at 3")},
{ctx2, `form: ${|__time-fmt ""}`,"", errors.New("time value is empty")},
{ctx2, `form: ${|__time-fmt now "%02:T"}`, "", errors.New("invalid time specifier format at 3")},
{ctx2, `form: ${|__time-fmt ""}`, "", errors.New("time value is empty")},
{ctx2, `form: ${|__time-fmt yesterday "%-D"}`, fmt.Sprintf("form: %d-%d-%d", yday.Year(), yday.Month(), yday.Day()), nil},
{ctx1, "${one|pippo}", "1", nil},
{ctx1, "${one|__default pluto}", "1", nil},
@@ -83,7 +90,6 @@ func TestNewDictSetContext(t *testing.T) {
if (gotErr != nil && input.wantErr == nil) || (gotErr == nil && input.wantErr != nil) || (gotErr.Error() != input.wantErr.Error()) {
t.Errorf("%d: %s(%q)/err = <%v>, want <%v>", i, funcName, input.source, gotErr, input.wantErr)
}
}
}
+40
View File
@@ -1,3 +1,6 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expander-base-funcs.go
package text
@@ -81,6 +84,40 @@ func ExFuncTrimSuffix(ctx ExpanderContext, varValue string, args []string) (resu
return
}
func ExFuncTrim(ctx ExpanderContext, varValue string, args []string) (result string, err error) {
result = strings.TrimSpace(varValue)
return
}
func ExFuncMap(ctx ExpanderContext, varValue string, args []string) (result string, err error) {
sep := args[0]
for _, pairSpec := range args[1:] {
pair := strings.SplitN(pairSpec, sep, 2)
if pair[0] == varValue {
if len(pair) == 2 {
result = pair[1]
}
break
}
}
return
}
func ExFuncMapNC(ctx ExpanderContext, varValue string, args []string) (result string, err error) {
sep := args[0]
varValue = strings.ToLower(varValue)
for _, pairSpec := range args[1:] {
pair := strings.SplitN(pairSpec, sep, 2)
if strings.ToLower(pair[0]) == varValue {
if len(pair) == 2 {
result = pair[1]
}
break
}
}
return
}
func AddBaseFuncs(ctx ExpanderContext) ExpanderContext {
ctx.AddFunc("__default", ExFuncDefault, 0, -1, "Returns the flow value if not empty, else all args joined; first arg is used as separator.")
ctx.AddFunc("__get", ExFuncGet, 0, 2, "Returns the value of the specified variable (arg1). With no arguments it returns the value of the variable having the flow value as name (same as __get ${.}). Arg2 is the default value. ")
@@ -88,6 +125,9 @@ func AddBaseFuncs(ctx ExpanderContext) ExpanderContext {
ctx.AddFunc("__join", ExFuncJoin, 1, -1, "Joins its args starting from the second. The first one (arg1) is the separator")
ctx.AddFunc("__lower", ExFuncLower, 0, 0, "Changes the flow value to lower case.")
ctx.AddFunc("__upper", ExFuncUpper, 0, 0, "Changes the flow value to upper case.")
ctx.AddFunc("__trim", ExFuncTrim, 0, 0, "Removes blanks on the left and on the right of the flow.")
ctx.AddFunc("__trim-suffix", ExFuncTrimSuffix, 1, 1, "Removes the specified suffix (arg1) from the flow.")
ctx.AddFunc("__map", ExFuncMap, 1, 1, "Returns the value mapped by the flow value as a key; e.g. two __map : 'one:1' 'two:2' 'six:6' returns 2")
ctx.AddFunc("__map-nc", ExFuncMapNC, 1, 1, "Same as __map but this comapares keys ignoring case differencies; e.g. two __map-nc : 'ONE:1' 'Two:2' 'sIX:6' returns 2")
return ctx
}
+4 -2
View File
@@ -1,3 +1,6 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expander-context.go
package text
@@ -125,7 +128,7 @@ func (self *BaseExpander) AddFunc(name string, f ExpanderFunc, minArgs, maxArgs
}
func (self *BaseExpander) Handle(varSpec string, flags ScannerFlag) (value string, err error) {
err = errors.New("maybe you did somwthing wrong")
err = errors.New("maybe you did something wrong")
return
}
@@ -137,4 +140,3 @@ func (self *BaseExpander) IterateFunc(op func(funcInfo *ExpanderFuncBlock) (err
}
return
}
+3
View File
@@ -1,2 +1,5 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expander-context_test.go
package text
+3
View File
@@ -1,3 +1,6 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expander-time-funcs.go
package text
+3
View File
@@ -1,3 +1,6 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expander.go
package text
+3
View File
@@ -1,3 +1,6 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// scanner_test.go
package text
+4 -3
View File
@@ -1,3 +1,6 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// scanner.go
package text
@@ -14,12 +17,11 @@ const (
EncryptValue
)
// Flags symbols in the abova iota order
// Flags symbols in the above iota order
const flagSymbols = "!=*"
const defaultVarIntro = '$'
type VariableHandler interface {
Handle(varSpec string, flags ScannerFlag) (value string, err error)
}
@@ -120,4 +122,3 @@ func getVarSpec(source string, startOffset int) (spec string, flags ScannerFlag,
}
return
}
+3
View File
@@ -1,3 +1,6 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// scanner_test.go
package text
+21 -18
View File
@@ -1,3 +1,6 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// tty.go
package text
@@ -9,6 +12,24 @@ import (
"strings"
)
type caseMode uint8
const (
normal = caseMode(iota)
upper
lower
title
firstOnly
)
type alignMode uint8
const (
noAlign = alignMode(iota)
alignLeft
alignRight
)
type ctxFmt struct {
isTTY bool
ansiLen int
@@ -114,24 +135,6 @@ func (ctx *ctxFmt) putAnsiReset(sb *strings.Builder) {
}
}
type caseMode uint8
const (
normal = caseMode(iota)
upper
lower
title
firstOnly
)
type alignMode uint8
const (
noAlign = alignMode(iota)
alignLeft
alignRight
)
func (ctx *ctxFmt) putText(sb *strings.Builder, text string, start int, mode caseMode, align alignMode, fieldSize int, filler byte) (offset int) {
if t := text[start+1:]; len(t) > 0 {
if align == alignRight {
+27 -7
View File
@@ -1,27 +1,47 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expander-context_test.go
package text
import (
"fmt"
"os"
"testing"
)
func TestSprintf(t *testing.T) {
list := []string{
func testCases() []string {
return []string{
"#{<(.20)-Param}: #{>(06)-123}",
"ciao #{c(r)i<(12)-GIAN Carlo}. #{ub-Come} #{c(G)b-Stai}?",
"ciao #{c(r)i>(12)-GIAN Carlo}. #{ub-Come} #{c(G)b-Stai}?",
"ciao #{c(red);i}Mario#. #{u;b}Come# #{GREEN;b}Stai#{.}?",
"ciao #{c(red)i-Mario}. #{ub-Come} #{GREEN;b-Stai#}?",
"ciao #{c(red)i-Mario}. #{ub-Come} #{GREEN;b-Stai}?",
"ciao #{c(r)i-Mario}. #{ub-Come} #{Gr;b-Stai}?",
"ciao #{c(r)i-Mario}. #{ub-Come} #{c(G)b-Stai}?",
"ciao #{c(r)i'-gian carlo}. #{ub-Come} #{c(G)b-Stai}?",
"ciao #{c(r)i\"-gian carlo}. #{ub-Come} #{c(G)b-Stai}?",
"ciao #{c(r)i,-GIAN Carlo}. #{ub-Come} #{c(G)b-Stai}?",
"ciao #{c(r)i^-GIAN Carlo}. #{ub-Come} #{c(G)b-Stai}?",
}
//s := Sprintf("ciao #{fg(139);i}Mario#. #{u;b}Come# #{GREEN;b}%s#{.}?", "Stai")
}
func TestSprintf(t *testing.T) {
list := testCases()
fmt.Println("--- Sprintf() ---")
for i, s := range list {
x := Sprintf(s)
fmt.Printf("--- Test nr %d: %q\n", i+1, s)
fmt.Println(x)
}
fmt.Println("\n--- Sprintf() ---")
}
func TestPrintf(t *testing.T) {
list := testCases()
fmt.Println("\n--- Printf() ---")
for i, s := range list {
fmt.Printf("--- Test nr %d: %q\n", i+1, s)
Fprintf(os.Stdout, s)
Printf(s)
fmt.Println()
}
}
+3
View File
@@ -1,3 +1,6 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// utils.go
package text