Skip to content

Commit

Permalink
feat: add map function
Browse files Browse the repository at this point in the history
  • Loading branch information
kantord committed Oct 5, 2018
1 parent 7077234 commit 7d4a1b5
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 3 deletions.
11 changes: 10 additions & 1 deletion src/__tests__/builtins.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import builtIns from '../builtins'

const { projection, join } = builtIns
const { projection, join, map } = builtIns

describe('built ins', () => {
describe('projection', () => {
Expand Down Expand Up @@ -36,4 +36,13 @@ describe('built ins', () => {
expect(join(',')(['foo', 'World', ' '])).toEqual('foo,World, ')
})
})

describe('map', () => {
it('returns correct value', () => {
const id = a => a; // eslint-disable-line
const foo = a => 'foo'; // eslint-disable-line
expect(map(id)(['Hello', 'World'])).toEqual(['Hello', 'World'])
expect(map(foo)(['Hello', 'World'])).toEqual(['foo', 'foo'])
})
})
})
5 changes: 5 additions & 0 deletions src/__tests__/interpreter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ const tests = [
sourceCode: `join " "`,
input: ['Hello', 'World'],
output: 'Hello World'
},
{
sourceCode: `map \\" "`,
input: ['Hello', 'World'],
output: [' ', ' ']
}
]

Expand Down
5 changes: 4 additions & 1 deletion src/builtins.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,8 @@ export default {
: handleProjection(left)(right),
join: (separator: string): ((Array<string>) => string) => (
input: Array<string>
): string => input.join(separator)
): string => input.join(separator),
map: (f: mixed => mixed): ((Array<mixed>) => Array<mixed>) => (
input: Array<mixed>
): Array<mixed> => input.map(f)
}
26 changes: 26 additions & 0 deletions src/generators/__tests__/functionCallLambda.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// @flow

import functionCallLambda from '../functionCallLambda'

describe('functionCallLambda generator', () => {
it('generates correct code', () => {
const fakeGenerator = (
{ type } // eslint-disable-line flowtype/require-parameter-type
): string => '[]'
expect(
functionCallLambda(fakeGenerator)({
type: 'functionCallLambda',
value: {
left: {
type: 'identifier',
value: 'foo'
},
right: {
type: 'list',
value: []
}
}
})
).toEqual('_.foo(function(input) {return []})(input)')
})
})
16 changes: 16 additions & 0 deletions src/generators/functionCallLambda.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @flow

import type {
FunctionCallLambdaNodeType,
GeneratedCodeType,
NodeType
} from '../types'

export default (
Generator: NodeType => GeneratedCodeType
): (FunctionCallLambdaNodeType => GeneratedCodeType) => ({
value
}: FunctionCallLambdaNodeType): GeneratedCodeType =>
`_.${value.left.value}(function(input) {return ${Generator(
value.right
)}})(input)`
3 changes: 3 additions & 0 deletions src/generators/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import object from './object'
import projection from './projection'
import pipe from './pipe'
import functionCall from './functionCall'
import functionCallLambda from './functionCallLambda'
import type { NodeType, GeneratedCodeType } from '../types'

const Generator = (node: NodeType): GeneratedCodeType => {
Expand All @@ -37,6 +38,8 @@ const Generator = (node: NodeType): GeneratedCodeType => {
return projection(Generator)(node)
case 'functionCall':
return functionCall(Generator)(node)
case 'functionCallLambda':
return functionCallLambda(Generator)(node)
default:
throw new Error(`Unknown node type '${node.type}'`)
}
Expand Down
32 changes: 32 additions & 0 deletions src/parsers/__tests__/functionCallLambda.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import parser from '../functionCallLambda'

describe('functionCallLambda parser', () => {
it('parses map \\"foo"', () => {
expect(parser.parse('map \\"foo"').status).toBe(true)
})

it('returns correct value', () => {
expect(parser.parse('map \\["a", "b"]').value).toEqual({
type: 'functionCallLambda',
value: {
left: {
type: 'identifier',
value: 'map'
},
right: {
type: 'list',
value: [
{
type: 'primitive',
value: '"a"'
},
{
type: 'primitive',
value: '"b"'
}
]
}
}
})
})
})
37 changes: 37 additions & 0 deletions src/parsers/functionCallLambda.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// @flow

import Parsimmon from 'parsimmon'

import type {
FunctionCallLambdaNodeType,
NodeType,
IdentifierNodeType
} from '../types'

const FunctionCallLambdaParser = Parsimmon.lazy((): mixed => {
const ProgramParser = require('./program').default
const IdentifierParser = require('./identifier').default
return Parsimmon.seq(
IdentifierParser,
Parsimmon.optWhitespace,
Parsimmon.string('\\'),
Parsimmon.optWhitespace,
ProgramParser
).map(
([left, _, __, ___, right]: [
IdentifierNodeType,
mixed,
mixed,
mixed,
NodeType
]): FunctionCallLambdaNodeType => ({
type: 'functionCallLambda',
value: {
left,
right
}
})
)
})

export default FunctionCallLambdaParser
8 changes: 7 additions & 1 deletion src/parsers/program.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ import TupleParser from './tuple/tuple'
import PipeParser from './pipe/pipe'
import type { NodeType } from '../types'
import FunctionCallParser from './functionCall'
import FunctionCallLambdaParser from './functionCallLambda'

export default Parsimmon.seq(
Parsimmon.optWhitespace,
Parsimmon.alt(FunctionCallParser, PipeParser, TupleParser),
Parsimmon.alt(
FunctionCallLambdaParser,
FunctionCallParser,
PipeParser,
TupleParser
),
Parsimmon.optWhitespace
).map((value: [mixed, NodeType, mixed]): NodeType => value[1])
10 changes: 10 additions & 0 deletions src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ export type FunctionCallNodeType = {|
}
|};

export type FunctionCallLambdaNodeType = {|
type: 'functionCallLambda',
value: {
left: IdentifierNodeType,
right: NodeType // eslint-disable-line no-use-before-define
}
|};

export type ProjectionNodeType = {|
type: 'projection',
value: {
Expand All @@ -98,6 +106,8 @@ export type NodeType =
| ParenthesesNodeType
| ObjectNodeType
| ValuePropNodeType
| FunctionCallNodeType
| FunctionCallLambdaNodeType
| ProjectionNodeType;

export type OutputType = mixed => mixed;
Expand Down

0 comments on commit 7d4a1b5

Please sign in to comment.