├── .gitignore ├── README.md └── nanomonkey.js /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | node_modules 3 | test.js 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nanomonkey 2 | 3 | A JavaScript to JavaScript experimental compiler. 4 | 5 | The Spec is available on the [wiki](https://github.com/sankha93/nanomonkey/wiki/Spec). 6 | 7 | --- 8 | 9 | The aim of this compiler is to take annotated JavaScript source and then output JavaScript. The role of this compiler will be to help developers catch bugs, both by detecting bugs during compilation and by inserting runtime checks that will be executed only in debug mode. 10 | 11 | It can be used to add type-safety checks in a dynamic language like JavaScript. 12 | 13 | Example Input: 14 | 15 | ```javascript 16 | /** 17 | * @precondition {y != 0} 18 | */ 19 | function divide(x, y) { 20 | return x / y; 21 | } 22 | ``` 23 | 24 | Corresponding output: 25 | 26 | ```javascript 27 | function divide(x, y) { 28 | if (!(y != 0)) { 29 | throw new Error("Precondition failed: (y != 0)"); 30 | } 31 | return x / y; 32 | } 33 | ``` 34 | 35 | Original idea inspired from a [Mozilla student project](https://github.com/Yoric/Mozilla-Student-Projects/issues/40). 36 | 37 | ## Why Nanomonkey? 38 | 39 | Because nano is an anagram of anno, and Nanomonkey works on annotated JavaScript. 40 | 41 | ## Contribute 42 | 43 | Help is always welcome. Just contribute to the source (there is none now though) or help in writing the [spec](https://github.com/sankha93/nanomonkey/wiki/Spec). 44 | -------------------------------------------------------------------------------- /nanomonkey.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | esprima = require('esprima'), 3 | escodegen = require('escodegen'); 4 | 5 | var ANNOTATION_PATTERN = /@\w*/; 6 | var EXPRESSION_PATTERN = /\{(.*?)\}/; 7 | 8 | function parseBody(node) { 9 | for (var i = 0; node != undefined && i < node.body.length; i++) { 10 | switch (node.body[i].type) { 11 | case 'FunctionDeclaration': 12 | node.body[i] = parseFunction(node.body[i]); 13 | break; 14 | } 15 | node.body[i].body = parseBody(node.body[i].body); 16 | } 17 | return node; 18 | } 19 | 20 | function parseFunction(node) { 21 | var startLine = node.loc.start.line, 22 | endLine = node.loc.end.line; 23 | var annotation = searchComment(startLine - 1); 24 | if (annotation.type === 'Block') { 25 | var keyword = ANNOTATION_PATTERN.exec(annotation.value); 26 | switch (keyword[0]) { 27 | case '@precondition': 28 | var expr = EXPRESSION_PATTERN.exec(annotation.value); 29 | node.body.body = parsePrecondition(node.body.body, expr[1]); 30 | break; 31 | } 32 | } 33 | return node; 34 | } 35 | 36 | function searchComment(tillLine) { 37 | for (var i = 0; i < ast.comments.length; i ++) { 38 | if (ast.comments[i].loc.end.line === tillLine) 39 | return ast.comments[i]; 40 | } 41 | } 42 | 43 | function parsePrecondition(functionNode, expression) { 44 | var preconditionObj = { 45 | type: 'IfStatement', 46 | test: { type: 'UnaryExpression', 47 | operator: '!', 48 | argument: esprima.parse(expression).body[0].expression, 49 | prefix: true 50 | }, 51 | consequent: { 52 | type: 'BlockStatement', 53 | body: [{ type: 'ThrowStatement', 54 | argument: { 55 | type: 'NewExpression', 56 | callee: { 57 | type: 'Identifier', 58 | name: 'Error' 59 | }, 60 | arguments: [{ type: 'Literal', 61 | value: 'Precondition failed: ' + expression, 62 | raw: '"Precondition failed: ' + expression + '"' 63 | }] 64 | } 65 | }] 66 | }, 67 | alternate: null 68 | }; 69 | functionNode.splice(0, 0, preconditionObj); 70 | return functionNode; 71 | } 72 | 73 | var filename = process.argv[2]; 74 | var content = fs.readFileSync(filename); 75 | var ast = esprima.parse(content, { loc: true, 76 | comment: true}); 77 | var newAST = parseBody(ast); 78 | console.log(escodegen.generate(newAST)); 79 | --------------------------------------------------------------------------------