Add dot feature

This commit is contained in:
Anton Medvedev 2018-08-30 22:09:12 +07:00
parent 17b30f2622
commit 9bd442da48
3 changed files with 46 additions and 16 deletions

View File

@ -40,7 +40,7 @@ $ echo '{"key":"value"}' | fx
Use an anonymous function as reducer which gets JSON and processes it:
```
$ echo '{"foo": [{"bar": "value"}]}' | fx 'x => x.foo[0].bar'
"value"
value
```
### This Binding
@ -49,7 +49,16 @@ If you don't pass anonymous function `param => ...`, code will be automatically
And you can get access to JSON by `this` keyword:
```
$ echo '{"foo": [{"bar": "value"}]}' | fx 'this.foo[0].bar'
"value"
value
```
### Dot
It is possible to omit `this` keyword:
```
$ echo '{"foo": [{"bar": "value"}]}' | fx .foo[0].bar
value
```
### Chain
@ -57,7 +66,7 @@ $ echo '{"foo": [{"bar": "value"}]}' | fx 'this.foo[0].bar'
You can pass any number of anonymous functions for reducing JSON:
```
$ echo '{"foo": [{"bar": "value"}]}' | fx 'x => x.foo' 'this[0]' 'this.bar'
"value"
value
```
### Generator

View File

@ -10,16 +10,22 @@ const cli = meow(`
Examples
$ echo '{"key": "value"}' | fx 'x => x.key'
"value"
value
$ echo '[1,2,3]' | fx 'this.map(x => x * 2)'
[2, 4, 6]
$ echo '{"items": ["one", "two"]}' | fx 'this.items' 'this[1]'
"two"
two
$ echo '{"count": 0}' | fx '{...this, count: 1}'
{"count": 1}
$ echo '{"foo": 1, "bar": 2}' | fx ?
["foo", "bar"]
$ echo '{"key": "value"}' | fx .key
value
`)
async function main() {
@ -34,6 +40,8 @@ async function main() {
if (typeof result === 'undefined') {
process.stderr.write('undefined\n')
} else if (typeof result === 'string') {
console.log(result)
} else if (process.stdout.isTTY) {
console.log(pretty(result))
} else {
@ -45,7 +53,9 @@ function reduce(json, code) {
if (/^\w+\s*=>/.test(code)) {
const fx = eval(code)
return fx(json)
} else if (/yield/.test(code)) {
}
if (/yield/.test(code)) {
const fx = eval(`
function fn() {
const gen = (function*(){
@ -55,12 +65,19 @@ function reduce(json, code) {
}; fn
`)
return fx.call(json)
} else if (/^\?$/.test(code)) {
}
if (/^\?$/.test(code)) {
return Object.keys(json)
} else {
const fx = eval(`function fn() { return ${code} }; fn`)
}
if (/^\./.test(code)) {
const fx = eval(`function fn() { return ${code === '.' ? 'this' : 'this' + code} }; fn`)
return fx.call(json)
}
const fx = eval(`function fn() { return ${code} }; fn`)
return fx.call(json)
}
main()

18
test.js
View File

@ -3,26 +3,30 @@ const test = require('ava')
const {execSync} = require('child_process')
function fx(json, code = '') {
const output = execSync(`echo '${JSON.stringify(json)}' | node index.js ${code}`).toString('utf8')
return JSON.parse(output)
return execSync(`echo '${JSON.stringify(json)}' | node index.js ${code}`).toString('utf8')
}
test('pass', t => {
t.deepEqual(fx([{"greeting": "hello world"}]), [{"greeting": "hello world"}])
const r = fx([{"greeting": "hello world"}])
t.deepEqual(JSON.parse(r), [{"greeting": "hello world"}])
})
test('anon func', t => {
t.deepEqual(fx({"key": "value"}, "'x => x.key'"), 'value')
const r = fx({"key": "value"}, "'x => x.key'")
t.deepEqual(r, 'value\n')
})
test('this bind', t => {
t.deepEqual(fx([1, 2, 3, 4, 5], "'this.map(x => x * this.length)'"), [5, 10, 15, 20, 25])
const r = fx([1, 2, 3, 4, 5], "'this.map(x => x * this.length)'")
t.deepEqual(JSON.parse(r), [5, 10, 15, 20, 25])
})
test('generator', t => {
t.deepEqual(fx([1, 2, 3, 4, 5], "'for (let i of this) if (i % 2 == 0) yield i'"), [2, 4])
const r = fx([1, 2, 3, 4, 5], "'for (let i of this) if (i % 2 == 0) yield i'")
t.deepEqual(JSON.parse(r), [2, 4])
})
test('chain', t => {
t.deepEqual(fx({"items": ["foo", "bar"]}, "'this.items' 'yield* this' 'x => x[1]'"), 'bar')
const r = fx({"items": ["foo", "bar"]}, "'this.items' 'yield* this' 'x => x[1]'")
t.deepEqual(r, 'bar\n')
})