Skip to content
This repository was archived by the owner on Jul 15, 2021. It is now read-only.

Commit bc5536a

Browse files
committed
initial commit of tracer branch. this will be merged in or abandoned depnding on whether i can get the performance penalty down to a reasonable multiplier of the current master branch build. refs #2
1 parent 612b584 commit bc5536a

12 files changed

Lines changed: 28004 additions & 1132 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
All notable changes to this project will be documented in this file.
33

44
## [Unreleased][unreleased]
5+
### Changed
6+
- Created a `tracer` branch to continue developing the `Tracer` class into something viable.
57

68
## [v0.10.1] - 2015-07-09
79
### Changed

Gruntfile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ module.exports = function(grunt) {
7070
options: {
7171
failOnError: true
7272
},
73-
command: './node_modules/.bin/pegjs --optimize "speed" src/grammar.pegjs lib/parser.js'
73+
command: './node_modules/.bin/pegjs --trace src/grammar.pegjs lib/parser.js'
7474
},
7575
test: {
7676
options: {

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
[![devDependencies Status Image](https://img.shields.io/david/dev/codeschool/sqlite-parser.svg)](https://github.com/codeschool/sqlite-parser/)
66
[![License Type Image](https://img.shields.io/github/license/codeschool/sqlite-parser.svg)](https://github.com/codeschool/sqlite-parser/blob/master/LICENSE)
77

8+
## This branch is a work-in-progress
9+
_Note: There is a currently a significant performance penalty (14x) to using this branch for the smart-error functionality._
10+
811
This library parses SQLite queries, using JavaScript, and generates
912
_abstract syntax tree_ (AST) representations of the input strings. A
1013
syntax error is produced if an AST cannot be generated.
@@ -47,6 +50,13 @@ sqliteParser(sampleSQL, function (err, res) {
4750
});
4851
```
4952

53+
## Syntax Errors
54+
55+
This parser uses the `--trace` flag exposed in `pegjs` to create "smart" error
56+
messages. The parser includes a `Trace` class that keeps track of which grammar
57+
rules were being traversed just prior to the error and uses that information
58+
to improve the error message and location information.
59+
5060
## AST
5161

5262
**NOTE: The SQLite AST is a work-in-progress and subject to change.**

demo/js/sqlite-parser-demo.js

Lines changed: 16 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/sqlite-parser-min.js

Lines changed: 10 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/sqlite-parser.js

Lines changed: 13927 additions & 555 deletions
Large diffs are not rendered by default.

index.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@
44
* @author Nick Wronski <nick@javascript.com>
55
*/
66
;(function (root) {
7-
var parser = require('./lib/parser');
7+
var parser = require('./lib/parser'),
8+
Tracer = require('./lib/tracer');
89

910
function sqliteParser(source, callback) {
11+
var t = Tracer(), res;
1012
try {
11-
callback(null, parser.parse(source));
13+
res = parser.parse(source, {
14+
'tracer': t
15+
});
16+
callback(null, res);
1217
} catch (e) {
13-
callback(e);
18+
callback(e instanceof parser.SyntaxError ? t.smartError(e) : e);
1419
}
1520
}
1621
sqliteParser['NAME'] = 'sqlite-parser';
17-
sqliteParser['VERSION'] = '0.10.1';
22+
sqliteParser['VERSION'] = '0.11.0';
1823

1924
module.exports = root.sqliteParser = sqliteParser;
2025
})(typeof self === 'object' ? self : global);

lib/parser.js

Lines changed: 13808 additions & 552 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/tracer.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*!
2+
* sqlite-parser
3+
* @copyright Code School 2015 {@link http://codeschool.com}
4+
* @author Nick Wronski <nick@javascript.com>
5+
*/
6+
var parserUtils = require('./parser-util');
7+
8+
module.exports = (function (util) {
9+
Tracer = function Tracer() {
10+
if (!(this instanceof Tracer)) {
11+
return new Tracer();
12+
}
13+
this.events = [];
14+
this.indentation = 0;
15+
this.whitespaceRule = /(^whitespace)|(char$)|(^[oe]$)/i;
16+
this.statementRule = /Statement$/i;
17+
this.firstNodeRule = /(Statement|Clause)$/i;
18+
};
19+
20+
Tracer.prototype.trace = function trace(event) {
21+
var that = this, lastIndex, lastWsIndex;
22+
event.indentation = this.indentation;
23+
switch (event.type) {
24+
case 'rule.enter':
25+
// add entered leaf
26+
this.events.push(event);
27+
this.indentation += 1;
28+
break;
29+
case 'rule.match':
30+
this.indentation -= 1;
31+
break;
32+
case 'rule.fail':
33+
// remove failed leaf
34+
lastIndex = util.findLastIndex(this.events, {rule: event.rule});
35+
lastWsIndex = util.findLastIndex(this.events, function (e) {
36+
return !that.whitespaceRule.test(e.rule);
37+
});
38+
if (that.whitespaceRule.test(event.rule) || lastIndex === lastWsIndex) {
39+
this.events.splice(lastIndex, 1);
40+
}
41+
this.indentation -= 1;
42+
break;
43+
}
44+
};
45+
46+
/**
47+
* @note
48+
* There is way too much magic/nonsense in this method now. Need to
49+
* come up with an alternative approach to getting the right
50+
* information for syntax errors.
51+
*/
52+
Tracer.prototype.smartError = function smartError(err) {
53+
var that = this, message, location, chain, chainDetail, firstNode,
54+
bestNode = {indentation: -1}, deep = false, stmts = 0,
55+
namedEvents = this.events
56+
.filter(function (e) {
57+
return e.description !== null &&
58+
!that.whitespaceRule.test(e.rule);
59+
})
60+
.reverse();
61+
62+
chain = util.takeWhile(namedEvents, function (elem) {
63+
if (/^(sym\_semi)$/i.test(elem.rule)) {
64+
stmts += 1;
65+
}
66+
if (stmts > 1) {
67+
return false;
68+
}
69+
if (!deep) {
70+
if (elem.indentation > bestNode.indentation) {
71+
bestNode = elem;
72+
} else {
73+
deep = true;
74+
}
75+
} else if (/^(stmt)$/i.test(elem.rule)) {
76+
deep = true;
77+
return true;
78+
}
79+
return true;
80+
});
81+
82+
if (chain.length) {
83+
location = bestNode.location;
84+
firstNode = util.findWhere(chain, function (elem) {
85+
return that.firstNodeRule.test(elem.description) &&
86+
elem.description !== bestNode.description &&
87+
elem.indentation !== bestNode.indentation;
88+
});
89+
if (firstNode != null) {
90+
if (this.statementRule.test(bestNode.description) &&
91+
this.statementRule.test(firstNode.description)) {
92+
chainDetail = firstNode.description;
93+
} else {
94+
chainDetail = bestNode.description + ' (' + firstNode.description + ')';
95+
}
96+
} else {
97+
chainDetail = bestNode.description;
98+
}
99+
message = 'Syntax error found near ' + chainDetail;
100+
util.extend(err, {
101+
'message': message,
102+
'location': location
103+
});
104+
}
105+
return err;
106+
};
107+
108+
return Tracer;
109+
})(parserUtils);

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "sqlite-parser",
33
"description": "JavaScript implentation of SQLite 3 query parser",
44
"author": "Code School (http://codeschool.com)",
5-
"version": "0.10.1",
5+
"version": "0.11.0",
66
"contributors": [
77
"Nick Wronski <nick@javascript.com>"
88
],
@@ -43,7 +43,7 @@
4343
"grunt-shell": "^1.1.2",
4444
"lodash": "^3.10.0",
4545
"mocha": "^2.2.5",
46-
"pegjs": "git+https://github.com/dmajda/pegjs.git#master",
46+
"pegjs": "git+https://github.com/nwronski/pegjs.git#master",
4747
"prettyjson": "^1.1.2",
4848
"promise": "^7.0.3"
4949
},

0 commit comments

Comments
 (0)