A small script triggering a configuration question.
2019-09-09 GoGraz Lukas Prokop
rename 's/[.]jpeg[0-9]*/.jpeg/' *.jpeg[0-9]*
for n in *.jpeg[0-9]* do
mv -i "$n" "${n%%.jpeg[0-9]*}.jpeg";
done
autoload zmv
zmv -n '(*.jpeg)<->' '$1'
% go run main.go
usage: rename [--dry-run] [-l <lua-filepath>:<luafunction>
<filepath-to-rename>+]+
this program takes filepaths and renames them
acc. to the Lua function's return value
exit status 1
% cat lib.lua
function titlecase(filename)
return filename:sub(1,1):upper() .. filename:sub(2)
end
% go run main.go --dry-run -l lib.lua:titlecase file.txt
Calling 'titlecase' with 'file.txt
'titlecase' returned 'File.txt' for 'file.txt'
Would rename 'file.txt' to 'File.txt' now, but I am dry-running
% go run main.go -l lib2.lua file.txt
lib2.lua seems to define the following functions: 'stripWhitespace' 'uppercase' 'lowercase' 'titlecase'
lua reference 'lib2.lua' must contain colon to separate lua path and function name
Lua syntax is very easy. I parsed it for fun. Function definition maches one of regexes:
function ([^(]+)\(
([^ =]+) *= *function
lib.lua:titlecase
with github.com/Shopify/go-lua
luaState := lua.NewState()
lua.OpenLibraries(luaState)
_ := lua.DoFile(luaState, luaScriptFilepath)
luaState.Global(luaFunctionName)
// prechecks
if !luaState.IsFunction(luaState.Top()) {
log.Fatal("loading function failed")
}
if luaState.PushString(baseName) != baseName ||
!luaState.IsString(luaState.Top()) {
log.Fatal("loading string failed")
}
with github.com/Shopify/go-lua
// call 1 argument, 1 result
luaState.Call(1, 1)
// fetch result
result, ok := luaState.ToString(luaState.Top())
if !ok {
log.Fatal("Reading lua return value somehow failed")
}
if result == "" {
log.Fatal(`Function %s returned empty string -
cannot rename to empty string`, luaFunctionName)
}
log.Printf("'%s' returned '%s' for '%s'",
luaFunctionName, result, baseName)
luaState.Pop(1)
go-lua is a port of the Lua 5.2 VM to pure Go. It is compatible with binary files dumped by luac, from the Lua reference implementation. The motivation is to enable simple scripting of Go applications. For example, it is used to describe flows in Shopify's load generation tool, Genghis.
→ Refer to the well-specified Lua C API …
> = string.gsub("Hello banana", "banana", "Lua user")
Hello Lua user 1
> -- capture any occurences of "an" and replace
> = string.gsub("banana", "(an)", "%1-")
ban-an-a 2
> -- brackets around n's which follow a's
> = string.gsub("banana", "a(n)", "a(%1)")
ba(n)a(n)a 2
> -- reverse any "an"s
> = string.gsub("banana", "(a)(n)", "%2%1")
bnanaa 2
> = string.gsub("banana", "(a)(n)",
function(a,b) return b..a end)
bnanaa 2
…
// {"gmatch", ...},
// {"gsub", ...},
{"len", func(l *State) int { l.PushInteger(len(CheckString(l, 1))); return 1 }},
{"lower", func(l *State) int { l.PushString(strings.ToLower(CheckString(l, 1))); return 1 }},
// {"match", ...},
…
We want the user to specify (parts of) the behavior of a program. How?
I identified 3 approaches to the problem:
UNIX specifies a lot of tiny syntaxes. /etc/fstab
, /etc/hosts
, … but also in tools like sed
, grep
, awk
, …
import lua "github.com/Shopify/go-lua"
and call lua.Whatever()
How and should we make programs more configurable? What about the next generation capable of programming as a common skill?
Thanks!