securityos/node_modules/shell-parse/overrides.js

281 lines
6.3 KiB
JavaScript

/**
* Functions defined here are stringified and injected into grammar actions, so
* their bodies are actually run in a *different* scope.
*
* This means that function arguments are meaningless, and the function bodies
* actually operate on variable names defined in the grammar file. We still
* define them as arguments for readability and so that these functions could
* potentially be used outside of the generated parser.
*
* This also explains the "global" functions used here (such as `text()`)...
* they are defined by the generated parser.
*/
var isArray = require('isarray') // not actually used, just for readability
var rules = exports.rules = {}
exports.initializer = [
'var isArray = require("isarray")',
'var map = require("array-map")',
function join (arr) {
return arr.join("")
},
function literal (string) {
return {
type: 'literal',
value: isArray(string) ? string.join('') : string
}
},
function first (arr) {
return arr[0]
},
function second (arr) {
return arr[1]
},
function flattenConcatenation (pieces) {
// TODO - this algo sucks, it's probably on the order of n^4
var result = [pieces[0]]
, len = pieces.length
, prev = pieces[0]
, current
for (var i = 1; i < len; i++) {
current = pieces[i]
if (current.type == 'concatenation') {
current = flattenConcatenation(current.pieces)
}
if (current.type == 'concatenation') {
// it's still a concatenation, append it's pieces to ours
result = result.concat(current.pieces)
}
else if ((current.type == 'literal' || current.type == 'glob')
&& (prev.type == 'literal' || prev.type == 'glob')) {
// globs & literals can be merged
prev.value += current.value
if (prev.type != 'glob' && current.type == 'glob') {
// globs are infectious
prev.type = 'glob'
}
}
else {
result.push(current)
prev = current
}
}
return result.length == 1 ? result[0] : {
type: 'concatenation',
pieces: result
}
}
].join('\n')
rules.script = function (sections) {
var statements = []
map(sections, function (section) {
statements = statements.concat(section[1])
})
return statements
}
rules.statementList = function (first, rest, last) {
var statements = [first]
var prev = first
map(rest, function (spaceOpSpaceCmd, i, cmds) {
setOperator(spaceOpSpaceCmd[1], prev)
statements.push(prev = spaceOpSpaceCmd[3])
})
return statements
function setOperator(operator, command) {
while (command.next) {
command = command.next
}
command.control = operator
}
}
rules.conditionalLoop = function (kind, test, body) {
return {
type: kind + '-loop',
test: test,
body: body
}
}
rules.ifBlock = function (test, body, elifBlocks, elseBody) {
return {
type: 'ifElse',
test: test,
body: body,
elifBlocks: elifBlocks.length ? elifBlocks : null,
elseBody: elseBody ? elseBody[1] : null,
}
}
rules.elifBlock = function (test, body) {
return {
type: 'ifElse',
test: test,
body: body
}
}
rules.condition = function bare (test) {
return test
}
rules.statement = function (statement, next) {
if (typeof next !== 'undefined' && next) {
next = next[1]
statement.control = next[0]
statement.next = next[1]
} else {
statement.control = ';'
statement.next = null
}
return statement
}
rules.command = function (pre, name, post, pipe) {
var command = {
type: 'command',
command: name,
args: [],
redirects: [],
env: {},
control: ';',
next: null,
}
map(pre, first).concat(map(post, second)).forEach(function (token) {
if (!token || !token.type) return
switch (token.type) {
case 'moveFd':
case 'duplicateFd':
case 'redirectFd':
return command.redirects.push(token)
case 'variableAssignment':
return command.env[token.name] = token.value
default:
command.args.push(token)
}
})
if (pipe) {
command.redirects.push(pipe[1])
}
return command
}
rules.chainedStatement = function (operator, statement) {
return [operator, statement]
}
rules.commandName = function (name) {
return name
}
rules.controlOperator = function (op) {
return op == '\n' ? ';' : op
}
rules.processSubstitution = function (rw) {
return {
type: 'processSubstitution',
readWrite: rw,
commands: commands,
}
}
rules.environmentVariable = function () {
return {type: 'variable', name: name}
}
rules.variableSubstitution = function () {
return {
type: 'variableSubstitution',
expression: join(expr), // TODO subParser
}
}
rules.variableAssignment = function (name, value) {
return {type: 'variableAssignment', name: name, value: value}
}
rules.writableVariableName = function () { return text() }
rules.bareword = function (cs) { return literal(cs) }
rules.glob = function (cs) {
return {
type: 'glob',
value: text()
}
}
rules.escapedMetaChar = function (character) { return character }
rules.concatenation = function (pieces) {
return flattenConcatenation(pieces)
}
rules.singleQuote = function (inner) { return literal(inner) }
rules.doubleQuote = function (contents) {
var pieces = contents.map(function (it) {
return isArray(it) ? literal(it) : it
})
return flattenConcatenation(pieces)
}
rules.escapedQuote = function (character) {
return character
}
rules.parenCommandSubstitution = function (commands) {
return {
type: 'commandSubstitution',
commands: commands
}
}
rules.backQuote = function (input) {
return { type: 'commandSubstitution', commands: parse(input.join('')) }
}
rules.pipe = function (command) {
return {type: 'pipe', command: command}
}
rules.moveFd = function (fd, op, dest) {
if (fd == null) fd = op[0] == '<' ? 0 : 1;
return {
type: 'moveFd',
fd: fd,
op: op,
dest: dest
}
}
rules.duplicateFd = function (src, op, dest) {
if (src == null) src = op[0] == '<' ? 0 : 1;
return {
type: 'duplicateFd',
srcFd: src,
op: op,
destFd: dest,
}
}
rules.redirectFd = function (fd, op, filename) {
if (fd == null) fd = op[0] == '<' ? 0 : 1;
return {
type: 'redirectFd',
fd: fd,
op: op,
filename: filename
}
}