Skip to content

Commit

Permalink
initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
goatslacker committed Mar 25, 2012
0 parents commit 40fe808
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lib
node_modules
47 changes: 47 additions & 0 deletions Cakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
fs = require 'fs'
{print} = require 'util'
{spawn, exec} = require 'child_process'

# ANSI Terminal Colors
bold = '\033[0;1m'
green = '\033[0;32m'
reset = '\033[0m'
red = '\033[0;31m'

log = (message, color, explanation) ->
console.log color + message + reset + ' ' + (explanation or '')

build = (watch, callback) ->
if typeof watch is 'function'
callback = watch
watch = false
options = ['-c', '-o', 'lib', 'src']
options.unshift '-w' if watch

coffee = spawn 'coffee', options
coffee.stdout.on 'data', (data) -> print data.toString()
coffee.stderr.on 'data', (data) -> log data.toString(), red
coffee.on 'exit', (status) -> callback?() if status is 0

test = (callback) ->
options = ['--spec']
spec = spawn 'vows', options
spec.stdout.on 'data', (data) -> print data.toString()
spec.stderr.on 'data', (data) -> log data.toString(), red
spec.on 'exit', (status) -> callback?() if status is 0


task 'docs', 'Generate annotated source code with Docco', ->
fs.readdir 'src', (err, contents) ->
files = ("src/#{file}" for file in contents when /\.coffee$/.test file)
docco = spawn 'docco', files
docco.stdout.on 'data', (data) -> print data.toString()
docco.stderr.on 'data', (data) -> log data.toString(), red
docco.on 'exit', (status) -> callback?() if status is 0


task 'build', ->
build -> log ":)", green

task 'test', 'Run Vows', ->
build -> test -> log ":)", green
25 changes: 25 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"author": "Josh Perez <josh@goatslacker.com> (http://goatslacker.com)",
"name": "fn-extractor",
"description": "Extracts functions",
"version": "0.0.0",
"repository": {
"type": "git",
"url": "git://github.com/goatslacker/fn-extractor.git"
},
"main": "lib/parser.js",
"scripts": {
"prepublish": "cake build",
"test": "cake test"
},
"dependencies": {
"esprima": "0.9.8"
},
"devDependencies": {
"vows": "latest"
},
"optionalDependencies": {},
"engines": {
"node": "*"
}
}
104 changes: 104 additions & 0 deletions src/parser.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
esprima = require 'esprima'
vm = require 'vm'

traverse = (object, visitor, master) ->

parent = if master is 'undefined' then [] else master

return if visitor.call(null, object, parent) is false

Object.keys(object).forEach (key) ->
child = object[key]
path = [object]
path.push parent

traverse(child, visitor, path) if typeof child is 'object' and child isnt null

getFunctions = (tree, code) ->
list = []

traverse tree, (node, path) ->
if node.type is 'FunctionDeclaration'
list.push {
name: node.id.name
params: node.params
range: node.range
blockStart: node.body.range[0]
end: node.body.range[1]
}

else if node.type is 'FunctionExpression'
parent = path[0]

if parent.type is 'AssignmentExpression'
if typeof parent.left.range isnt 'undefined'
list.push {
name: code.slice(parent.left.range[0], parent.left.range[1] + 1)
params: node.params
range: node.range
blockStart: node.body.range[0]
end: node.body.range[1]
}

else if parent.type is 'VariableDeclarator'
list.push {
name: parent.id.name
params: node.params
range: node.range
blockStart: node.body.range[0]
end: node.body.range[1]
}

else if parent.type is 'CallExpression'
list.push {
name: if parent.id then parent.id.name else '[Anonymous]'
params: node.params
range: node.range
blockStart: node.body.range[0]
end: node.body.range[1]
}

else if typeof parent.length is 'number'
list.push {
name: if parent.id then parent.id.name else '[Anonymous]'
params: node.params
range: node.range
blockStart: node.body.range[0]
end: node.body.range[1]
}

else if typeof parent.key isnt 'undefined'
if parent.key.type is 'Identifier'
if parent.value is node and parent.key.name
list.push {
name: parent.key.name
params: node.params
range: node.range
blockStart: node.body.range[0]
end: node.body.range[1]
}


list


compile = (name, node, code) ->
args = []
context = {}

node.params.forEach (param) -> args.push param.name

wrapped = "function #{name}(#{args.join(',')}) {\n #{code.slice(node.blockStart + 1, node.end)}\n}"

vm.runInNewContext wrapped, context

context[name]


module.exports = parse = (code) ->
tree = esprima.parse code, { loc: true, range: true }
functions = getFunctions tree, code
exported = {}
functions.forEach (fn) -> exported[fn.name] = compile fn.name, fn, code

exported
16 changes: 16 additions & 0 deletions test/index-test.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
parser = require '../'
vows = require 'vows'
assert = require 'assert'

test = vows.describe 'parser'
test.addBatch {
'when parsing code':
topic: ->
code = '(function () { var fn = function() { return 1; }; function foo() { return 2; } })'
parser code

'returns 1 when calling fn': (r) -> assert.equal r.fn(), 1
'returns 2 when calling foo': (r) -> assert.equal r.foo(), 2
}

test.export module

0 comments on commit 40fe808

Please sign in to comment.