mirror of
https://github.com/antonmedv/fx
synced 2024-11-01 21:40:20 +00:00
Add support for python reducers
This commit is contained in:
parent
73bf2d97cd
commit
1b09c93447
93
pkg/reducer/nodejs.go
Normal file
93
pkg/reducer/nodejs.go
Normal file
@ -0,0 +1,93 @@
|
||||
package reducer
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CreateNodejs(args []string) *exec.Cmd {
|
||||
cmd := exec.Command("node", "-e", GenerateCode(args))
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "NODE_OPTIONS=--max-old-space-size=8192")
|
||||
return cmd
|
||||
}
|
||||
|
||||
//go:embed reduce.js
|
||||
var templateJs string
|
||||
|
||||
func GenerateCodeNodejs(args []string) string {
|
||||
rs := "\n"
|
||||
for i, a := range args {
|
||||
rs += " try {"
|
||||
switch {
|
||||
case a == ".":
|
||||
rs += `
|
||||
x = function ()
|
||||
{ return this }
|
||||
.call(x)
|
||||
`
|
||||
|
||||
case flatMapRegex.MatchString(a):
|
||||
code := fold(strings.Split(a, "[]"))
|
||||
rs += fmt.Sprintf(
|
||||
`
|
||||
x = (
|
||||
%v
|
||||
)(x)
|
||||
`, code)
|
||||
|
||||
case strings.HasPrefix(a, ".["):
|
||||
rs += fmt.Sprintf(
|
||||
`
|
||||
x = function ()
|
||||
{ return this%v }
|
||||
.call(x)
|
||||
`, a[1:])
|
||||
|
||||
case strings.HasPrefix(a, "."):
|
||||
rs += fmt.Sprintf(
|
||||
`
|
||||
x = function ()
|
||||
{ return this%v }
|
||||
.call(x)
|
||||
`, a)
|
||||
|
||||
default:
|
||||
rs += fmt.Sprintf(
|
||||
`
|
||||
f = function ()
|
||||
{ return %v }
|
||||
.call(x)
|
||||
x = typeof f === 'function' ? f(x) : fn
|
||||
`, a)
|
||||
}
|
||||
// Generate a beautiful error message.
|
||||
rs += " } catch (e) {\n"
|
||||
pre, post, pointer := trace(args, i)
|
||||
rs += fmt.Sprintf(
|
||||
" throw `\\n ${%q} ${%q} ${%q}\\n %v\\n\\n${e.stack || e}`\n",
|
||||
pre, a, post, pointer,
|
||||
)
|
||||
rs += " }\n"
|
||||
}
|
||||
return fmt.Sprintf(templateJs, rs)
|
||||
}
|
||||
|
||||
var flatMapRegex = regexp.MustCompile("^(\\.\\w*)+\\[]")
|
||||
|
||||
func fold(s []string) string {
|
||||
if len(s) == 1 {
|
||||
return "x => x" + s[0]
|
||||
}
|
||||
obj := s[0]
|
||||
if obj == "." {
|
||||
obj = "x"
|
||||
} else {
|
||||
obj = "x" + obj
|
||||
}
|
||||
return fmt.Sprintf("x => Object.values(%v).flatMap(%v)", obj, fold(s[1:]))
|
||||
}
|
46
pkg/reducer/python.go
Normal file
46
pkg/reducer/python.go
Normal file
@ -0,0 +1,46 @@
|
||||
package reducer
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func CreatePython(bin string, args []string) *exec.Cmd {
|
||||
cmd := exec.Command(bin, "-c", GenerateCode(args))
|
||||
return cmd
|
||||
}
|
||||
|
||||
//go:embed reduce.py
|
||||
var templatePython string
|
||||
|
||||
func GenerateCodePython(args []string) string {
|
||||
rs := "\n"
|
||||
for i, a := range args {
|
||||
rs += "try:"
|
||||
switch {
|
||||
case a == ".":
|
||||
rs += `
|
||||
x = x
|
||||
`
|
||||
|
||||
default:
|
||||
rs += fmt.Sprintf(
|
||||
`
|
||||
f = (lambda json: (%v))(x)
|
||||
x = f(x) if callable(f) else f
|
||||
`, a)
|
||||
}
|
||||
// Generate a beautiful error message.
|
||||
rs += "except Exception as e:\n"
|
||||
pre, post, pointer := trace(args, i)
|
||||
rs += fmt.Sprintf(
|
||||
` sys.stderr.write('\n {} {} {}\n %v\n\n{}\n'.format(%q, %q, %q, e))
|
||||
sys.exit(1)`,
|
||||
pointer,
|
||||
pre, a, post,
|
||||
)
|
||||
rs += "\n"
|
||||
}
|
||||
return fmt.Sprintf(templatePython, rs)
|
||||
}
|
@ -5,112 +5,45 @@ import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
. "github.com/antonmedv/fx/pkg/json"
|
||||
. "github.com/antonmedv/fx/pkg/theme"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
. "github.com/antonmedv/fx/pkg/json"
|
||||
. "github.com/antonmedv/fx/pkg/theme"
|
||||
)
|
||||
|
||||
//go:embed reduce.js
|
||||
var template string
|
||||
|
||||
var flatMapRegex = regexp.MustCompile("^(\\.\\w*)+\\[]")
|
||||
|
||||
func GenerateCode(args []string) string {
|
||||
rs := "\n"
|
||||
for i, a := range args {
|
||||
rs += " try {"
|
||||
switch {
|
||||
case a == ".":
|
||||
rs += `
|
||||
json = function ()
|
||||
{ return this }
|
||||
.call(json)
|
||||
`
|
||||
|
||||
case flatMapRegex.MatchString(a):
|
||||
code := fold(strings.Split(a, "[]"))
|
||||
rs += fmt.Sprintf(
|
||||
`
|
||||
json = (
|
||||
%v
|
||||
)(json)
|
||||
`, code)
|
||||
|
||||
case strings.HasPrefix(a, ".["):
|
||||
rs += fmt.Sprintf(
|
||||
`
|
||||
json = function ()
|
||||
{ return this%v }
|
||||
.call(json)
|
||||
`, a[1:])
|
||||
|
||||
case strings.HasPrefix(a, "."):
|
||||
rs += fmt.Sprintf(
|
||||
`
|
||||
json = function ()
|
||||
{ return this%v }
|
||||
.call(json)
|
||||
`, a)
|
||||
|
||||
default:
|
||||
rs += fmt.Sprintf(
|
||||
`
|
||||
fn = function ()
|
||||
{ return %v }
|
||||
.call(json)
|
||||
json = typeof fn === 'function' ? json = fn(json) : fn
|
||||
`, a)
|
||||
}
|
||||
// Generate a beautiful error message.
|
||||
rs += " } catch (e) {\n"
|
||||
pre := strings.Join(args[:i], " ")
|
||||
if len(pre) > 20 {
|
||||
pre = "..." + pre[len(pre)-20:]
|
||||
}
|
||||
post := strings.Join(args[i+1:], " ")
|
||||
if len(post) > 20 {
|
||||
post = post[:20] + "..."
|
||||
}
|
||||
pointer := fmt.Sprintf(
|
||||
"%v %v %v",
|
||||
strings.Repeat(" ", len(pre)),
|
||||
strings.Repeat("^", len(a)),
|
||||
strings.Repeat(" ", len(post)),
|
||||
)
|
||||
rs += fmt.Sprintf(
|
||||
" throw `\\n"+
|
||||
" ${%q} ${%q} ${%q}\\n"+
|
||||
" %v\\n"+
|
||||
"\\n${e.stack || e}`\n",
|
||||
pre, a, post,
|
||||
pointer,
|
||||
)
|
||||
rs += " }\n"
|
||||
lang, ok := os.LookupEnv("FX_LANG")
|
||||
if !ok {
|
||||
lang = "node"
|
||||
}
|
||||
return fmt.Sprintf(template, rs)
|
||||
}
|
||||
|
||||
func fold(s []string) string {
|
||||
if len(s) == 1 {
|
||||
return "x => x" + s[0]
|
||||
switch {
|
||||
case lang == "node":
|
||||
return GenerateCodeNodejs(args)
|
||||
case strings.HasPrefix(lang, "python"):
|
||||
return GenerateCodePython(args)
|
||||
default:
|
||||
panic("unknown lang")
|
||||
}
|
||||
obj := s[0]
|
||||
if obj == "." {
|
||||
obj = "x"
|
||||
} else {
|
||||
obj = "x" + obj
|
||||
}
|
||||
return fmt.Sprintf("x => Object.values(%v).flatMap(%v)", obj, fold(s[1:]))
|
||||
}
|
||||
|
||||
func Reduce(object interface{}, args []string, theme Theme) {
|
||||
var cmd *exec.Cmd
|
||||
lang, ok := os.LookupEnv("FX_LANG")
|
||||
if !ok {
|
||||
lang = "node"
|
||||
}
|
||||
switch {
|
||||
case lang == "node":
|
||||
cmd = CreateNodejs(args)
|
||||
case strings.HasPrefix(lang, "python"):
|
||||
cmd = CreatePython(lang, args)
|
||||
default:
|
||||
panic("unknown lang")
|
||||
}
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd := exec.Command("node", "-e", GenerateCode(args))
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "NODE_OPTIONS=--max-old-space-size=8192")
|
||||
cmd.Stdin = strings.NewReader(Stringify(object))
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
@ -138,3 +71,21 @@ func Reduce(object interface{}, args []string, theme Theme) {
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func trace(args []string, i int) (pre, post, pointer string) {
|
||||
pre = strings.Join(args[:i], " ")
|
||||
if len(pre) > 20 {
|
||||
pre = "..." + pre[len(pre)-20:]
|
||||
}
|
||||
post = strings.Join(args[i+1:], " ")
|
||||
if len(post) > 20 {
|
||||
post = post[:20] + "..."
|
||||
}
|
||||
pointer = fmt.Sprintf(
|
||||
"%v %v %v",
|
||||
strings.Repeat(" ", len(pre)),
|
||||
strings.Repeat("^", len(args[i])),
|
||||
strings.Repeat(" ", len(post)),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -14,14 +14,14 @@ void async function () {
|
||||
for await (let chunk of process.stdin) {
|
||||
buffer += chunk
|
||||
}
|
||||
let json = JSON.parse(buffer)
|
||||
let x = JSON.parse(buffer)
|
||||
|
||||
// Reducers %v
|
||||
|
||||
if (typeof json === 'undefined') {
|
||||
if (typeof x === 'undefined') {
|
||||
process.stderr.write('undefined')
|
||||
} else {
|
||||
process.stdout.write(JSON.stringify(json))
|
||||
process.stdout.write(JSON.stringify(x))
|
||||
}
|
||||
}().catch(err => {
|
||||
console.error(err)
|
||||
|
9
pkg/reducer/reduce.py
Normal file
9
pkg/reducer/reduce.py
Normal file
@ -0,0 +1,9 @@
|
||||
import json, sys, os
|
||||
x = json.load(sys.stdin)
|
||||
|
||||
# Reducers %v
|
||||
|
||||
try:
|
||||
print(json.dumps(x))
|
||||
except:
|
||||
print(json.dumps(list(x)))
|
Loading…
Reference in New Issue
Block a user