Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: smtlaissezfaire/loop
base: 852b4d8e78
...
head fork: smtlaissezfaire/loop
compare: b41f21a145
  • 7 commits
  • 13 files changed
  • 0 commit comments
  • 1 contributor
View
1  .gitignore
@@ -1,6 +1,5 @@
lib/loop/grammar.js
TODO.txt
-*.loop
node_modules/*
!node_modules/uglify-js
View
6 lib/loop/compiler.js
@@ -1,5 +1,6 @@
var loop = require('./../loop');
var pro = require("uglify-js").uglify;
+var fs = require('fs');
var compiler = {};
@@ -8,6 +9,11 @@ compiler.compile = function(str, options) {
options = {};
}
+ loop.macroCompiler.addEval();
+
+ var stdlibBuiltInMacros = fs.readFileSync(__dirname + "/stdlib/built-ins.loop").toString();
+ loop.macroCompiler.addMacrosFromAst(loop.parse(stdlibBuiltInMacros));
+
var macroTransformedAst = loop.macroCompiler.transform(loop.parse(str));
var ast = loop.loopEval(macroTransformedAst);
var uglifyAst = loop.toUglifyTree(ast, options);
View
3  lib/loop/eval.js
@@ -48,7 +48,7 @@ var loopEval = function(syntaxTree) {
contents: body
}
};
- } else if (firstToken.contents === 'lambda' || firstToken.contents === 'function') {
+ } else if (firstToken.contents === 'lambda') {
var formalArgs = car(remainingTokens);
body = loopEvalCollection(cdr(remainingTokens));
@@ -92,6 +92,7 @@ var loopEval = function(syntaxTree) {
case 'number':
case 'comment':
case 'regexp':
+ case 'raw_js':
return syntaxTree;
default:
throw new Error('unknown type of syntaxTree, type: ' + syntaxTree.type);
View
81 lib/loop/macro-compiler.js
@@ -2,6 +2,8 @@ var _ = require("./util/underscore");
var printTree = require('./util/tree-inspector').withLineNumbers;
var helpers = require('./util/helpers');
var compiler = {};
+var loop = require('../loop');
+var fs = require('fs');
var macros = {};
@@ -13,8 +15,8 @@ compiler.reset = function() {
macros = {};
};
-compiler.transform = function(ast) {
- // extract out macro definitions,
+// extract out macro definitions and add them to the macro list
+compiler.addMacrosFromAst = function(ast) {
_.each(ast, function(tree) {
if (tree.type === 'list') {
var contents = tree.contents;
@@ -36,6 +38,10 @@ compiler.transform = function(ast) {
}
}
});
+};
+
+compiler.transform = function(ast) {
+ this.addMacrosFromAst(ast);
ast = compiler.macroCompile(ast);
@@ -71,6 +77,54 @@ compiler.addMacro = function(obj) {
macros[id].push(obj);
};
+compiler.addEval = function() {
+ compiler.addMacro({
+ template: {
+ type: 'list',
+ contents: [
+ { type: 'id', contents: '__loop_js_eval__'},
+ { type: 'id', contents: 'args' },
+ { type: 'macro-pattern' }
+ ]
+ },
+ replacement: {
+ type: '__loop_js_eval__',
+ fn: function(locals) {
+ var str = locals.args[0].contents;
+ return require('vm').runInNewContext(str).toString();
+ }
+ }
+ });
+
+ compiler.addMacro({
+ template: {
+ type: 'list',
+ contents: [
+ { type: 'id', contents: '__loop_eval__'},
+ { type: 'id', contents: 'body' },
+ { type: 'macro-pattern' }
+ ]
+ },
+ replacement: {
+ type: '__loop_eval__',
+ fn: function(locals) {
+ var loop = require('../loop');
+ var macroTransformedAst = loop.macroCompiler.transform(locals);
+ var ast = loop.loopEval(macroTransformedAst[1]);
+ var uglifyAst = loop.toUglifyTree(ast, {});
+ var pro = require("uglify-js").uglify;
+ var jsCode = pro.gen_code(uglifyAst, {});
+
+ return require('vm').runInNewContext(jsCode, {
+ require: require,
+ loop: loop,
+ fs: fs
+ }).toString();
+ }
+ }
+ });
+};
+
var isList = function(tree) {
return tree && tree.type === 'list' && tree.contents;
};
@@ -351,22 +405,28 @@ var extractFormalsFromList = function(template, ast, locals) {
extractFormals = function(template, ast) {
var locals = {};
-
template = helpers.deepCopy(template);
-
markOffPatternElements(template);
-
template = removePatternElements(template);
-
extractFormalsFromList(template, ast, locals);
-
return locals;
};
var replaceFormalsFromList = function(replacement, locals, shouldShift) {
- replacement = helpers.deepCopy(replacement);
-
- if (replacement.type === 'list') {
+ replacement = _.extend({}, replacement);
+
+
+ if (replacement.type === '__loop_js_eval__') {
+ return {
+ type: 'raw_js',
+ contents: replacement.fn(locals)
+ };
+ } else if (replacement.type === '__loop_eval__') {
+ return {
+ type: 'raw_js',
+ contents: replacement.fn(locals)
+ };
+ } else if (replacement.type === 'list') {
// use case:
// replacement: (a b) ...,
// locals: {}
@@ -451,7 +511,6 @@ var replaceFormalsFromList = function(replacement, locals, shouldShift) {
};
macroReplaceFromLocals = function(replacement, locals) {
- replacement = helpers.deepCopy(replacement);
markOffPatternElements(replacement);
replacement = removePatternElements(replacement);
return replaceFormalsFromList(replacement, locals);
View
20 lib/loop/stdlib/built-ins.loop
@@ -0,0 +1,20 @@
+(define-macro
+ (if arg body ...)
+ (cond (arg body ...)))
+
+(define-macro
+ (function body ...)
+ (lambda body ...))
+
+(define-macro
+ (let ((key value) ...)
+ body ...)
+ ((lambda (key ...)
+ body ...) value ...))
+
+(define-macro
+ (__loop_include__ filename)
+ (__loop_eval__
+ (let ((buf (fs.readFileSync filename)))
+ ; eek! shouldn't need this return
+ (return (loop.compile (buf.toString))))))
View
15 lib/loop/uglify.js
@@ -161,6 +161,8 @@ var _var = function(pairs) {
var progn = function(tree) {
switch (tree.type) {
+ case 'raw_js':
+ return require('uglify-js').parser.parse(tree.contents.toString());
case 'funcall':
var f = tree.function;
var processedArgs;
@@ -179,7 +181,7 @@ var progn = function(tree) {
throw new Error("quote must take one argument");
}
return ['string', car(args).contents];
- } else if (f.contents === 'lambda' || f.contents === 'function') {
+ } else if (f.contents === 'lambda') {
var formalArgs = _.map(car(args).contents, function(arg) {
return arg.contents;
});
@@ -263,15 +265,6 @@ var progn = function(tree) {
});
return mainList;
- } else if (f.contents === 'if') {
- var condition = args.shift();
- body = args;
-
- condition = progn(condition);
-
- body = processBody(args);
-
- return ['if', condition, ['block', body]];
} else if (f.contents === '{}') {
pairs = [];
_.inGroupsOf(args, 2, function(pair) {
@@ -433,6 +426,7 @@ processStatementOrExpression = function(statementOrExpression) {
case 'prop-access':
case 'number':
case 'regexp':
+ case 'raw_js':
return stmt(progn(statementOrExpression));
case 'comment':
return progn(statementOrExpression);
@@ -443,7 +437,6 @@ processStatementOrExpression = function(statementOrExpression) {
if (f.type === 'id' &&
(f.contents === 'var' ||
- f.contents === 'if' ||
f.contents === 'cond' ||
f.contents === '//')) {
isStatement = false;
View
1  test/fixtures/define-x-as-10.js
@@ -0,0 +1 @@
+var x = 10;
View
1  test/fixtures/define_x_equal_to_one.loop
@@ -0,0 +1 @@
+(define x 1)
View
3  test/fixtures/macro-my-unless.loop
@@ -0,0 +1,3 @@
+(define-macro
+ (my-unless condition body)
+ (if (! condition) body))
View
1  test/integration/if-cond-spec.js
@@ -19,7 +19,6 @@ describe("if + cond", function() {
it('should be able to handle an if statement with no block conditions', function() {
var code = "(if x)";
-
assert.equal(loop.compile(code), 'if(x){}');
});
View
30 test/integration/macro-spec.js
@@ -469,6 +469,7 @@ describe("integration specs (macros)", function() {
assert.equal(loop.compile(code), expected);
});
+
it('should use three arguments', function() {
var code = "";
code += let_star();
@@ -488,6 +489,7 @@ describe("integration specs (macros)", function() {
assert.equal(loop.compile(code), expected);
});
+
it('should use one argument but multiple body statements', function() {
var code = "";
code += let_star();
@@ -552,4 +554,32 @@ describe("integration specs (macros)", function() {
var expected = "mult(2,mult(2,1))";
assert.equal(loop.compile(code), expected);
});
+
+ it('should be able to evaluate raw js', function() {
+ var code = '';
+ code += '(define x (__loop_js_eval__ "7 + 1"))';
+ var expected = "var x=8";
+ assert.equal(loop.compile(code), expected);
+ });
+
+ it('should be able to evaluate raw, but complex js', function() {
+ var jsCode = '(function() { var x = 10; var y = 20; return x + y }())';
+
+ var code = '';
+ code += "(define x (__loop_js_eval__ '" + jsCode + "'))";
+ var expected = "var x=30";
+ assert.equal(loop.compile(code), expected);
+ });
+
+ it("should allow a macro to evaluate its arguments", function() {
+ var code = '';
+ code += '(define-macro';
+ code += ' (eight)';
+ code += ' (__loop_eval__ (+ 7 1)))';
+ code += '(define my_eight (eight))';
+
+ var expected = "var my_eight=8";
+
+ assert.equal(loop.compile(code), expected);
+ });
});
View
43 test/integration/require-spec.js
@@ -0,0 +1,43 @@
+var assert = require("assert");
+var loop = require(__dirname + "/../../lib/loop");
+var path = require('path');
+
+describe("include", function() {
+ beforeEach(function() {
+ loop.macroCompiler.reset();
+ });
+
+ it('should be able to include a file with .loop extension', function() {
+ var filePath = path.join(__dirname, '..', 'fixtures', 'define_x_equal_to_one.loop');
+ var code = "";
+ code += "(__loop_include__ '" + filePath + "')";
+ var expectedCode = "var x=1";
+ assert.equal(loop.compile(code), expectedCode);
+ });
+
+ it('should be able to use the variables from a required file', function() {
+ var filePath = path.join(__dirname, '..', 'fixtures', 'define_x_equal_to_one.loop');
+ var code = "";
+ code += "(__loop_include__ '" + filePath + "')";
+ code += "(define y (+ x 10))";
+ var expectedCode = '';
+ expectedCode += "var x=1;";
+ expectedCode += 'var y=x+10';
+ assert.equal(loop.compile(code), expectedCode);
+ });
+
+ it('should be able to use a macro from a required file', function() {
+ var filePath = path.join(__dirname, '..', 'fixtures', 'macro-my-unless.loop');
+ var code = "";
+ code += "(__loop_include__ \"" + filePath + "\")";
+ code += '(my-unless x';
+ code += ' (console.log "foo!"))';
+
+ var expectedCode = '';
+ expectedCode += 'if(!x){';
+ expectedCode += 'console.log("foo!")';
+ expectedCode += '}';
+
+ assert.equal(loop.compile(code), expectedCode);
+ });
+});
View
44 test/stdlib/let-spec.js
@@ -0,0 +1,44 @@
+var assert = require("assert");
+var loop = require(__dirname + "/../../lib/loop");
+
+describe("built in let", function() {
+ it('should be able to use the let with 2 args', function() {
+ var code = '';
+ code += '(let ((x 10)';
+ code += ' (y 20))';
+ code += ' (console.log (+ x y)))';
+
+ var expected = '';
+ expected += '(function(x,y){';
+ expected += 'console.log(x+y)';
+ expected += '})(10,20)';
+
+ assert.equal(loop.compile(code), expected);
+ });
+
+ it('should be able to use the let with zero args', function() {
+ var code = '';
+ code += '(let ()';
+ code += ' (console.log (+ x y)))';
+
+ var expected = '';
+ expected += '(function(){';
+ expected += 'console.log(x+y)';
+ expected += '})()';
+
+ assert.equal(loop.compile(code), expected);
+ });
+
+ it.pending('should always return the last value', function() {
+ var code = '';
+ code += '(let ()';
+ code += ' (console.log (+ x y)))';
+
+ var expected = '';
+ expected += '(function(){';
+ expected += 'console.log(x+y)';
+ expected += '})()';
+
+ assert.equal(loop.compile(code), expected);
+ });
+});

No commit comments for this range

Something went wrong with that request. Please try again.