-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement funcfile, `len` and `@len`
- Loading branch information
Showing
27 changed files
with
518 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Expression Functions File | ||
|
||
A *functions file* allows you to specify additional expression | ||
helpers that can be loaded and used in *rare*. | ||
|
||
## Example | ||
|
||
For example, if you frequently need to double a number you | ||
could create a function: | ||
|
||
```funcfile title="custom.funcs" | ||
double {multi {0} 2} | ||
``` | ||
|
||
And use it in rare with argument `--funcs`: | ||
```sh | ||
rare --funcs custom.funcs filter -m '(\d+)' -e '{double {1}}' access.log | ||
``` | ||
|
||
Or via environment variable `RARE_FUNC_FILES`: | ||
```sh | ||
export RARE_FUNC_FILES=/path/to/custom.funcs | ||
rare filter -m '(\d+)' -e '{double {1}}' access.log | ||
``` | ||
|
||
You can load multiple files by providing `--funcs` argument multiple | ||
times, or providing a comma-separated list to `$RARE_FUNC_FILES` | ||
|
||
## Format | ||
|
||
A functions file is key-value pairs of `name` to `expression`. Lines | ||
starting with `#`, or any characters after `#`, are considered comments. | ||
|
||
*Expressions* can be multi-line by ending the previous line with a `\`. | ||
|
||
```funcsfile | ||
# Allows comments that start with '#' | ||
name-of-func {sumi {0} {1}} # comments can also go here | ||
# Multi-line ends with '\' | ||
classifylen {switch \ | ||
# short string | ||
{lt {len {0}} 5} short \ | ||
# long string | ||
{gt {len {0}} 15} long \ | ||
medium \ # else, medium | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# test func | ||
double {sumi {0} {0}} | ||
|
||
# Blank space above | ||
multipline {switch \ | ||
{eq {0} a} isa \ | ||
{eq {0} b} isb \ | ||
} | ||
|
||
multipline2 {switch \ | ||
{eq {0} a} isa \ #inline | ||
|
||
#Blank above | ||
{eq {0} b} isb \ | ||
# else | ||
b \ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package funcfile | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"os" | ||
"rare/pkg/expressions" | ||
"rare/pkg/logger" | ||
"strings" | ||
) | ||
|
||
func LoadDefinitionsFile(compiler *expressions.KeyBuilder, filename string) (map[string]expressions.KeyBuilderFunction, error) { | ||
f, err := os.Open(filename) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer f.Close() | ||
|
||
return LoadDefinitions(compiler, f) | ||
} | ||
|
||
func LoadDefinitions(compiler *expressions.KeyBuilder, r io.Reader) (map[string]expressions.KeyBuilderFunction, error) { | ||
scanner := bufio.NewScanner(r) | ||
ret := make(map[string]expressions.KeyBuilderFunction) | ||
|
||
errors := 0 | ||
linenum := 0 | ||
for { | ||
// read possible multiline | ||
var sb strings.Builder | ||
for scanner.Scan() { | ||
linenum++ | ||
|
||
// Get line and split out comments | ||
line := strings.TrimSpace(trimAfter(scanner.Text(), '#')) | ||
if line == "" { | ||
continue | ||
} | ||
|
||
if strings.HasSuffix(line, "\\") { // multiline | ||
sb.WriteString(strings.TrimSuffix(line, "\\")) | ||
} else { | ||
sb.WriteString(line) | ||
break | ||
} | ||
} | ||
if sb.Len() == 0 { | ||
break | ||
} | ||
phrase := sb.String() | ||
|
||
// Split arguments | ||
args := strings.SplitN(phrase, " ", 2) | ||
if len(args) != 2 { | ||
continue | ||
} | ||
|
||
// Compile and save | ||
fnc, err := createAndAddFunc(compiler, args[0], args[1]) | ||
if err != nil { | ||
logger.Printf("Error creating function '%s', line %d: %s", args[0], linenum, err) | ||
errors++ | ||
} else { | ||
ret[args[0]] = fnc | ||
} | ||
} | ||
|
||
if errors > 0 { | ||
return ret, fmt.Errorf("%d compile errors while loading func spec", errors) | ||
} | ||
return ret, nil | ||
} | ||
|
||
func trimAfter(s string, r rune) string { | ||
idx := strings.IndexRune(s, r) | ||
if idx < 0 { | ||
return s | ||
} | ||
return s[:idx] | ||
} | ||
|
||
func createAndAddFunc(compiler *expressions.KeyBuilder, name, expr string) (expressions.KeyBuilderFunction, error) { | ||
kb, err := compiler.Compile(expr) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
fnc := keyBuilderToFunction(kb) | ||
compiler.Func(name, fnc) | ||
return fnc, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package funcfile | ||
|
||
import ( | ||
"rare/pkg/expressions/stdlib" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestLoadDefinitions(t *testing.T) { | ||
data := `# a comment | ||
double {sumi {0} {0}} | ||
# Another comment | ||
quad this equals: \ | ||
{multi \ | ||
{0} \ | ||
{0} \ | ||
#Mixed comment | ||
{0} {0}} | ||
` | ||
kb := stdlib.NewStdKeyBuilder() | ||
funcs, err := LoadDefinitions(kb, strings.NewReader(data)) | ||
assert.NoError(t, err) | ||
assert.Len(t, funcs, 2) | ||
assert.Contains(t, funcs, "quad") | ||
assert.Contains(t, funcs, "double") | ||
|
||
val, err := kb.Compile("{quad 5}") | ||
assert.Nil(t, err) | ||
assert.Equal(t, "this equals: 625", val.BuildKey(nil)) | ||
} | ||
|
||
func TestLoadDefinitionsErrs(t *testing.T) { | ||
data := `# a comment | ||
unterm unterm { | ||
nofunc | ||
` | ||
kb := stdlib.NewStdKeyBuilder() | ||
funcs, err := LoadDefinitions(kb, strings.NewReader(data)) | ||
assert.NotNil(t, err) | ||
assert.Len(t, funcs, 0) | ||
} | ||
|
||
func TestLoadFile(t *testing.T) { | ||
kb := stdlib.NewStdKeyBuilder() | ||
funcs, err := LoadDefinitionsFile(kb, "example.funcfile") | ||
assert.NoError(t, err) | ||
assert.Len(t, funcs, 3) | ||
|
||
_, err = LoadDefinitionsFile(kb, "notfile.funcfile") | ||
assert.Error(t, err) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package funcfile | ||
|
||
import ( | ||
"rare/pkg/expressions" | ||
"rare/pkg/slicepool" | ||
) | ||
|
||
type lazySubContext struct { | ||
args []expressions.KeyBuilderStage | ||
sub expressions.KeyBuilderContext | ||
} | ||
|
||
func (s *lazySubContext) GetMatch(idx int) string { | ||
if idx < 0 || idx >= len(s.args) { | ||
return "" | ||
} | ||
return s.args[idx](s.sub) | ||
} | ||
|
||
func (s *lazySubContext) GetKey(name string) string { | ||
return s.sub.GetKey(name) | ||
} | ||
|
||
func keyBuilderToFunction(stage *expressions.CompiledKeyBuilder) expressions.KeyBuilderFunction { | ||
return func(args []expressions.KeyBuilderStage) (expressions.KeyBuilderStage, error) { | ||
ctxPool := slicepool.NewObjectPoolEx(5, func() *lazySubContext { | ||
return &lazySubContext{ | ||
args: args, | ||
} | ||
}) | ||
|
||
return func(kbc expressions.KeyBuilderContext) string { | ||
subCtx := ctxPool.Get() | ||
defer ctxPool.Return(subCtx) | ||
subCtx.sub = kbc | ||
|
||
return stage.BuildKey(subCtx) | ||
}, nil | ||
} | ||
} |
Oops, something went wrong.