pegjs-coffee-plugin
Version:
A plugin for PEG.js to use CoffeeScript in your actions.
118 lines (105 loc) • 2.74 kB
JavaScript
//
// pegjs-coffee-plugin
//
var CoffeeScript = require('coffee-script')
var detectIndent = require('detect-indent')
// The acutal compilation of CoffeeScript.
function compileCoffeeScript (code) {
// Compile options
var options = {
bare: true
}
try {
return CoffeeScript.compile(code, options)
} catch (error) {
var message = 'In: \n' + code + '\n was the following error:' + error.message
throw new SyntaxError(message, error.fileName, error.lineNumber)
}
}
// The initializer gets its own scope which we save
// in __initializer for later use
function wrapInitializer (initializer) {
var indent = detectIndent(initializer).indent || ' '
return [
'__initializer = ( ->',
indent + initializer,
indent + 'return this',
').call({})'
].join('\n')
}
function wrapCode (code) {
return [
'return ( -> ',
code,
' ).apply(__initializer)'
].join('')
}
function compileNode (node) {
if (node.code === undefined) {
return
}
// We inject the scope of the initializer if it exists
// into the function that calls the action code
node.code = compileCoffeeScript(wrapCode(node.code))
return node
}
function buildNodeVisitor (visitorMap) {
visitorMap = visitorMap || {}
return function (node) {
var visitor = visitorMap[node.type] || function () {}
return visitor(node)
}
}
function compileExpression (node) {
compile(node.expression)
compileNode(node)
return node
}
function compileSubnodes (property) {
return function (node) {
for (var i = 0; i < node[property].length; i++) {
compile(node[property][i])
}
return node
}
}
// Recursively walk through all nodes
function compile (nodes) {
buildNodeVisitor({
grammar: compileSubnodes('rules'),
choice: compileSubnodes('alternatives'),
sequence: compileSubnodes('elements'),
semantic_not: compileNode,
semantic_and: compileNode,
literal: compileNode,
rule_ref: compileNode,
'class': compileNode,
any: compileNode,
action: compileExpression,
rule: compileExpression,
named: compileExpression,
labeled: compileExpression,
text: compileExpression,
simple_and: compileExpression,
simple_not: compileExpression,
optional: compileExpression,
zero_or_more: compileExpression,
one_or_more: compileExpression
})(nodes)
}
function pass (ast) {
// Add an initializer block if there is none.
if (!ast.initializer) {
ast.initializer = {
type: 'initializer',
code: ''
}
}
ast.initializer.code = compileCoffeeScript(wrapInitializer(ast.initializer.code))
compile(ast)
}
module.exports = {
use: function (config, options) {
config.passes.transform.unshift(pass)
}
}