Skip to content

Commit

Permalink
feat: add ternary operator
Browse files Browse the repository at this point in the history
fixes #79
  • Loading branch information
kantord committed Nov 2, 2018
1 parent abb21e8 commit 1b071e0
Show file tree
Hide file tree
Showing 11 changed files with 353 additions and 6 deletions.
19 changes: 16 additions & 3 deletions docs/basic_filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ The following Python-style operators are also available as an alternative:

`or`, `and`

### Ternary operator (conditional)

The ternary (conditional) operator is the only emuto operator that takes three
operands. The ternary operator is the equivalent of the if statement in other
languages.

```
"Foo" if 3 < 4 else "Bar" // "Foo"
```

```
3.14 if true == false else [42] // [42]
```

## Spread operator

The spread operator `...` allows an iterable (like an array or string) to be
Expand All @@ -48,7 +62,7 @@ Retrieves a certain property of the input.

Property access can be made optional using a `?`:

```$?.foo?.bar```
`$?.foo?.bar`

The effect of this is that when the left hand side of `?.` is `undefined` or
`null`, no error will be thrown. Instead, `null` will be returned.
Expand All @@ -59,7 +73,7 @@ This code does not produce an error:
{"foo": 4}?.bar?.foo
```

## Projection `$[3]`, `$[0, -1]`, `$["foo", "bar"]`
## Projection `$[3]`, `$[0, -1]`, `$["foo", "bar"]`

Retrieve a single element from an object or array, or retrieve a list of
elements.
Expand All @@ -75,4 +89,3 @@ too. In this example, no error is produced. Instead, a `null` is returned.
```
null ?[3, 4]
```

234 changes: 234 additions & 0 deletions src/__tests__/__snapshots__/interpreter.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ exports[`interpreter correct target code 3 * 2 -1 >= -1.34 * 3 1`] = `"(function

exports[`interpreter correct target code 3 + $foobar where $foobar = 4 1`] = `"(function(_) { return (function(input) { return ((function() {_ = _.assign('foobar', (4), _); return (3+_.get('foobar'))})())})})"`;

exports[`interpreter correct target code 3 + 4 if 2 <= 3 else -12 1`] = `"(function(_) { return (function(input) { return ((2<=3) ? (3+4) : (-12))})})"`;

exports[`interpreter correct target code 3 : 4 if null else [-12] | [$[0]] 1`] = `"(function(_) { return (function(input) { return (function (input) {return [_.projection(input, [0], false)]})(((null) ? ([3,4]) : ([-12])))})})"`;

exports[`interpreter correct target code 8 * 3.14 / 2 1`] = `"(function(_) { return (function(input) { return 8*3.14/2})})"`;

exports[`interpreter correct target code 8 + 3.14 * 2 1`] = `"(function(_) { return (function(input) { return 8+3.14*2})})"`;
Expand Down Expand Up @@ -4847,6 +4851,236 @@ Object {
}
`;

exports[`interpreter correct target tree 3 + 4 if 2 <= 3 else -12 1`] = `
Object {
"status": true,
"value": Object {
"end": Object {
"column": 25,
"line": 1,
"offset": 24,
},
"name": "ternary",
"start": Object {
"column": 1,
"line": 1,
"offset": 0,
},
"value": Object {
"left": Object {
"name": "binaryOperation",
"value": Array [
Object {
"name": "primitive",
"value": "3",
},
Object {
"end": Object {
"column": 4,
"line": 1,
"offset": 3,
},
"name": "primitive",
"start": Object {
"column": 3,
"line": 1,
"offset": 2,
},
"value": "+",
},
Object {
"name": "primitive",
"value": "4",
},
],
},
"middle": Object {
"name": "binaryOperation",
"value": Array [
Object {
"name": "primitive",
"value": "2",
},
Object {
"end": Object {
"column": 14,
"line": 1,
"offset": 13,
},
"name": "primitive",
"start": Object {
"column": 12,
"line": 1,
"offset": 11,
},
"value": "<=",
},
Object {
"name": "primitive",
"value": "3",
},
],
},
"right": Object {
"name": "primitive",
"value": "-12",
},
},
},
}
`;

exports[`interpreter correct target tree 3 : 4 if null else [-12] | [$[0]] 1`] = `
Object {
"status": true,
"value": Object {
"name": "pipe",
"value": Object {
"left": Object {
"end": Object {
"column": 25,
"line": 1,
"offset": 24,
},
"name": "ternary",
"start": Object {
"column": 1,
"line": 1,
"offset": 0,
},
"value": Object {
"left": Object {
"name": "tuple",
"value": Array [
Object {
"name": "primitive",
"value": "3",
},
Object {
"name": "primitive",
"value": "4",
},
],
},
"middle": Object {
"name": "primitive",
"value": "null",
},
"right": Object {
"end": Object {
"column": 25,
"line": 1,
"offset": 24,
},
"name": "list",
"start": Object {
"column": 20,
"line": 1,
"offset": 19,
},
"value": Array [
Object {
"end": Object {
"column": 24,
"line": 1,
"offset": 23,
},
"name": "simpleList",
"start": Object {
"column": 21,
"line": 1,
"offset": 20,
},
"value": Array [
Object {
"name": "primitive",
"value": "-12",
},
],
},
],
},
},
},
"right": Object {
"end": Object {
"column": 34,
"line": 1,
"offset": 33,
},
"name": "list",
"start": Object {
"column": 28,
"line": 1,
"offset": 27,
},
"value": Array [
Object {
"end": Object {
"column": 33,
"line": 1,
"offset": 32,
},
"name": "simpleList",
"start": Object {
"column": 29,
"line": 1,
"offset": 28,
},
"value": Array [
Object {
"name": "projection",
"value": Object {
"left": Object {
"name": "variable",
"value": "$",
},
"optional": false,
"right": Object {
"end": Object {
"column": 33,
"line": 1,
"offset": 32,
},
"name": "list",
"start": Object {
"column": 30,
"line": 1,
"offset": 29,
},
"value": Array [
Object {
"end": Object {
"column": 32,
"line": 1,
"offset": 31,
},
"name": "simpleList",
"start": Object {
"column": 31,
"line": 1,
"offset": 30,
},
"value": Array [
Object {
"name": "primitive",
"value": "0",
},
],
},
],
},
},
},
],
},
],
},
},
},
}
`;

exports[`interpreter correct target tree 8 * 3.14 / 2 1`] = `
Object {
"status": true,
Expand Down
8 changes: 8 additions & 0 deletions src/__tests__/interpreter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,14 @@ const tests = [
sourceCode: `$?[3]`,
input: [1, 2, 3],
output: null
},
{
sourceCode: `3 + 4 if 2 <= 3 else -12`,
output: 7
},
{
sourceCode: `3 : 4 if null else [-12] | [$[0]]`,
output: [-12]
}
]

Expand Down
16 changes: 16 additions & 0 deletions src/generators/__tests__/ternary.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ternary from '../ternary'

describe('ternary generator', () => {
it('generates correct code', () => {
expect(
ternary({
name: 'ternary',
value: {
left: { name: 'primitive', value: '0' },
middle: { name: 'primitive', value: '1' },
right: { name: 'primitive', value: '2' }
}
})
).toEqual('((1) ? (0) : (2))')
})
})
3 changes: 3 additions & 0 deletions src/generators/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import pipe from './pipe'
import functionCall from './functionCall'
import binaryOperator from './binaryOperator'
import unaryOperator from './unaryOperator'
import ternary from './ternary'
import assignment from './assignment'
import lambda from './lambda'
import variable from './variable'
Expand Down Expand Up @@ -49,6 +50,8 @@ const Generator = (node: NodeType): GeneratedCodeType => {
return unaryOperator(node)
case 'assignment':
return assignment(Generator)(node)
case 'ternary':
return ternary(node)
default:
throw new Error(`Unknown node name '${node.name}'`)
}
Expand Down
10 changes: 10 additions & 0 deletions src/generators/ternary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @flow

import type { TernaryNodeType, GeneratedCodeType } from '../types'

export default ({ value }: TernaryNodeType): GeneratedCodeType => {
const Generator = require('./generator').default
return `((${Generator(value.middle)}) ? (${Generator(
value.left
)}) : (${Generator(value.right)}))`
}
21 changes: 21 additions & 0 deletions src/parsers/__tests__/ternary.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import parser from '../ternary'

describe('ternary parser', () => {
it('parses "Hello" + " World" if $hello >= 4 else null : false', () => {
expect(
parser.parse('"Hello" + " World" if $hello >= 4 else null : false')
.status
).toBe(true)
})

it('returns correct value', () => {
expect(parser.parse('0 if 1 else 2').value).toMatchObject({
name: 'ternary',
value: {
left: { name: 'primitive', value: '0' },
middle: { name: 'primitive', value: '1' },
right: { name: 'primitive', value: '2' }
}
})
})
})
3 changes: 2 additions & 1 deletion src/parsers/pipe/pipe.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import type { PipeNodeType, NodeType } from '../../types'

const PipeParser = P.lazy((): mixed => {
const TupleParser = require('../tuple/tuple').default
const TernaryParser = require('../ternary').default
const ProgramParser = require('../program').default
return P.seq(
TupleParser,
P.alt(TernaryParser, TupleParser),
P.regexp(/\s*\|\s*/),
ProgramParser
).map((value: [NodeType, mixed, NodeType]): PipeNodeType => ({
Expand Down
Loading

0 comments on commit 1b071e0

Please sign in to comment.