Skip to content

Commit 7a99b8a

Browse files
committed
more stuff
1 parent b5083c4 commit 7a99b8a

12 files changed

+199
-77
lines changed

build.js

+45-9
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,52 @@
11
var fs = require('fs')
22
var pegjs = require('pegjs')
33
var overrideAction = require('pegjs-override-action')
4-
var grammar = fs.readFileSync(__dirname + '/grammar.pegjs', 'utf8')
54

6-
var parserSource = pegjs.buildParser(grammar, {
7-
output: "source",
8-
plugins: [overrideAction],
9-
overrideActionPlugin: require('./overrides')
10-
})
5+
var input = __dirname + '/grammar.pegjs'
6+
var output = __dirname + '/parser.js'
7+
var overrides = __dirname + '/overrides.js'
118

129
if (require.main === module) {
13-
console.log('module.exports=' + parserSource + '.parse')
14-
} else {
15-
module.exports = eval(parserSource).parse
10+
if (process.argv[2] == '-w') {
11+
watch()
12+
} else {
13+
console.log(getSource())
14+
}
15+
}
16+
17+
function getSource () {
18+
var grammar = fs.readFileSync(input, 'utf8')
19+
var parserSource = pegjs.buildParser(grammar, {
20+
output: "source",
21+
/*
22+
allowedStartRules: [
23+
'commandList',
24+
'command',
25+
'argument'
26+
],
27+
*/
28+
plugins: [overrideAction],
29+
overrideActionPlugin: require('./overrides')
30+
})
31+
return [
32+
'var parser=' + parserSource,
33+
'parser.parse.SyntaxError = parser.SyntaxError',
34+
'module.exports=parser.parse'
35+
].join('\n');
36+
}
37+
38+
function watch () {
39+
fs.watchFile(input, onChange)
40+
fs.watchFile(overrides, onChange)
41+
42+
function onChange (curr, prev) {
43+
if (curr.mtime > prev.mtime) {
44+
try {
45+
var source = getSource()
46+
fs.writeFileSync(output, source)
47+
} catch (err) {
48+
console.error(err.message)
49+
}
50+
}
51+
}
1652
}

grammar.pegjs

+8-13
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,7 @@ variableAssignment
1313
= writableVariableName '=' argToken
1414

1515
commandToken "command name"
16-
= bareword
17-
/ concatenation
18-
/ doubleQuote
19-
/ variableSubstitution
20-
/ environmentVariable
21-
/ subShell
22-
/ backticks
16+
= concatenation
2317

2418
argToken "command argument"
2519
= commandToken
@@ -33,7 +27,7 @@ writableVariableName = [a-zA-Z0-9_]+
3327
readableVariableName = writableVariableName / '?' /* todo, other special vars */
3428

3529
bareword
36-
= cs:(escapedMetaChar / [^$"';&<>\n()\[\]*?| ])+
30+
= cs:(escapedMetaChar / [^$"';&<>\n()\[\]*?|` ])+
3731

3832
escapedMetaChar
3933
= '\\' character:[$\\"&<> ]
@@ -43,10 +37,11 @@ variableSubstitution
4337

4438
concatenation
4539
= pieces:( bareword
40+
/ singleQuote
4641
/ doubleQuote
4742
/ environmentVariable
4843
/ variableSubstitution
49-
/ subShell
44+
/ subshell
5045
/ backticks
5146
)+
5247

@@ -69,13 +64,13 @@ expandsInQuotes
6964
= backticks
7065
/ environmentVariable
7166
/ variableSubstitution
72-
/ subShell
67+
/ subshell
7368

7469
backticks
75-
= '`' commands:commandList+ '`'
70+
= '`' commands:(!backticks command)+ '`'
7671

77-
subShell
78-
= '$(' commands:commandList+ ')'
72+
subshell
73+
= '$(' commands:command+ ')'
7974

8075
commandSubstitution
8176
= rw:[<>] '(' commands:commandList ')'

overrides.js

+30-21
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var rules = exports.rules = {}
1717

1818
exports.initializer = [
1919
'var isArray = require("isarray")',
20+
'var map = require("array-map")',
2021
function join (arr) {
2122
return arr.join("")
2223
},
@@ -32,15 +33,30 @@ exports.initializer = [
3233
function second (arr) {
3334
return arr[1]
3435
},
35-
function flattenConcatenations (pieces) {
36-
var results = []
37-
pieces.forEach(function (piece) {
38-
if (piece.type == 'concatenation')
39-
results = results.concat(piece.pieces)
40-
else
41-
results.push(piece)
42-
})
43-
return results
36+
function flattenConcatenation (pieces) {
37+
var result = [pieces[0]]
38+
, len = pieces.length
39+
, prev = pieces[0]
40+
, current
41+
42+
for (var i = 1; i < len; i++) {
43+
current = pieces[i]
44+
if (current.type == 'concatenation') {
45+
current = flattenConcatenation(current)
46+
}
47+
if (current.type == 'literal' && prev.type == 'literal') {
48+
// merge two literals
49+
prev.value += current.value
50+
}
51+
else {
52+
result.push(current)
53+
prev = current
54+
}
55+
}
56+
return result.length == 1 ? result[0] : {
57+
type: 'concatenation',
58+
pieces: result
59+
}
4460
}
4561
].join('\n')
4662

@@ -53,7 +69,7 @@ rules.command = function (command, args, redirects, control) {
5369
return {
5470
type: 'command',
5571
command: command,
56-
args: args.map(second),
72+
args: map(args, second),
5773
redirects: redirects,
5874
control: control || ';',
5975
env: env
@@ -91,30 +107,23 @@ rules.bareword = function (cs) { return literal(cs) }
91107
rules.escapedMetaChar = function (character) { return character }
92108

93109
rules.concatenation = function (pieces) {
94-
pieces = flattenConcatenations(pieces)
95-
return pieces.length == 1 ? pieces[0] : {
96-
type: 'concatenation',
97-
pieces: pieces
98-
}
110+
return flattenConcatenation(pieces)
99111
}
100112

101113
rules.singleQuote = function (cs) { return literal(cs) }
102114
rules.doubleQuote = function (contents) {
103115
var pieces = contents.map(function (it) {
104116
return isArray(it) ? literal(it) : it
105117
})
106-
pieces = flattenConcatenations(pieces)
107-
return pieces.length == 1 ? pieces[0] : {
108-
type: 'concatenation',
109-
pieces: pieces
110-
}
118+
return flattenConcatenation(pieces)
111119
}
112120

113121
rules.escapedQuote = function (character) {
114122
return character
115123
}
116124
rules.backticks = function (commands) {
117-
return {type: 'backticks', commands: commands}
125+
debugger
126+
return {type: 'backticks', commands: commands.map(second)}
118127
}
119128

120129
rules.subshell = function (commands) {

package.json

+27-10
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,42 @@
44
"description": "Parse bash scripts into AST's",
55
"main": "parser.js",
66
"dependencies": {
7-
"isarray": "0.0.1"
7+
"isarray": "0.0.1",
8+
"array-map": "0.0.0"
9+
},
10+
"scripts": {
11+
"test": "tape tests/*.js",
12+
"watch-grammar": "node build.js -w &",
13+
"prepublish": "node build.js > parser.js"
814
},
915
"testling": {
1016
"files": "tests/*.js",
1117
"browsers": {
12-
"ie": [ 8, 9, 10 ],
13-
"firefox": [ "3.5", "nightly" ],
14-
"opera": [ 11, 17 ],
15-
"safari": ["5.1"]
18+
"ie": [
19+
8,
20+
9,
21+
10
22+
],
23+
"firefox": [
24+
"3.5",
25+
"nightly"
26+
],
27+
"opera": [
28+
11,
29+
17
30+
],
31+
"safari": [
32+
"5.1"
33+
]
1634
}
1735
},
1836
"devDependencies": {
1937
"pegjs": "~0.8.0",
2038
"pegjs-override-action": "0.0.7",
21-
"tape": "~2.3.2"
22-
},
23-
"scripts": {
24-
"test": "tape tests/*.js",
25-
"prepublish": "node build.js > parser.js"
39+
"tape": "~2.3.2",
40+
"watchify": "~0.5.0",
41+
"catw": "~0.2.0",
42+
"covert": "~0.2.0"
2643
},
2744
"repository": {
2845
"type": "git",

tests/concatenation.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
var test = require('tape')
2+
var parse = require('../parser')
3+
4+
test('concatenation', function (t) {
5+
var ast = parse('echo "two "\' strings\'')
6+
t.deepEqual(ast[0].args, [
7+
{ type: 'literal', value: 'two strings' }
8+
], 'concatenated literals are flattened')
9+
10+
var ast = parse('echo $var"and a string"')
11+
t.deepEqual(ast[0].args, [
12+
{ type: 'concatenation',
13+
pieces: [
14+
{ type: 'variable',
15+
name: 'var' },
16+
{ type: 'literal',
17+
value: 'and a string' }
18+
]
19+
}
20+
], 'can concatenate $var and strings')
21+
22+
var ast = parse('echo "it\'s easy to switch "\'"back"\'" and "\'"forth"\'')
23+
t.deepEqual(ast[0].args, [
24+
{ type: 'literal',
25+
value: 'it\'s easy to switch "back" and "forth"' }
26+
], 'can concatenate alternating quote contexts')
27+
28+
t.end()
29+
})

tests/escaping-in-quotes.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
var test = require('tape')
2-
var parse = require('../build')
2+
var parse = require('../parser')
33

44
test('escaping in quotes', function (t) {
55
var ast = parse('echo "An escaped double-quote: \\""')

tests/escaping-meta-chars.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
var test = require('tape')
2-
var parse = require('../build')
2+
var parse = require('../parser')
33

44
test('escaping meta characters in barewords', function (t) {
55
var ast = parse('echo I\\ am\\ one\\ arg')[0]

tests/interpolation-in-quotes.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
var test = require('tape')
2+
var parse = require('../parser')
3+
4+
test('interpolating in quotes', function (t) {
5+
6+
var ast = parse('echo "interpolated `backtick command`"')
7+
t.deepEqual(ast[0].args, [{
8+
type: 'concatenation',
9+
pieces: [
10+
{ type: "literal", value:"interpolated " },
11+
{ type: "backticks",
12+
commands: [
13+
{ type: 'command',
14+
command: { type: 'literal', value: 'backtick' },
15+
args: [
16+
{type: 'literal', value: 'command'}
17+
],
18+
redirects: [],
19+
env: {},
20+
control: ';' }
21+
]
22+
}]
23+
}], "Can interpolate back-ticks (but you really shouldn't!)")
24+
25+
26+
var ast = parse('echo "interpolated $(command1; command2)"')
27+
t.deepEqual(ast[0].args, [{
28+
type: 'concatenation',
29+
pieces: [
30+
{ type: "literal", value:"interpolated " },
31+
{ type: "subshell",
32+
commands: [
33+
{ type: 'command',
34+
command: { type: 'literal', value: 'command1' },
35+
args: [],
36+
redirects: [],
37+
env: {},
38+
control: ';' },
39+
{ type: 'command',
40+
command: { type: 'literal', value: 'command2' },
41+
args: [],
42+
redirects: [],
43+
env: {},
44+
control: ';' }
45+
]
46+
}]
47+
}], "Can interpolate subshells (better idea!)")
48+
t.end()
49+
})

tests/multi-line-strings.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
var test = require('tape')
2-
var parse = require('../build')
2+
var parse = require('../parser')
33

44
test('Multi-line strings', function (t) {
55
var input = [

tests/multiple-commands.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
var test = require('tape')
2-
var parse = require('../build')
2+
var parse = require('../parser')
33

44
test('Multiple commands', function (t) {
55
var input = [

tests/npm-debug.log

-20
This file was deleted.

tests/variable-substitution.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
var test = require('tape')
2+
var parse = require('../parser')
3+
4+
test('variable substitutions', function (t) {
5+
t.skip('not yet')
6+
t.end()
7+
})

0 commit comments

Comments
 (0)