Short Answer
- In CriteriaValue.js, change
module.exports
in the first line to parser
- In index.html, swap the
<script>
tags so that CriteriaValue.js comes first
- (Optional) In script.js, output the results of your parse as a formatted JSON string, in order to see the actual values
Here's the plunker: http://plnkr.co/edit/kiBp2Na9s4PXpenCzQjx?p=preview
Long Answer
Run your original plunker and check the console logs; you'll notice 2 errors:
ReferenceError: Can't find variable: parser (script.js:3)
ReferenceError: Can't find variable: error (CriteriaValue.js:1)
The first error is due to the fact that no parser
object is ever created in global scope, by script.js or by CriteriaValue.js.
Looking at CriteriaValue.js, you can see it's actually assigning the generated parser object to a non-existent modules.export
. This is the default behavior of PEG.js, as it assumes you're going to use your parser with node.js. The reason you're seeing the error is that there is no module
object, so we can't assign to this non-existent object's export
property. Changing the assignment to parser
, which is something we can assign to (because PEG.js doesn't use strict mode), avoids this error and makes parser
available for use in script.js.
Finally, the parser needs to be created before script.js can use it; hence the reason for the <script>
swap.
For future creation of CriteriaValue.js, do it like this:
pegjs --export-var parser CriteriaValue.pegjs
This will generate the file so that the object is assigned to the variable parser
instead of module.exports
.
Where AngularJS Comes In
As @dirkk said in the comments, defining the parser as a global variable is bad practice and certainly not the AngularJS way, which is to implement your parser as a service.
The quickest (but not necessarily best) way to do that is to take your already generated CriteriaValue.js code and wrap a service around it. e.g.:
angular.module('yourApp.services', [])
.factory('Parser', function() {
// The generated code, except replace "parser = " with "return "
});
Another option is to fetch the .pegjs file & generate your parser on the client using PEG.buildParser()
:
angular.module('yourApp.services', [])
.factory('Parser', ['$http', '$q', function($http, $q) {
var deferred = $q.defer();
$http.get('path/to/CriteriaValue.pegjs')
.success(function(grammar) {
try {
deferred.resolve(PEG.buildParser(grammar));
} catch (e) {
deferred.reject(e);
}
})
.error(function(message) {
deferred.reject('Unable to load grammar: ' + message);
});
return deferred.promise;
}]);
This makes updating your grammar easier, as you won't have to rewrite your service every time, but it adds a delay in loading your app. The feasibility of this depends on how complex your grammar is & how often it actually needs to change.
Despite how you build your parser, you don't necessarily need to expose the generated parser object directly to the rest of your Angular app. Instead, you can implement a higher-level API for what your app will actually do with this parser (e.g. validate(input)
, getAST(input)
, etc...). This way, if you decide in the future to switch to a different parser (e.g. Jison), you'll have much less code to change.