Skip to content

Commit

Permalink
added log as a primitive function
Browse files Browse the repository at this point in the history
  • Loading branch information
keyz committed Apr 27, 2016
1 parent 4429844 commit c6bfc9c
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 22 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"node": true,
"es6": true,
"jest": true,
"jasmine": true,
},
"ecmaFeatures": {
"generators": true,
Expand Down
2 changes: 1 addition & 1 deletion src/Closure.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import invariant from 'fbjs/lib/invariant';

import { batchExtendEnv, extendEnv } from './Environment';
const CLOSURE_TYPE_FLAG = Symbol('CLOSURE_TYPE_FLAG');
import { CLOSURE_TYPE_FLAG } from './typeFlags';

/*
* As we've said,
Expand Down
2 changes: 1 addition & 1 deletion src/Environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const emptyEnv = iMap({
const lookupEnv = (name, env) => {
if (!env.has(name)) {
throw new Error(
`* Uncaught ReferenceError: ${name} is not defined. env snapshot: ${env.toString()}`,
`Uncaught ReferenceError: ${name} is not defined. env snapshot: ${env.toString()}`,
);
}

Expand Down
25 changes: 23 additions & 2 deletions src/Interp/ExpressionInterp.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {
applyClosure,
} from '../Closure';

import { CLOSURE_TYPE_FLAG, NATIVE_FUNC_FLAG } from '../typeFlags';

import Prims from '../Prims';

import Options from '../Options';

import { interp as statementInterp } from './StatementInterp';
Expand All @@ -33,6 +37,12 @@ const interp = (exp, env) => {

case 'Identifier': {
const { name } = exp;

// @TODO document this
if (Object.keys(Prims).includes(name)) {
return Prims[name];
}

return lookupEnv(name, env);
}

Expand All @@ -51,10 +61,21 @@ const interp = (exp, env) => {
case 'CallExpression': {
const { callee, arguments: rawArgs } = exp;
// here we recur on both sides
const closure = interp(callee, env);
const vals = rawArgs.map((obj) => interp(obj, env));
const closureOrFunc = interp(callee, env);

return applyClosure(interp, closure, vals, env, Options.isLexical);
// @TODO document this
switch (closureOrFunc.type) {
case CLOSURE_TYPE_FLAG: {
return applyClosure(interp, closureOrFunc, vals, env, Options.isLexical);
}
case NATIVE_FUNC_FLAG: {
return closureOrFunc.func.apply(null, vals);
}
default: {
throw new Error(`unsupported ~closure type ${closureOrFunc.type}`);
}
}
}

case 'UnaryExpression': {
Expand Down
37 changes: 37 additions & 0 deletions src/Interp/__tests__/ExpressionInterp-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import { makeClosure } from '../../Closure';
import Options from '../../Options';

jest.unmock('../index')
.unmock('../../typeFlags')
.unmock('../../Parser')
.unmock('../ExpressionInterp')
.unmock('../StatementInterp')
.unmock('../../Environment')
.unmock('../../Closure')
.unmock('../../Prims')
.unmock('../../Options');

const parseAndgetExp = (code) => parse(code).body[0].expression;
Expand Down Expand Up @@ -209,6 +211,18 @@ describe('Interp', () => {
})());
});

it('should return undefined when there\'s no return', () => {
expect(interpExp(`(() => {
const fact = (x) => (x < 2 ? 1 : x * fact(x - 1));
const bar = 140;
bar;
})()`)).toBe((() => {
const fact = (x) => (x < 2 ? 1 : x * fact(x - 1));
const bar = 140;
bar; // eslint-disable-line no-unused-expressions
})());
});

it('should support dynamic scope', () => {
Options.isLexical = false;

Expand Down Expand Up @@ -236,4 +250,27 @@ describe('Interp', () => {

Options.isLexical = true;
});

it('should support `log` as a native func call', () => {
spyOn(console, 'log');

expect(interpExp(`(() => {
const fact = (x) => (x < 2 ? 1 : x * fact(x - 1));
log(fact(5));
log(256);
const foo = 12;
const bar = 140;
log(bar);
return foo + bar;
})()`)).toBe((() => {
const foo = 12;
const bar = 140;
return foo + bar;
})());

expect(console.log.calls.count()).toEqual(3); // eslint-disable-line no-console
expect(console.log.calls.argsFor(0)).toEqual([ 120 ]); // eslint-disable-line no-console
expect(console.log.calls.argsFor(1)).toEqual([ 256 ]); // eslint-disable-line no-console
expect(console.log.calls.argsFor(2)).toEqual([ 140 ]); // eslint-disable-line no-console
});
});
25 changes: 12 additions & 13 deletions src/Prims.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { makeClosure } from './Closure';
import { log } from './Writer';
import { emptyEnv } from './Environment';
import { NATIVE_FUNC_FLAG } from './typeFlags';

const LogClosure = makeClosure(
[ 'x' ], {
type: 'NativeFuncCall',
value: log,
},
emptyEnv,
const wrapNativeFunc = (params, func) => ({
type: NATIVE_FUNC_FLAG,
params,
func,
});

const logFunc = wrapNativeFunc(
[ 'x' ],
(x) => { console.log(x); }, // eslint-disable-line no-console
);

const Prims = {
log: LogClosure,
log: logFunc,
};

export {
Prims,
};
export default Prims;
5 changes: 0 additions & 5 deletions src/Writer.js

This file was deleted.

2 changes: 2 additions & 0 deletions src/typeFlags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const CLOSURE_TYPE_FLAG = Symbol('CLOSURE_TYPE_FLAG');
export const NATIVE_FUNC_FLAG = Symbol('NATIVE_FUNC_FLAG');

0 comments on commit c6bfc9c

Please sign in to comment.