get rid of lisp interpreter, -R and -F are enough, fixes #30

This commit is contained in:
2025-01-14 12:28:54 +01:00
committed by T.v.Dein
parent 0e68dc585d
commit 14c50b4e63
11 changed files with 43 additions and 674 deletions

View File

@@ -8,6 +8,49 @@ Tablizer can be used to re-format tabular output of other
programs. While you could do this using standard unix tools, in some programs. While you could do this using standard unix tools, in some
cases it's a hard job. cases it's a hard job.
Usage:
```default
Usage:
tablizer [regex] [file, ...] [flags]
Operational Flags:
-c, --columns string Only show the speficied columns (separated by ,)
-v, --invert-match select non-matching rows
-n, --no-numbering Disable header numbering
-N, --no-color Disable pattern highlighting
-H, --no-headers Disable headers display
-s, --separator string Custom field separator
-k, --sort-by int Sort by column (default: 1)
-z, --fuzzy Use fuzzy search [experimental]
-F, --filter field=reg Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T
Output Flags (mutually exclusive):
-X, --extended Enable extended output
-M, --markdown Enable markdown table output
-O, --orgtbl Enable org-mode table output
-S, --shell Enable shell evaluable output
-Y, --yaml Enable yaml output
-C, --csv Enable CSV output
-A, --ascii Default output mode, ascii tabular
-L, --hightlight-lines Use alternating background colors for tables
Sort Mode Flags (mutually exclusive):
-a, --sort-age sort according to age (duration) string
-D, --sort-desc Sort in descending order (default: ascending)
-i, --sort-numeric sort according to string numerical value
-t, --sort-time sort according to time string
Other Flags:
--completion <shell> Generate the autocompletion script for <shell>
-f, --config <file> Configuration file (default: ~/.config/tablizer/config)
-d, --debug Enable debugging
-h, --help help for tablizer
-m, --man Display manual page
-V, --version Print program version
```
Let's take this output: Let's take this output:
``` ```
% kubectl get pods -o wide % kubectl get pods -o wide
@@ -99,9 +142,6 @@ a dash. If you need to work with `/` characters, you can also use any
other separator, for instance: `-R '| |-|'`. other separator, for instance: `-R '| |-|'`.
Last but not least tablizer has support for plugins written in
lisp. This feature is expermental yet. Take a look into the manpage
for details.
## Demo ## Demo

10
TODO.md
View File

@@ -6,13 +6,3 @@
- add --no-headers option - add --no-headers option
### Lisp Plugin Infrastructure using zygo
Hooks:
| Filter | Purpose | Args | Return |
|-----------|-------------------------------------------------------------|---------------------|--------|
| filter | include or exclude lines | row as hash | bool |
| process | do calculations with data, store results in global lisp env | whole dataset | nil |
| transpose | modify a cell | headername and cell | cell |
| append | add one or more rows to the dataset (use this to add stats) | nil | rows |

View File

@@ -23,7 +23,6 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/glycerine/zygomys/zygo"
"github.com/gookit/color" "github.com/gookit/color"
"github.com/hashicorp/hcl/v2/hclsimple" "github.com/hashicorp/hcl/v2/hclsimple"
) )
@@ -32,7 +31,6 @@ const DefaultSeparator string = `(\s\s+|\t)`
const Version string = "v1.3.0" const Version string = "v1.3.0"
const MAXPARTS = 2 const MAXPARTS = 2
var DefaultLoadPath = os.Getenv("HOME") + "/.config/tablizer/lisp"
var DefaultConfigfile = os.Getenv("HOME") + "/.config/tablizer/config" var DefaultConfigfile = os.Getenv("HOME") + "/.config/tablizer/config"
var VERSION string // maintained by -x var VERSION string // maintained by -x
@@ -89,13 +87,6 @@ type Config struct {
NoColor bool NoColor bool
// special case: we use the config struct to transport the lisp
// env trough the program
Lisp *zygo.Zlisp
// a path containing lisp scripts to be loaded on startup
LispLoadPath string
// config file, optional // config file, optional
Configfile string Configfile string
@@ -138,9 +129,6 @@ type Sortmode struct {
Age bool Age bool
} }
// valid lisp hooks
var ValidHooks []string
// default color schemes // default color schemes
func (conf *Config) Colors() map[color.Level]map[string]color.Color { func (conf *Config) Colors() map[color.Level]map[string]color.Color {
colors := map[color.Level]map[string]color.Color{ colors := map[color.Level]map[string]color.Color{
@@ -342,8 +330,6 @@ func (conf *Config) ApplyDefaults() {
if conf.OutputMode == Yaml || conf.OutputMode == CSV { if conf.OutputMode == Yaml || conf.OutputMode == CSV {
conf.NoNumbering = true conf.NoNumbering = true
} }
ValidHooks = []string{"filter", "process", "transpose", "append"}
} }
func (conf *Config) PreparePattern(pattern string) error { func (conf *Config) PreparePattern(pattern string) error {

View File

@@ -117,9 +117,6 @@ func Execute() {
conf.DetermineColormode() conf.DetermineColormode()
conf.ApplyDefaults() conf.ApplyDefaults()
// setup lisp env, load plugins etc
wrapE(lib.SetupLisp(&conf))
// actual execution starts here // actual execution starts here
wrapE(lib.ProcessFiles(&conf, args)) wrapE(lib.ProcessFiles(&conf, args))
}, },
@@ -187,10 +184,6 @@ func Execute() {
rootCmd.MarkFlagsMutuallyExclusive("extended", "markdown", "orgtbl", rootCmd.MarkFlagsMutuallyExclusive("extended", "markdown", "orgtbl",
"shell", "yaml", "csv") "shell", "yaml", "csv")
// lisp options
rootCmd.PersistentFlags().StringVarP(&conf.LispLoadPath, "load-path", "l", cfg.DefaultLoadPath,
"Load path for lisp plugins (expects *.zy files)")
// config file // config file
rootCmd.PersistentFlags().StringVarP(&conf.Configfile, "config", "f", cfg.DefaultConfigfile, rootCmd.PersistentFlags().StringVarP(&conf.Configfile, "config", "f", cfg.DefaultConfigfile,
"config file (default: ~/.config/tablizer/config)") "config file (default: ~/.config/tablizer/config)")

View File

@@ -40,7 +40,6 @@ SYNOPSIS
Other Flags: Other Flags:
--completion <shell> Generate the autocompletion script for <shell> --completion <shell> Generate the autocompletion script for <shell>
-f, --config <file> Configuration file (default: ~/.config/tablizer/config) -f, --config <file> Configuration file (default: ~/.config/tablizer/config)
-l, --load-path <path> Load path for lisp plugins (expects *.zy files)
-d, --debug Enable debugging -d, --debug Enable debugging
-h, --help help for tablizer -h, --help help for tablizer
-m, --man Display manual page -m, --man Display manual page
@@ -344,60 +343,6 @@ CONFIGURATION AND COLORS
Colorization can be turned off completely either by setting the Colorization can be turned off completely either by setting the
parameter "-N" or the environment variable NO_COLOR to a true value. parameter "-N" or the environment variable NO_COLOR to a true value.
LISP PLUGINS [experimental]
Tablizer supports plugins written in zygomys lisp. You can supply a
directory to the "-l" parameter containing *.zy files or a single .zy
file containing lisp code.
You can put as much code as you want into the file, but you need to add
one lips function to a hook at the end.
The following hooks are available:
filter
The filter hook works one a whole line of the input. Your hook
function is expected to return true or false. If you return true,
the line will be included in the output, otherwise not.
Multiple filter hook functions are supported.
Example:
/*
Simple filter hook function. Splits the argument by whitespace,
fetches the 2nd element, converts it to an int and returns true
if it s larger than 5, false otherwise.
*/
(defn uselarge [line]
(cond (> (atoi (second (resplit line ` +`))) 5) true false))
/* Register the filter hook */
(addhook %filter %uselarge)
process
The process hook function gets a table containing the parsed input
data (see "lib/common.go:type Tabdata struct". It is expected to
return a pair containing a bool to denote if the table has been
modified, and the [modified] table. The resulting table may have
less rows than the original and cells may have changed content but
the number of columns must persist.
transpose
not yet implemented.
append
not yet implemented.
Beside the existing language features, the following additional lisp
functions are provided by tablizer:
(resplit [string, regex]) => list
(atoi [string]) => int
(matchre [string, regex]) => bool
The standard language is described here:
<https://github.com/glycerine/zygomys/wiki/Language>.
BUGS BUGS
In order to report a bug, unexpected behavior, feature requests or to In order to report a bug, unexpected behavior, feature requests or to
submit a patch, please open an issue on github: submit a patch, please open an issue on github:
@@ -472,7 +417,6 @@ Sort Mode Flags (mutually exclusive):
Other Flags: Other Flags:
--completion <shell> Generate the autocompletion script for <shell> --completion <shell> Generate the autocompletion script for <shell>
-f, --config <file> Configuration file (default: ~/.config/tablizer/config) -f, --config <file> Configuration file (default: ~/.config/tablizer/config)
-l, --load-path <path> Load path for lisp plugins (expects *.zy files)
-d, --debug Enable debugging -d, --debug Enable debugging
-h, --help help for tablizer -h, --help help for tablizer
-m, --man Display manual page -m, --man Display manual page

View File

@@ -19,7 +19,6 @@ package lib
import ( import (
"bufio" "bufio"
"fmt"
"io" "io"
"strings" "strings"
@@ -144,18 +143,6 @@ func FilterByPattern(conf cfg.Config, input io.Reader) (io.Reader, error) {
// so we ignore all lines, which DO match. // so we ignore all lines, which DO match.
continue continue
} }
// apply user defined lisp filters, if any
accept, err := RunFilterHooks(conf, line)
if err != nil {
return input, fmt.Errorf("failed to apply filter hook: %w", err)
}
if !accept {
// IF there are filter hook[s] and IF one of them
// returns false on the current line, reject it
continue
}
} }
lines = append(lines, line) lines = append(lines, line)

View File

@@ -1,319 +0,0 @@
/*
Copyright © 2023 Thomas von Dein
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 <http://www.gnu.org/licenses/>.
*/
package lib
import (
"errors"
"fmt"
"log"
"os"
"strings"
"github.com/glycerine/zygomys/zygo"
"github.com/tlinden/tablizer/cfg"
)
/*
needs to be global because we can't feed an cfg object to AddHook()
which is being called from user lisp code
*/
var Hooks map[string][]*zygo.SexpSymbol
/*
AddHook() (called addhook from lisp code) can be used by the user to
add a function to one of the available hooks provided by tablizer.
*/
func AddHook(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) {
var hookname string
if len(args) < 2 {
return zygo.SexpNull, errors.New("argument of %add-hook should be: %hook-name %your-function")
}
switch sexptype := args[0].(type) {
case *zygo.SexpSymbol:
if !HookExists(sexptype.Name()) {
return zygo.SexpNull, errors.New("Unknown hook " + sexptype.Name())
}
hookname = sexptype.Name()
default:
return zygo.SexpNull, errors.New("hook name must be a symbol ")
}
switch sexptype := args[1].(type) {
case *zygo.SexpSymbol:
_, exists := Hooks[hookname]
if !exists {
Hooks[hookname] = []*zygo.SexpSymbol{sexptype}
} else {
Hooks[hookname] = append(Hooks[hookname], sexptype)
}
default:
return zygo.SexpNull, errors.New("hook function must be a symbol ")
}
return zygo.SexpNull, nil
}
/*
Check if a hook exists
*/
func HookExists(key string) bool {
for _, hook := range cfg.ValidHooks {
if hook == key {
return true
}
}
return false
}
/*
* Basic sanity checks and load lisp file
*/
func LoadAndEvalFile(env *zygo.Zlisp, path string) error {
if strings.HasSuffix(path, `.zy`) {
code, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read lisp file %s: %w", path, err)
}
// FIXME: check what res (_ here) could be and mean
_, err = env.EvalString(string(code))
if err != nil {
log.Fatal(env.GetStackTrace(err))
}
}
return nil
}
/*
* Setup lisp interpreter environment
*/
func SetupLisp(conf *cfg.Config) error {
// iterate over load-path and evaluate all *.zy files there, if any
// we ignore if load-path does not exist, which is the default anyway
path, err := os.Stat(conf.LispLoadPath)
if err != nil {
if os.IsNotExist(err) {
// ignore non-existent files
return nil
}
return fmt.Errorf("failed to stat path: %w", err)
}
// init global hooks
Hooks = make(map[string][]*zygo.SexpSymbol)
// init sandbox
env := zygo.NewZlispSandbox()
env.AddFunction("addhook", AddHook)
if !path.IsDir() {
// load single lisp file
err = LoadAndEvalFile(env, conf.LispLoadPath)
if err != nil {
return err
}
} else {
// load all lisp file in load dir
dir, err := os.ReadDir(conf.LispLoadPath)
if err != nil {
return fmt.Errorf("failed to read lisp dir %s: %w",
conf.LispLoadPath, err)
}
for _, entry := range dir {
if !entry.IsDir() {
err := LoadAndEvalFile(env, conf.LispLoadPath+"/"+entry.Name())
if err != nil {
return err
}
}
}
}
RegisterLib(env)
conf.Lisp = env
return nil
}
/*
Execute every user lisp function registered as filter hook.
Each function is given the current line as argument and is expected to
return a boolean. True indicates to keep the line, false to skip
it.
If there are multiple such functions registered, then the first one
returning false wins, that is if each function returns true the line
will be kept, if at least one of them returns false, it will be
skipped.
*/
func RunFilterHooks(conf cfg.Config, line string) (bool, error) {
for _, hook := range Hooks["filter"] {
var result bool
conf.Lisp.Clear()
res, err := conf.Lisp.EvalString(fmt.Sprintf("(%s `%s`)", hook.Name(), line))
if err != nil {
return false, fmt.Errorf("failed to evaluate hook loader: %w", err)
}
switch sexptype := res.(type) {
case *zygo.SexpBool:
result = sexptype.Val
default:
return false, fmt.Errorf("filter hook shall return bool")
}
if !result {
// the first hook which returns false leads to complete false
return result, nil
}
}
// if no hook returned false, we succeed and accept the given line
return true, nil
}
/*
These hooks get the data (Tabdata) readily processed by tablizer as
argument. They are expected to return a SexpPair containing a boolean
denoting if the data has been modified and the actual modified
data. Columns must be the same, rows may differ. Cells may also have
been modified.
Replaces the internal data structure Tabdata with the user supplied
version.
Only one process hook function is supported.
The somewhat complicated code is being caused by the fact, that we
need to convert our internal structure to a lisp variable and vice
versa afterwards.
*/
func RunProcessHooks(conf cfg.Config, data *Tabdata) (*Tabdata, bool, error) {
var userdata Tabdata
lisplist := []zygo.Sexp{}
if len(Hooks["process"]) == 0 {
return data, false, nil
}
if len(Hooks["process"]) > 1 {
fmt.Println("Warning: only one process hook is allowed!")
}
// there are hook[s] installed, convert the go data structure 'data to lisp
for _, row := range data.entries {
var entry zygo.SexpHash
for idx, cell := range row {
err := entry.HashSet(&zygo.SexpStr{S: data.headers[idx]}, &zygo.SexpStr{S: cell})
if err != nil {
return data, false, fmt.Errorf("failed to convert to lisp data: %w", err)
}
}
lisplist = append(lisplist, &entry)
}
// we need to add it to the env so that the function can use the struct directly
conf.Lisp.AddGlobal("data", &zygo.SexpArray{Val: lisplist, Env: conf.Lisp})
// execute the actual hook
hook := Hooks["process"][0]
conf.Lisp.Clear()
var result bool
res, err := conf.Lisp.EvalString(fmt.Sprintf("(%s data)", hook.Name()))
if err != nil {
return data, false, fmt.Errorf("failed to eval lisp loader: %w", err)
}
// we expect (bool, array(hash)) as return from the function
switch sexptype := res.(type) {
case *zygo.SexpPair:
switch th := sexptype.Head.(type) {
case *zygo.SexpBool:
result = th.Val
default:
return data, false, errors.New("expect (bool, array(hash)) as return value")
}
switch sexptailtype := sexptype.Tail.(type) {
case *zygo.SexpArray:
lisplist = sexptailtype.Val
default:
return data, false, errors.New("expect (bool, array(hash)) as return value ")
}
default:
return data, false, errors.New("process hook shall return array of hashes ")
}
if !result {
// no further processing required
return data, result, nil
}
// finally convert lispdata back to Tabdata
for _, item := range lisplist {
row := []string{}
switch hash := item.(type) {
case *zygo.SexpHash:
for _, header := range data.headers {
entry, err := hash.HashGetDefault(
conf.Lisp,
&zygo.SexpStr{S: header},
&zygo.SexpStr{S: ""})
if err != nil {
return data, false, fmt.Errorf("failed to get lisp hash entry: %w", err)
}
switch sexptype := entry.(type) {
case *zygo.SexpStr:
row = append(row, sexptype.S)
default:
return data, false, errors.New("hash values should be string ")
}
}
default:
return data, false, errors.New("returned array should contain hashes ")
}
userdata.entries = append(userdata.entries, row)
}
userdata.headers = data.headers
return &userdata, result, nil
}

View File

@@ -1,110 +0,0 @@
/*
Copyright © 2023 Thomas von Dein
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 <http://www.gnu.org/licenses/>.
*/
package lib
import (
"errors"
"fmt"
"regexp"
"strconv"
"github.com/glycerine/zygomys/zygo"
)
func Splice2SexpList(list []string) zygo.Sexp {
slist := []zygo.Sexp{}
for _, item := range list {
slist = append(slist, &zygo.SexpStr{S: item})
}
return zygo.MakeList(slist)
}
func StringReSplit(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) {
if len(args) < 2 {
return zygo.SexpNull, errors.New("expecting 2 arguments: <string>, <regex>")
}
var separator, input string
switch t := args[0].(type) {
case *zygo.SexpStr:
input = t.S
default:
return zygo.SexpNull, errors.New("first argument must be a string")
}
switch t := args[1].(type) {
case *zygo.SexpStr:
separator = t.S
default:
return zygo.SexpNull, errors.New("second argument must be a string")
}
sep := regexp.MustCompile(separator)
return Splice2SexpList(sep.Split(input, -1)), nil
}
func String2Int(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) {
var number int
switch t := args[0].(type) {
case *zygo.SexpStr:
num, err := strconv.Atoi(t.S)
if err != nil {
return zygo.SexpNull, fmt.Errorf("failed to convert string to number: %w", err)
}
number = num
default:
return zygo.SexpNull, errors.New("argument must be a string")
}
return &zygo.SexpInt{Val: int64(number)}, nil
}
func RegMatch(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) {
if len(args) != 2 {
return zygo.SexpNull, fmt.Errorf("argument must be <regexp>, <string>")
}
arguments := []string{}
for _, arg := range args {
switch t := arg.(type) {
case *zygo.SexpStr:
arguments = append(arguments, t.S)
default:
return zygo.SexpNull, errors.New("argument must be a string")
}
}
reg := regexp.MustCompile(arguments[0])
return &zygo.SexpBool{Val: reg.MatchString(arguments[1])}, nil
}
func RegisterLib(env *zygo.Zlisp) {
env.AddFunction("resplit", StringReSplit)
env.AddFunction("atoi", String2Int)
env.AddFunction("matchre", RegMatch)
}

View File

@@ -145,18 +145,6 @@ func parseTabular(conf cfg.Config, input io.Reader) (Tabdata, error) {
continue continue
} }
// apply user defined lisp filters, if any
accept, err := RunFilterHooks(conf, line)
if err != nil {
return data, fmt.Errorf("failed to apply filter hook: %w", err)
}
if !accept {
// IF there are filter hook[s] and IF one of them
// returns false on the current line, reject it
continue
}
idx := 0 // we cannot use the header index, because we could exclude columns idx := 0 // we cannot use the header index, because we could exclude columns
values := []string{} values := []string{}
for _, part := range parts { for _, part := range parts {
@@ -214,17 +202,6 @@ func PostProcess(conf cfg.Config, data *Tabdata) (*Tabdata, bool, error) {
modified = true modified = true
} }
// apply user defined lisp process hooks, if any
userdata, changed, err := RunProcessHooks(conf, data)
if err != nil {
return data, false, fmt.Errorf("failed to apply filter hook: %w", err)
}
if changed {
data = userdata
modified = true
}
if conf.Debug { if conf.Debug {
repr.Print(data) repr.Print(data)
} }

View File

@@ -178,7 +178,6 @@ tablizer \- Manipulate tabular output of other programs
\& Other Flags: \& Other Flags:
\& \-\-completion <shell> Generate the autocompletion script for <shell> \& \-\-completion <shell> Generate the autocompletion script for <shell>
\& \-f, \-\-config <file> Configuration file (default: ~/.config/tablizer/config) \& \-f, \-\-config <file> Configuration file (default: ~/.config/tablizer/config)
\& \-l, \-\-load\-path <path> Load path for lisp plugins (expects *.zy files)
\& \-d, \-\-debug Enable debugging \& \-d, \-\-debug Enable debugging
\& \-h, \-\-help help for tablizer \& \-h, \-\-help help for tablizer
\& \-m, \-\-man Display manual page \& \-m, \-\-man Display manual page
@@ -541,63 +540,6 @@ the \f(CW\*(C`\-L\*(C'\fR parameter).
.PP .PP
Colorization can be turned off completely either by setting the Colorization can be turned off completely either by setting the
parameter \f(CW\*(C`\-N\*(C'\fR or the environment variable \fB\s-1NO_COLOR\s0\fR to a true value. parameter \f(CW\*(C`\-N\*(C'\fR or the environment variable \fB\s-1NO_COLOR\s0\fR to a true value.
.SH "LISP PLUGINS [experimental]"
.IX Header "LISP PLUGINS [experimental]"
Tablizer supports plugins written in zygomys lisp. You can supply a
directory to the \f(CW\*(C`\-l\*(C'\fR parameter containing \fB*.zy\fR files or a single
\&.zy file containing lisp code.
.PP
You can put as much code as you want into the file, but you need to
add one lips function to a hook at the end.
.PP
The following hooks are available:
.IP "\fBfilter\fR" 4
.IX Item "filter"
The filter hook works one a whole line of the input. Your hook
function is expected to return true or false. If you return true, the
line will be included in the output, otherwise not.
.Sp
Multiple filter hook functions are supported.
.Sp
Example:
.Sp
.Vb 7
\& /*
\& Simple filter hook function. Splits the argument by whitespace,
\& fetches the 2nd element, converts it to an int and returns true
\& if it s larger than 5, false otherwise.
\& */
\& (defn uselarge [line]
\& (cond (> (atoi (second (resplit line \` +\`))) 5) true false))
\&
\& /* Register the filter hook */
\& (addhook %filter %uselarge)
.Ve
.IP "\fBprocess\fR" 4
.IX Item "process"
The process hook function gets a table containing the parsed input
data (see \f(CW\*(C`lib/common.go:type Tabdata struct\*(C'\fR. It is expected to
return a pair containing a bool to denote if the table has been
modified, and the [modified] table. The resulting table may have less
rows than the original and cells may have changed content but the
number of columns must persist.
.IP "\fBtranspose\fR" 4
.IX Item "transpose"
not yet implemented.
.IP "\fBappend\fR" 4
.IX Item "append"
not yet implemented.
.PP
Beside the existing language features, the following additional lisp
functions are provided by tablizer:
.PP
.Vb 3
\& (resplit [string, regex]) => list
\& (atoi [string]) => int
\& (matchre [string, regex]) => bool
.Ve
.PP
The standard language is described here: <https://github.com/glycerine/zygomys/wiki/Language>.
.SH "BUGS" .SH "BUGS"
.IX Header "BUGS" .IX Header "BUGS"
In order to report a bug, unexpected behavior, feature requests In order to report a bug, unexpected behavior, feature requests

View File

@@ -39,7 +39,6 @@ tablizer - Manipulate tabular output of other programs
Other Flags: Other Flags:
--completion <shell> Generate the autocompletion script for <shell> --completion <shell> Generate the autocompletion script for <shell>
-f, --config <file> Configuration file (default: ~/.config/tablizer/config) -f, --config <file> Configuration file (default: ~/.config/tablizer/config)
-l, --load-path <path> Load path for lisp plugins (expects *.zy files)
-d, --debug Enable debugging -d, --debug Enable debugging
-h, --help help for tablizer -h, --help help for tablizer
-m, --man Display manual page -m, --man Display manual page
@@ -384,67 +383,7 @@ the C<-L> parameter).
Colorization can be turned off completely either by setting the Colorization can be turned off completely either by setting the
parameter C<-N> or the environment variable B<NO_COLOR> to a true value. parameter C<-N> or the environment variable B<NO_COLOR> to a true value.
=head1 LISP PLUGINS [experimental]
Tablizer supports plugins written in zygomys lisp. You can supply a
directory to the C<-l> parameter containing B<*.zy> files or a single
.zy file containing lisp code.
You can put as much code as you want into the file, but you need to
add one lips function to a hook at the end.
The following hooks are available:
=over
=item B<filter>
The filter hook works one a whole line of the input. Your hook
function is expected to return true or false. If you return true, the
line will be included in the output, otherwise not.
Multiple filter hook functions are supported.
Example:
/*
Simple filter hook function. Splits the argument by whitespace,
fetches the 2nd element, converts it to an int and returns true
if it s larger than 5, false otherwise.
*/
(defn uselarge [line]
(cond (> (atoi (second (resplit line ` +`))) 5) true false))
/* Register the filter hook */
(addhook %filter %uselarge)
=item B<process>
The process hook function gets a table containing the parsed input
data (see C<lib/common.go:type Tabdata struct>. It is expected to
return a pair containing a bool to denote if the table has been
modified, and the [modified] table. The resulting table may have less
rows than the original and cells may have changed content but the
number of columns must persist.
=item B<transpose>
not yet implemented.
=item B<append>
not yet implemented.
=back
Beside the existing language features, the following additional lisp
functions are provided by tablizer:
(resplit [string, regex]) => list
(atoi [string]) => int
(matchre [string, regex]) => bool
The standard language is described here: L<https://github.com/glycerine/zygomys/wiki/Language>.
=head1 BUGS =head1 BUGS