Skip to content

Commit 04f79a7

Browse files
authored
Add fallback engine (#300)
1 parent b7c4bab commit 04f79a7

25 files changed

+882
-627
lines changed

.github/workflows/snap.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
- name: Set up Go
1414
uses: actions/setup-go@v3
1515
with:
16-
go-version: 1.18
16+
go-version: 1.21
1717

1818
- uses: snapcore/action-build@v1
1919
id: build

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- name: Set up Go
1616
uses: actions/setup-go@v3
1717
with:
18-
go-version: 1.18
18+
go-version: 1.21
1919

2020
- name: Test
2121
run: go test ./...

go.mod

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/antonmedv/fx
22

3-
go 1.20
3+
go 1.21
44

55
require (
66
github.com/antonmedv/clipboard v1.0.1
@@ -23,10 +23,10 @@ require (
2323
github.com/aymanbagabas/go-udiff v0.1.3 // indirect
2424
github.com/containerd/console v1.0.4 // indirect
2525
github.com/davecgh/go-spew v1.1.1 // indirect
26-
github.com/dlclark/regexp2 v1.7.0 // indirect
26+
github.com/dlclark/regexp2 v1.10.0 // indirect
2727
github.com/fatih/color v1.16.0 // indirect
2828
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
29-
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
29+
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect
3030
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
3131
github.com/mattn/go-colorable v0.1.13 // indirect
3232
github.com/mattn/go-localereader v0.0.1 // indirect

go.sum

+11-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
2323
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2424
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2525
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
26-
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
2726
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
27+
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
28+
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
2829
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
2930
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 h1:O7I1iuzEA7SG+dK8ocOBSlYAA9jBUmCYl/Qa7ey7JAM=
3031
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
@@ -33,15 +34,20 @@ github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8
3334
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
3435
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
3536
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
37+
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
3638
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
39+
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
3740
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
41+
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
3842
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
3943
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
4044
github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
4145
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
4246
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
43-
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
47+
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
4448
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
49+
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ=
50+
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
4551
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
4652
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
4753
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -52,7 +58,9 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
5258
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
5359
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
5460
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
61+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
5562
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
63+
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
5664
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
5765
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
5866
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@@ -89,6 +97,7 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
8997
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
9098
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
9199
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
100+
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
92101
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
93102
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
94103
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=

internal/complete/complete.go

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package complete
22

33
import (
4+
_ "embed"
45
"fmt"
56
"os"
67
"path/filepath"
@@ -10,6 +11,7 @@ import (
1011
"github.com/dop251/goja"
1112
"github.com/goccy/go-yaml"
1213

14+
"github.com/antonmedv/fx/internal/engine"
1315
"github.com/antonmedv/fx/internal/shlex"
1416
)
1517

@@ -53,6 +55,9 @@ var globals = []string{
5355
"skip",
5456
}
5557

58+
//go:embed prelude.js
59+
var prelude string
60+
5661
func Complete() bool {
5762
compLine, ok := os.LookupEnv("COMP_LINE")
5863

@@ -145,7 +150,7 @@ func doComplete(compLine string, compWord string) {
145150
}
146151
}
147152

148-
codeComplete(string(input), args, compWord)
153+
codeComplete(input, args, compWord)
149154
}
150155
}
151156

@@ -163,7 +168,7 @@ func globalsComplete(compWord string) bool {
163168
return false
164169
}
165170

166-
func codeComplete(input string, args []string, compWord string) {
171+
func codeComplete(input []byte, args []string, compWord string) {
167172
args = args[2:] // Drop binary & file from the args.
168173

169174
if compWord == "" {
@@ -180,21 +185,24 @@ func codeComplete(input string, args []string, compWord string) {
180185

181186
var code strings.Builder
182187
code.WriteString(prelude)
183-
code.WriteString(fmt.Sprintf("let json = %s\n", input))
188+
code.WriteString(engine.Stdlib)
189+
code.WriteString("let json = ")
190+
code.Write(input)
184191
for _, arg := range args {
185-
if arg == "" {
192+
if arg == "" { // After dropTail, we can have empty strings.
186193
continue
187194
}
188-
code.WriteString(Transform(arg))
195+
code.WriteString(engine.Transform(arg))
189196
}
190197
code.WriteString("\n__keys\n")
191198

192-
out, err := goja.New().RunString(code.String())
199+
vm := goja.New()
200+
value, err := vm.RunString(code.String())
193201
if err != nil {
194202
return
195203
}
196204

197-
if array, ok := out.Export().([]interface{}); ok {
205+
if array, ok := value.Export().([]interface{}); ok {
198206
prefix := dropTail(compWord)
199207
var reply []string
200208
for _, key := range array {

internal/complete/prelude.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const __keys = new Set()
2+
3+
Object.prototype.__keys = function () {
4+
if (Array.isArray(this)) return
5+
if (typeof this === 'string') return
6+
if (this instanceof String) return
7+
if (typeof this === 'object' && this !== null)
8+
Object.keys(this).forEach(x => __keys.add(x))
9+
}
10+

internal/engine/engine.go

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package engine
2+
3+
import (
4+
_ "embed"
5+
"fmt"
6+
"io"
7+
"os"
8+
"strconv"
9+
"strings"
10+
11+
"github.com/dop251/goja"
12+
"github.com/goccy/go-yaml"
13+
14+
"github.com/antonmedv/fx/internal/jsonx"
15+
)
16+
17+
//go:embed stdlib.js
18+
var Stdlib string
19+
20+
//go:embed prelude.js
21+
var prelude string
22+
23+
func Reduce(args []string) {
24+
if len(args) < 1 {
25+
panic("args must have at least one element")
26+
}
27+
28+
var (
29+
flagYaml bool
30+
flagRaw bool
31+
flagSlurp bool
32+
)
33+
34+
var src io.Reader = os.Stdin
35+
if isFile(args[0]) {
36+
src = open(args[0], &flagYaml)
37+
args = args[1:]
38+
} else if isFile(args[len(args)-1]) {
39+
src = open(args[len(args)-1], &flagYaml)
40+
args = args[:len(args)-1]
41+
}
42+
43+
var fns []string
44+
for _, arg := range args {
45+
switch arg {
46+
case "--yaml":
47+
flagYaml = true
48+
case "--raw", "-r":
49+
flagRaw = true
50+
case "--slurp", "-s":
51+
flagSlurp = true
52+
case "-rs", "-sr":
53+
flagRaw = true
54+
flagSlurp = true
55+
default:
56+
fns = append(fns, arg)
57+
}
58+
}
59+
60+
if flagSlurp {
61+
println("Error: Built-in JS engine does not support \"--slurp\" flag. Install Node.js or Deno to use this flag.")
62+
os.Exit(1)
63+
}
64+
65+
data, err := io.ReadAll(src)
66+
if err != nil {
67+
panic(err)
68+
}
69+
70+
if flagRaw {
71+
data = []byte(strconv.Quote(string(data)))
72+
} else if flagYaml {
73+
data, err = yaml.YAMLToJSON(data)
74+
if err != nil {
75+
println(err.Error())
76+
os.Exit(1)
77+
}
78+
} else {
79+
node, err := jsonx.Parse(data)
80+
if err != nil {
81+
println(err.Error())
82+
os.Exit(1)
83+
}
84+
data = []byte(node.String())
85+
}
86+
87+
var code strings.Builder
88+
code.WriteString(prelude)
89+
code.WriteString(Stdlib)
90+
code.WriteString(fmt.Sprintf("let json = JSON.parse(%q)\n", data))
91+
for _, fn := range fns {
92+
code.WriteString(Transform(fn))
93+
}
94+
code.WriteString("JSON.stringify(json)")
95+
96+
vm := goja.New()
97+
vm.Set("println", func(s string) any {
98+
fmt.Println(s)
99+
return nil
100+
})
101+
102+
value, err := vm.RunString(code.String())
103+
if err != nil {
104+
println(err.Error())
105+
os.Exit(1)
106+
}
107+
108+
output, ok := value.Export().(string)
109+
if !ok {
110+
println("undefined")
111+
return
112+
}
113+
114+
node, err := jsonx.Parse([]byte(output))
115+
if err != nil {
116+
println(err.Error())
117+
os.Exit(1)
118+
}
119+
120+
if len(node.Value) > 0 && node.Value[0] == '"' {
121+
s, _ := strconv.Unquote(string(node.Value))
122+
fmt.Println(s)
123+
return
124+
}
125+
fmt.Print(node.PrettyPrint())
126+
}

internal/engine/prelude.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const console = {
2+
log: function (...args) {
3+
const parts = []
4+
for (const arg of args) {
5+
parts.push(typeof arg === 'string' ? arg : JSON.stringify(arg, null, 2))
6+
}
7+
println(parts.join(' '))
8+
},
9+
}

internal/complete/prelude.go renamed to internal/engine/stdlib.js

+9-23
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,3 @@
1-
package complete
2-
3-
const prelude = `
4-
const __keys = new Set()
5-
6-
Object.prototype.__keys = function () {
7-
if (Array.isArray(this)) return
8-
if (typeof this === 'string') return
9-
if (this instanceof String) return
10-
if (typeof this === 'object' && this !== null)
11-
Object.keys(this).forEach(x => __keys.add(x))
12-
}
13-
141
function apply(fn, ...args) {
152
if (typeof fn === 'function') return fn(...args)
163
return fn
@@ -20,23 +7,23 @@ function len(x) {
207
if (Array.isArray(x)) return x.length
218
if (typeof x === 'string') return x.length
229
if (typeof x === 'object' && x !== null) return Object.keys(x).length
23-
throw new Error()
10+
throw new Error(`Cannot get length of ${typeof x}`)
2411
}
2512

2613
function uniq(x) {
2714
if (Array.isArray(x)) return [...new Set(x)]
28-
throw new Error()
15+
throw new Error(`Cannot get unique values of ${typeof x}`)
2916
}
3017

3118
function sort(x) {
3219
if (Array.isArray(x)) return x.sort()
33-
throw new Error()
20+
throw new Error(`Cannot sort ${typeof x}`)
3421
}
3522

3623
function map(fn) {
3724
return function (x) {
3825
if (Array.isArray(x)) return x.map((v, i) => fn(v, i))
39-
throw new Error()
26+
throw new Error(`Cannot map ${typeof x}`)
4027
}
4128
}
4229

@@ -47,7 +34,7 @@ function sortBy(fn) {
4734
const fb = fn(b)
4835
return fa < fb ? -1 : fa > fb ? 1 : 0
4936
})
50-
throw new Error()
37+
throw new Error(`Cannot sort ${typeof x}`)
5138
}
5239
}
5340

@@ -85,21 +72,20 @@ function zip(...x) {
8572

8673
function flatten(x) {
8774
if (Array.isArray(x)) return x.flat()
88-
throw new Error()
75+
throw new Error(`Cannot flatten ${typeof x}`)
8976
}
9077

9178
function reverse(x) {
9279
if (Array.isArray(x)) return x.reverse()
93-
throw new Error()
80+
throw new Error(`Cannot reverse ${typeof x}`)
9481
}
9582

9683
function keys(x) {
9784
if (typeof x === 'object' && x !== null) return Object.keys(x)
98-
throw new Error()
85+
throw new Error(`Cannot get keys of ${typeof x}`)
9986
}
10087

10188
function values(x) {
10289
if (typeof x === 'object' && x !== null) return Object.values(x)
103-
throw new Error()
90+
throw new Error(`Cannot get values of ${typeof x}`)
10491
}
105-
`

internal/complete/transpile.go renamed to internal/engine/transform.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package complete
1+
package engine
22

33
import (
44
"fmt"

0 commit comments

Comments
 (0)