Comparing version 0.8.2 to 0.8.3
@@ -138,3 +138,3 @@ API Reference | ||
markscript.transformNextBlock(function(code) { | ||
return "var g = require('ohm').grammar('" + code.replace(/\n/g, '\\n') + "');"; | ||
return "var g = require('ohm-js').grammar('" + code.replace(/\n/g, '\\n') + "');"; | ||
}); | ||
@@ -141,0 +141,0 @@ </script> |
@@ -17,3 +17,3 @@ # Ohm Documentation | ||
```js | ||
var ohm = require('ohm'); | ||
var ohm = require('ohm-js'); | ||
var g = ohm.grammar( | ||
@@ -20,0 +20,0 @@ 'Laugh {' + |
@@ -10,3 +10,3 @@ # Syntax Reference | ||
Ohm is closely related to OMeta, another PEG-based language for parsing and pattern matching. Like OMeta, Ohm supports a few features not supported by many PEG parsing frameworks: | ||
Ohm is closely related to [OMeta](http://tinlizzie.org/ometa/), another PEG-based language for parsing and pattern matching. Like OMeta, Ohm supports a few features not supported by many PEG parsing frameworks: | ||
@@ -20,3 +20,3 @@ - [Rule applications](#rule-application) can accept parameters. This makes it possible to write higher-order rules, such as the built-in `ListOf` rule. | ||
<script type="text/markscript"> | ||
var ohm = require('ohm'); | ||
var ohm = require('ohm-js'); | ||
function checkGrammar(source) { | ||
@@ -23,0 +23,0 @@ assert(ohm.grammar(source)); |
{ | ||
"name": "ohm-js", | ||
"version": "0.8.2", | ||
"version": "0.8.3", | ||
"description": "An object-oriented language for parsing and pattern matching", | ||
@@ -30,7 +30,7 @@ "repository": "https://github.com/cdglabs/ohm", | ||
"prebuild-debug": "bin/update-env.sh", | ||
"build-debug": "browserify $npm_package_browserify_options", | ||
"build-debug": "browserifyinc --cachefile dist/browserify-cache.json $npm_package_browserify_options", | ||
"clean": "rm -f dist/ohm.js dist/ohm.min.js", | ||
"deploy-gh-pages": "bin/deploy-gh-pages.sh", | ||
"lint": "eslint --rulesdir eslint_rules . && jscs --preset=google .", | ||
"pretest": "bin/update-env.sh && npm run build", | ||
"pretest": "bin/update-env.sh && npm run build-debug", | ||
"test": "tape test/*.js | tap-spec", | ||
@@ -58,4 +58,5 @@ "test-watch": "watchy -w README.md,example,doc,src,test -- npm test", | ||
"devDependencies": { | ||
"browser-sync": "^2.4.0", | ||
"browserify": "^3.30.4", | ||
"browser-sync": "^2.8.0", | ||
"browserify": "^11.0.0", | ||
"browserify-incremental": "^3.0.1", | ||
"eslint": "^0.17.1", | ||
@@ -70,3 +71,3 @@ "eslint-plugin-no-extension-in-require": "^0.2.0", | ||
"uglify-js": "^2.4.12", | ||
"watchify": "^0.6.1", | ||
"watchify": "^3.3.0", | ||
"watchy": "^0.6.1" | ||
@@ -73,0 +74,0 @@ }, |
@@ -19,4 +19,7 @@ Ohm | ||
host language. This separation improves modularity, and makes both grammars and semantic actions | ||
easier to read and understand. _(More on the Ohm philosophy [here](doc/philosophy.md).)_ | ||
easier to read and understand. Currently, JavaScript is the only host language, but as the API | ||
stabilizes, we hope to have implementations for other languages. | ||
Learn more on the Ohm philosophy [here](doc/philosophy.md). | ||
Getting Started | ||
@@ -53,2 +56,8 @@ --------------- | ||
This will install Ohm in the local node_modules folder. Use `require` to access it from a Node script: | ||
```js | ||
var ohm = require('ohm-js'); | ||
``` | ||
### Basics | ||
@@ -58,2 +67,4 @@ | ||
![Instantiating a grammar](http://www.cdglabs.org/ohm/doc/images/instantiating-grammars.png) | ||
To use Ohm, you need a grammar that is written in the Ohm language. The grammar provides a formal | ||
@@ -102,10 +113,12 @@ definition of the language or data format that you want to parse. There are a few different ways | ||
![Matching input](http://www.cdglabs.org/ohm/doc/images/matching.png) | ||
<script type="text/markscript"> | ||
// The duplication here is required because Markscript only executes top-level code blocks. | ||
// TODO: Consider fixing this in Markscript. | ||
var ohm = require('ohm'); | ||
var ohm = require('ohm-js'); | ||
var myGrammar = ohm.grammar('MyGrammar { greeting = "Hello" | "Hola" }'); | ||
</script> | ||
Once you've instantiated a grammar object, use the grammar's `match()` method to parse input: | ||
Once you've instantiated a grammar object, use the grammar's `match()` method to recognize input: | ||
@@ -122,2 +135,4 @@ ```js | ||
The result is a MatchResult object. You can use the `succeeded()` and `failed()` methods to see whether the input was recognized or not. | ||
For more information, see the [main documentation](doc/index.md). | ||
@@ -130,5 +145,5 @@ | ||
![Ohm Visualizer](http://www.cdglabs.org/ohm/doc/images/visualizer-small.png) | ||
[![Ohm Visualizer](http://www.cdglabs.org/ohm/doc/images/visualizer-small.png)](http://www.cdglabs.org/ohm/visualizer/) | ||
To run the visualizer, just open `visualizer/index.html` in your web browser. | ||
You can [try the visualizer online](http://www.cdglabs.org/ohm/visualizer/), or if you have an Ohm checkout, open `visualizer/index.html` in your web browser. | ||
@@ -135,0 +150,0 @@ To see the text trace for a grammar `g`, just use the [`g.trace()`](./doc/api-reference.md#trace) |
@@ -13,22 +13,12 @@ 'use strict'; | ||
function OhmError() {} | ||
OhmError.prototype = Object.create(Error.prototype); | ||
function makeCustomError(name, initFn) { | ||
// Make E think it's really called OhmError, so that errors look nicer when they're | ||
// console.log'ed in Chrome. | ||
var E = function OhmError() { | ||
initFn.apply(this, arguments); | ||
// `captureStackTrace` is V8-only. | ||
if (typeof Error.captureStackTrace === 'function') { | ||
Error.captureStackTrace(this, this.constructor); | ||
} else { | ||
var e = new Error(); | ||
Object.defineProperty(this, 'stack', {get: function() { return e.stack; }}); | ||
} | ||
}; | ||
E.prototype = Object.create(OhmError.prototype); | ||
E.prototype.constructor = E; | ||
E.prototype.name = name; | ||
return E; | ||
function createError(message, optInterval) { | ||
var e; | ||
if (optInterval) { | ||
e = new Error(optInterval.getLineAndColumnMessage() + message); | ||
e.shortMessage = message; | ||
e.interval = optInterval; | ||
} else { | ||
e = new Error(message); | ||
} | ||
return e; | ||
} | ||
@@ -38,8 +28,5 @@ | ||
var IntervalSourcesDontMatch = makeCustomError( | ||
'ohm.error.IntervalSourcesDontMatch', | ||
function() { | ||
this.message = "Interval sources don't match"; | ||
} | ||
); | ||
function intervalSourcesDontMatch() { | ||
return createError("Interval sources don't match"); | ||
} | ||
@@ -50,48 +37,26 @@ // ----------------- errors about grammars ----------------- | ||
var GrammarSyntaxError = makeCustomError( | ||
'ohm.error.GrammarSyntaxError', | ||
function(matchFailure) { | ||
Object.defineProperty(this, 'message', { | ||
get: function() { | ||
return 'Failed to parse grammar:\n' + matchFailure.message; | ||
} | ||
}); | ||
Object.defineProperty(this, 'shortMessage', { | ||
get: function() { | ||
return 'Expected ' + matchFailure.getExpectedText(); | ||
} | ||
}); | ||
this.interval = matchFailure.getInterval(); | ||
} | ||
); | ||
function grammarSyntaxError(matchFailure) { | ||
var e = new Error(); | ||
Object.defineProperty(e, 'message', {get: function() { return matchFailure.message; }}); | ||
Object.defineProperty(e, 'shortMessage', {get: function() { | ||
return 'Expected ' + matchFailure.getExpectedText(); | ||
}}); | ||
e.interval = matchFailure.getInterval(); | ||
return e; | ||
} | ||
// Undeclared grammar | ||
var UndeclaredGrammar = makeCustomError( | ||
'ohm.error.UndeclaredGrammar', | ||
function(grammarName, namespace, interval) { | ||
this.grammarName = grammarName; | ||
this.namespace = namespace; | ||
if (this.namespace) { | ||
this.message = 'Grammar ' + this.grammarName + | ||
' is not declared in namespace ' + Namespace.toString(this.namespace); | ||
} else { | ||
this.message = 'Undeclared grammar ' + this.grammarName; | ||
} | ||
this.interval = interval; | ||
} | ||
); | ||
function undeclaredGrammar(grammarName, namespace, interval) { | ||
var message = namespace ? | ||
'Grammar ' + grammarName + ' is not declared in namespace ' + Namespace.toString(namespace) : | ||
'Undeclared grammar ' + grammarName; | ||
return createError(message, interval); | ||
} | ||
// Duplicate grammar declaration | ||
var DuplicateGrammarDeclaration = makeCustomError( | ||
'ohm.error.DuplicateGrammarDeclaration', | ||
function(grammar, namespace) { | ||
this.grammarName = grammar.name; | ||
this.namespace = namespace; | ||
this.message = 'Grammar ' + this.grammarName + | ||
' is already declared in namespace ' + Namespace.toString(this.namespace); | ||
this.interval = grammar.definitionInterval; | ||
} | ||
); | ||
function duplicateGrammarDeclaration(grammar, namespace) { | ||
return createError('Grammar ' + grammar.name + ' is already declared in this namespace'); | ||
} | ||
@@ -102,179 +67,110 @@ // ----------------- rules ----------------- | ||
var UndeclaredRule = makeCustomError( | ||
'ohm.error.UndeclaredRule', | ||
function(ruleName, grammarName, expr) { | ||
this.ruleName = ruleName; | ||
this.grammarName = grammarName; | ||
this.message = 'Rule ' + this.ruleName + ' is not declared in grammar ' + this.grammarName; | ||
this.interval = expr.interval; | ||
} | ||
); | ||
function undeclaredRule(ruleName, grammarName, expr) { | ||
return createError( | ||
'Rule ' + ruleName + ' is not declared in grammar ' + grammarName, | ||
expr.interval); | ||
} | ||
// Cannot override undeclared rule | ||
var CannotOverrideUndeclaredRule = makeCustomError( | ||
'ohm.error.CannotOverrideUndeclaredRule', | ||
function(ruleName, grammarName, body) { | ||
this.ruleName = ruleName; | ||
this.grammarName = grammarName; | ||
this.message = | ||
'Cannot override rule ' + this.ruleName + | ||
' because it is not declared in ' + this.grammarName; | ||
this.interval = body.definitionInterval; | ||
} | ||
); | ||
function cannotOverrideUndeclaredRule(ruleName, grammarName, body) { | ||
return createError( | ||
'Cannot override rule ' + ruleName + ' because it is not declared in ' + grammarName, | ||
body.definitionInterval); | ||
} | ||
// Cannot extend undeclared rule | ||
var CannotExtendUndeclaredRule = makeCustomError( | ||
'ohm.error.CannotExtendUndeclaredRule', | ||
function(ruleName, grammarName, body) { | ||
this.ruleName = ruleName; | ||
this.grammarName = grammarName; | ||
this.message = | ||
'Cannot extend rule ' + this.ruleName + | ||
' because it is not declared in ' + this.grammarName; | ||
this.interval = body.definitionInterval; | ||
} | ||
); | ||
function cannotExtendUndeclaredRule(ruleName, grammarName, body) { | ||
return createError( | ||
'Cannot extend rule ' + ruleName + ' because it is not declared in ' + grammarName, | ||
body.definitionInterval); | ||
} | ||
// Duplicate rule declaration | ||
var DuplicateRuleDeclaration = makeCustomError( | ||
'ohm.error.DuplicateRuleDeclaration', | ||
function(ruleName, offendingGrammarName, declGrammarName, body) { | ||
this.ruleName = ruleName; | ||
this.offendingGrammarName = offendingGrammarName; | ||
this.declGrammarName = declGrammarName; | ||
this.message = "Duplicate declaration for rule '" + this.ruleName + | ||
"' in grammar '" + this.offendingGrammarName + "'"; | ||
if (this.offendingGrammarName !== declGrammarName) { | ||
this.message += " (originally declared in grammar '" + this.declGrammarName + "')"; | ||
} | ||
this.interval = body.definitionInterval; | ||
} | ||
); | ||
function duplicateRuleDeclaration(ruleName, offendingGrammarName, declGrammarName, body) { | ||
var message = "Duplicate declaration for rule '" + ruleName + | ||
"' in grammar '" + offendingGrammarName + "'"; | ||
if (offendingGrammarName !== declGrammarName) { | ||
message += " (originally declared in '" + declGrammarName + "')"; | ||
} | ||
return createError(message, body.definitionInterval); | ||
} | ||
// Wrong number of parameters | ||
var WrongNumberOfParameters = makeCustomError( | ||
'ohm.error.WrongNumberOfParameters', | ||
function(ruleName, expected, actual, body) { | ||
this.ruleName = ruleName; | ||
this.expected = expected; | ||
this.actual = actual; | ||
this.message = 'Wrong number of parameters for rule ' + this.ruleName + | ||
' (expected ' + this.expected + ', got ' + this.actual + ')'; | ||
this.interval = body.definitionInterval; | ||
} | ||
); | ||
function wrongNumberOfParameters(ruleName, expected, actual, body) { | ||
return createError( | ||
'Wrong number of parameters for rule ' + ruleName + | ||
' (expected ' + expected + ', got ' + actual + ')', | ||
// FIXME: the definition interval is OK if this error is about a definition, but not a call. | ||
// Should probably split this up into two errors. | ||
body.definitionInterval); | ||
} | ||
// Duplicate parameter names | ||
var DuplicateParameterNames = makeCustomError( | ||
'ohm.error.DuplicateParameterNames', | ||
function(ruleName, duplicates, body) { | ||
this.ruleName = ruleName; | ||
this.duplicates = duplicates; | ||
this.message = 'Duplicate parameter names in rule ' + this.ruleName + ': ' + | ||
this.duplicates.join(','); | ||
this.interval = body.definitionInterval; | ||
} | ||
); | ||
function duplicateParameterNames(ruleName, duplicates, body) { | ||
return createError( | ||
'Duplicate parameter names in rule ' + ruleName + ': ' + duplicates.join(','), | ||
body.definitionInterval); | ||
} | ||
// Invalid parameter expression | ||
var InvalidParameter = makeCustomError( | ||
'ohm.error.InvalidParameter', | ||
function(ruleName, expr) { | ||
this.ruleName = ruleName; | ||
this.expr = expr; | ||
this.interval = expr.interval; | ||
this.message = 'Invalid parameter to rule ' + this.ruleName + ': ' + this.expr + | ||
' has arity ' + this.expr.getArity() + ', but parameter expressions ' + | ||
'must have arity 1'; | ||
} | ||
); | ||
function invalidParameter(ruleName, expr) { | ||
return createError( | ||
'Invalid parameter to rule ' + ruleName + ': ' + expr + ' has arity ' + expr.getArity() + | ||
', but parameter expressions ' + 'must have arity 1', | ||
expr.interval); | ||
} | ||
// Application of syntactic rule from lexical rule | ||
var ApplicationOfSyntacticRuleFromLexicalContext = makeCustomError( | ||
'ohm.error.ApplicationOfSyntacticRuleFromLexicalContext', | ||
function(ruleName, applyExpr) { | ||
this.ruleName = ruleName; | ||
this.applyExpr = applyExpr; | ||
this.interval = applyExpr.interval; | ||
this.shortMessage = | ||
'Cannot apply syntactic rule ' + ruleName + ' from here (inside a lexical context)'; | ||
this.message = applyExpr.interval.getLineAndColumnMessage() + this.shortMessage; | ||
} | ||
); | ||
function applicationOfSyntacticRuleFromLexicalContext(ruleName, applyExpr) { | ||
return createError( | ||
'Cannot apply syntactic rule ' + ruleName + ' from here (inside a lexical context)', | ||
applyExpr.interval); | ||
} | ||
// ----------------- Kleene operators ----------------- | ||
var KleeneExprHasNullableOperand = makeCustomError( | ||
'ohm.error.KleeneExprHasNullableOperand', | ||
function(kleeneExpr) { | ||
this.expr = kleeneExpr; | ||
function kleeneExprHasNullableOperand(kleeneExpr) { | ||
return createError( | ||
'Nullable expression ' + kleeneExpr.expr.interval.contents + " is not allowed inside '" + | ||
kleeneExpr.operator + "' (possible infinite loop)", | ||
kleeneExpr.expr.interval); | ||
} | ||
var operator = kleeneExpr.operator; | ||
var nullableExpr = kleeneExpr.expr; | ||
this.shortMessage = 'Nullable expression ' + nullableExpr.interval.contents + | ||
" is not allowed inside '" + operator + "' (possible infinite loop)"; | ||
this.message = nullableExpr.interval.getLineAndColumnMessage() + this.shortMessage; | ||
this.interval = nullableExpr.interval; | ||
} | ||
); | ||
// ----------------- arity ----------------- | ||
var InconsistentArity = makeCustomError( | ||
'ohm.error.InconsistentArity', | ||
function(ruleName, expected, actual, expr) { | ||
this.ruleName = ruleName; | ||
this.expected = expected; | ||
this.actual = actual; | ||
this.message = | ||
'Rule ' + this.ruleName + ' involves an alternation which has inconsistent arity ' + | ||
'(expected ' + this.expected + ', got ' + this.actual + ')'; | ||
this.interval = expr.interval; | ||
} | ||
); | ||
function inconsistentArity(ruleName, expected, actual, expr) { | ||
return createError( | ||
'Rule ' + ruleName + ' involves an alternation which has inconsistent arity ' + | ||
'(expected ' + expected + ', got ' + actual + ')', | ||
expr.interval); | ||
} | ||
// ----------------- properties ----------------- | ||
var DuplicatePropertyNames = makeCustomError( | ||
'ohm.error.DuplicatePropertyNames', | ||
function(duplicates) { | ||
this.duplicates = duplicates; | ||
this.message = 'Object pattern has duplicate property names: ' + this.duplicates.join(', '); | ||
} | ||
); | ||
function duplicatePropertyNames(duplicates) { | ||
return createError('Object pattern has duplicate property names: ' + duplicates.join(', ')); | ||
} | ||
// ----------------- constructors ----------------- | ||
var InvalidConstructorCall = makeCustomError( | ||
'ohm.error.InvalidConstructorCall', | ||
function(grammar, ctorName, children) { | ||
this.grammar = grammar; | ||
this.ctorName = ctorName; | ||
this.children = children; | ||
this.message = 'Attempt to invoke constructor ' + this.ctorName + | ||
' with invalid or unexpected arguments'; | ||
} | ||
); | ||
function invalidConstructorCall(grammar, ctorName, children) { | ||
return createError( | ||
'Attempt to invoke constructor ' + ctorName + ' with invalid or unexpected arguments'); | ||
} | ||
// ----------------- convenience ----------------- | ||
var MultipleErrors = makeCustomError( | ||
'ohm.error.MultipleErrors', | ||
function(errors) { | ||
this.errors = errors; | ||
var messages = errors.map(function(e) { return e.message; }); | ||
this.message = ['Errors:'].concat(messages).join('\n- '); | ||
// Let's use the first error. | ||
this.shortMessage = errors[0].shortMessage ? errors[0].shortMessage : errors[0].message; | ||
this.interval = errors[0].interval; | ||
} | ||
); | ||
function multipleErrors(errors) { | ||
var messages = errors.map(function(e) { return e.message; }); | ||
return createError( | ||
['Errors:'].concat(messages).join('\n- '), | ||
errors[0].interval); | ||
} | ||
@@ -286,20 +182,18 @@ // -------------------------------------------------------------------- | ||
module.exports = { | ||
ApplicationOfSyntacticRuleFromLexicalContext: ApplicationOfSyntacticRuleFromLexicalContext, | ||
CannotExtendUndeclaredRule: CannotExtendUndeclaredRule, | ||
CannotOverrideUndeclaredRule: CannotOverrideUndeclaredRule, | ||
DuplicateGrammarDeclaration: DuplicateGrammarDeclaration, | ||
DuplicateParameterNames: DuplicateParameterNames, | ||
DuplicatePropertyNames: DuplicatePropertyNames, | ||
DuplicateRuleDeclaration: DuplicateRuleDeclaration, | ||
Error: OhmError, | ||
InconsistentArity: InconsistentArity, | ||
IntervalSourcesDontMatch: IntervalSourcesDontMatch, | ||
InvalidConstructorCall: InvalidConstructorCall, | ||
InvalidParameter: InvalidParameter, | ||
GrammarSyntaxError: GrammarSyntaxError, | ||
KleeneExprHasNullableOperand: KleeneExprHasNullableOperand, | ||
MultipleErrors: MultipleErrors, | ||
UndeclaredGrammar: UndeclaredGrammar, | ||
UndeclaredRule: UndeclaredRule, | ||
WrongNumberOfParameters: WrongNumberOfParameters, | ||
applicationOfSyntacticRuleFromLexicalContext: applicationOfSyntacticRuleFromLexicalContext, | ||
cannotExtendUndeclaredRule: cannotExtendUndeclaredRule, | ||
cannotOverrideUndeclaredRule: cannotOverrideUndeclaredRule, | ||
duplicateGrammarDeclaration: duplicateGrammarDeclaration, | ||
duplicateParameterNames: duplicateParameterNames, | ||
duplicatePropertyNames: duplicatePropertyNames, | ||
duplicateRuleDeclaration: duplicateRuleDeclaration, | ||
inconsistentArity: inconsistentArity, | ||
intervalSourcesDontMatch: intervalSourcesDontMatch, | ||
invalidConstructorCall: invalidConstructorCall, | ||
invalidParameter: invalidParameter, | ||
grammarSyntaxError: grammarSyntaxError, | ||
kleeneExprHasNullableOperand: kleeneExprHasNullableOperand, | ||
undeclaredGrammar: undeclaredGrammar, | ||
undeclaredRule: undeclaredRule, | ||
wrongNumberOfParameters: wrongNumberOfParameters, | ||
@@ -311,5 +205,5 @@ throwErrors: function(errors) { | ||
if (errors.length > 1) { | ||
throw new MultipleErrors(errors); | ||
throw multipleErrors(errors); | ||
} | ||
} | ||
}; |
@@ -39,3 +39,3 @@ 'use strict'; | ||
if (!body || !body.check(this, children) || children.length !== body.getArity()) { | ||
throw new errors.InvalidConstructorCall(this, ruleName, children); | ||
throw errors.invalidConstructorCall(this, ruleName, children); | ||
} | ||
@@ -42,0 +42,0 @@ var interval = new Interval(InputStream.newFor(children), 0, children.length); |
@@ -24,14 +24,2 @@ 'use strict'; | ||
function onOhmError(doFn, onErrorFn) { | ||
try { | ||
doFn(); | ||
} catch (e) { | ||
if (e instanceof errors.Error) { | ||
onErrorFn(e); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
} | ||
GrammarDecl.prototype.ensureSuperGrammar = function() { | ||
@@ -53,7 +41,7 @@ if (!this.superGrammar) { | ||
if (duplicateParameterNames.length > 0) { | ||
throw new errors.DuplicateParameterNames(name, duplicateParameterNames, body); | ||
throw errors.duplicateParameterNames(name, duplicateParameterNames, body); | ||
} | ||
var baseRule = this.ensureSuperGrammar().ruleDict[name]; | ||
if (formals.length !== baseRule.formals.length) { | ||
throw new errors.WrongNumberOfParameters(name, baseRule.formals.length, formals.length, body); | ||
throw errors.wrongNumberOfParameters(name, baseRule.formals.length, formals.length, body); | ||
} | ||
@@ -105,11 +93,13 @@ return this.install(name, formals, baseRule.description, body); | ||
var body = grammar.ruleDict[ruleName]; | ||
onOhmError( | ||
function() { body.assertChoicesHaveUniformArity(ruleName); }, | ||
function(e) { grammarErrors.push(e); }); | ||
onOhmError( | ||
function() { body.assertAllApplicationsAreValid(ruleName, grammar); }, | ||
function(e) { | ||
grammarErrors.push(e); | ||
grammarHasInvalidApplications = true; | ||
}); | ||
try { | ||
body.assertChoicesHaveUniformArity(ruleName); | ||
} catch (e) { | ||
grammarErrors.push(e); | ||
} | ||
try { | ||
body.assertAllApplicationsAreValid(ruleName, grammar); | ||
} catch (e) { | ||
grammarErrors.push(e); | ||
grammarHasInvalidApplications = true; | ||
} | ||
}); | ||
@@ -120,5 +110,7 @@ if (!grammarHasInvalidApplications) { | ||
var body = grammar.ruleDict[ruleName]; | ||
onOhmError( | ||
function() { body.assertIteratedExprsAreNotNullable(grammar, ruleName); }, | ||
function(e) { grammarErrors.push(e); }); | ||
try { | ||
body.assertIteratedExprsAreNotNullable(grammar, ruleName); | ||
} catch (e) { | ||
grammarErrors.push(e); | ||
} | ||
}); | ||
@@ -137,9 +129,9 @@ } | ||
if (this.superGrammar.ruleDict[name]) { | ||
throw new errors.DuplicateRuleDeclaration(name, this.name, this.superGrammar.name, body); | ||
throw errors.duplicateRuleDeclaration(name, this.name, this.superGrammar.name, body); | ||
} else if (this.ruleDict[name]) { | ||
throw new errors.DuplicateRuleDeclaration(name, this.name, this.name, body); | ||
throw errors.duplicateRuleDeclaration(name, this.name, this.name, body); | ||
} | ||
var duplicateParameterNames = common.getDuplicates(formals); | ||
if (duplicateParameterNames.length > 0) { | ||
throw new errors.DuplicateParameterNames(name, duplicateParameterNames, body); | ||
throw errors.duplicateParameterNames(name, duplicateParameterNames, body); | ||
} | ||
@@ -152,3 +144,3 @@ return this.install(name, formals, optDescr, body); | ||
if (!baseRule) { | ||
throw new errors.CannotOverrideUndeclaredRule(name, this.superGrammar.name, body); | ||
throw errors.cannotOverrideUndeclaredRule(name, this.superGrammar.name, body); | ||
} | ||
@@ -162,3 +154,3 @@ this.installOverriddenOrExtendedRule(name, formals, body); | ||
if (!baseRule) { | ||
throw new errors.CannotExtendUndeclaredRule(name, this.superGrammar.name, body); | ||
throw errors.cannotExtendUndeclaredRule(name, this.superGrammar.name, body); | ||
} | ||
@@ -165,0 +157,0 @@ this.installOverriddenOrExtendedRule( |
@@ -27,3 +27,3 @@ 'use strict'; | ||
if (interval.inputStream !== inputStream) { | ||
throw new errors.IntervalSourcesDontMatch(); | ||
throw errors.intervalSourcesDontMatch(); | ||
} else { | ||
@@ -30,0 +30,0 @@ startIdx = Math.min(startIdx, arguments[idx].startIdx); |
@@ -87,3 +87,3 @@ /* global document, XMLHttpRequest */ | ||
if (grammarName in namespace) { | ||
throw new errors.DuplicateGrammarDeclaration(g, namespace); | ||
throw errors.duplicateGrammarDeclaration(g, namespace); | ||
} | ||
@@ -100,3 +100,3 @@ namespace[grammarName] = g; | ||
if (!namespace || !(superGrammarName in namespace)) { | ||
throw new errors.UndeclaredGrammar(superGrammarName, namespace, n.interval); | ||
throw errors.undeclaredGrammar(superGrammarName, namespace, n.interval); | ||
} | ||
@@ -284,3 +284,3 @@ decl.withSuperGrammar(namespace[superGrammarName]); | ||
if (m.failed()) { | ||
throw new errors.GrammarSyntaxError(m); | ||
throw errors.grammarSyntaxError(m); | ||
} | ||
@@ -379,3 +379,2 @@ return buildGrammar(m, namespace); | ||
createNamespace: Namespace.createNamespace, | ||
error: errors, | ||
grammar: grammar, | ||
@@ -382,0 +381,0 @@ grammars: grammars, |
@@ -70,3 +70,3 @@ 'use strict'; | ||
if (!body) { | ||
throw new errors.UndeclaredRule(this.ruleName, grammar.name, this); | ||
throw errors.undeclaredRule(this.ruleName, grammar.name, this); | ||
} | ||
@@ -76,3 +76,3 @@ | ||
if (common.isSyntactic(this.ruleName) && (!common.isSyntactic(ruleName) || lexifyCount > 0)) { | ||
throw new errors.ApplicationOfSyntacticRuleFromLexicalContext(this.ruleName, this); | ||
throw errors.applicationOfSyntacticRuleFromLexicalContext(this.ruleName, this); | ||
} | ||
@@ -84,3 +84,3 @@ | ||
if (actual !== expected) { | ||
throw new errors.WrongNumberOfParameters(this.ruleName, expected, actual, this); | ||
throw errors.wrongNumberOfParameters(this.ruleName, expected, actual, this); | ||
} | ||
@@ -93,5 +93,5 @@ | ||
if (param.getArity() !== 1) { | ||
throw new errors.InvalidParameter(self.ruleName, param); | ||
throw errors.invalidParameter(self.ruleName, param); | ||
} | ||
}); | ||
}; |
@@ -37,3 +37,3 @@ 'use strict'; | ||
if (arity !== otherArity) { | ||
throw new errors.InconsistentArity(ruleName, arity, otherArity, this); | ||
throw errors.inconsistentArity(ruleName, arity, otherArity, this); | ||
} | ||
@@ -49,3 +49,3 @@ } | ||
if (actualArity !== expectedArity) { | ||
throw new errors.InconsistentArity(ruleName, expectedArity, actualArity, this); | ||
throw errors.inconsistentArity(ruleName, expectedArity, actualArity, this); | ||
} | ||
@@ -52,0 +52,0 @@ }; |
@@ -44,3 +44,3 @@ 'use strict'; | ||
if (this.expr.isNullable(grammar)) { | ||
throw new errors.KleeneExprHasNullableOperand(this, ruleName); | ||
throw errors.kleeneExprHasNullableOperand(this, ruleName); | ||
} | ||
@@ -47,0 +47,0 @@ }; |
@@ -207,5 +207,5 @@ 'use strict'; | ||
var origNumBindings = state.bindings.length; | ||
state.ignoreFailures(); | ||
state.doNotRecordFailures(); | ||
var ans = this.expr.eval(state); | ||
state.recordFailures(); | ||
state.doRecordFailures(); | ||
if (ans) { | ||
@@ -306,5 +306,2 @@ state.recordFailure(origPos, this); | ||
inputStream.pos = memoRecOrLR.pos; | ||
if (memoRecOrLR.failureDescriptor) { | ||
state.recordFailures(memoRecOrLR.failureDescriptor, application); | ||
} | ||
if (state.isTracing()) { | ||
@@ -356,3 +353,3 @@ state.trace.push(memoRecOrLR.traceEntry); | ||
if (body.description) { | ||
state.ignoreFailures(); | ||
state.doNotRecordFailures(); | ||
} | ||
@@ -378,3 +375,3 @@ var value = app.evalOnce(body, state); | ||
if (body.description) { | ||
state.recordFailures(); | ||
state.doRecordFailures(); | ||
if (!value) { | ||
@@ -381,0 +378,0 @@ state.recordFailure(origPos, app); |
@@ -172,3 +172,3 @@ 'use strict'; | ||
if (duplicates.length > 0) { | ||
throw new errors.DuplicatePropertyNames(duplicates); | ||
throw errors.duplicatePropertyNames(duplicates); | ||
} else { | ||
@@ -175,0 +175,0 @@ this.properties = properties; |
@@ -35,3 +35,3 @@ 'use strict'; | ||
this.failures = optFailuresArray; | ||
this.ignoreFailuresCount = 0; | ||
this.doNotRecordFailuresCount = 0; // should not record failures if this is > 0 | ||
if (this.isTracing()) { | ||
@@ -84,6 +84,6 @@ this.trace = []; | ||
skipSpaces: function() { | ||
this.ignoreFailures(); | ||
this.doNotRecordFailures(); | ||
applySpaces_.eval(this); | ||
this.bindings.pop(); | ||
this.recordFailures(); | ||
this.doRecordFailures(); | ||
return this.inputStream.pos; | ||
@@ -127,3 +127,3 @@ }, | ||
recordFailure: function(pos, expr) { | ||
if (this.ignoreFailuresCount > 0) { | ||
if (this.doNotRecordFailuresCount > 0) { | ||
return; | ||
@@ -177,8 +177,8 @@ } | ||
ignoreFailures: function() { | ||
this.ignoreFailuresCount++; | ||
doNotRecordFailures: function() { | ||
this.doNotRecordFailuresCount++; | ||
}, | ||
recordFailures: function() { | ||
this.ignoreFailuresCount--; | ||
doRecordFailures: function() { | ||
this.doNotRecordFailuresCount--; | ||
}, | ||
@@ -185,0 +185,0 @@ |
var test = require('tape-catch'); | ||
var errors = require('../src/errors'); | ||
var fs = require('fs'); | ||
@@ -84,3 +83,5 @@ var ohm = require('..'); | ||
it('_nonterminal entry rejects nonexistent rule name', function() { | ||
t.throws(function() { m.construct('foobar', []); }, errors.InvalidConstructorCall); | ||
t.throws( | ||
function() { m.construct('foobar', []); }, | ||
/Attempt to invoke constructor foobar with invalid or unexpected arguments/); | ||
}); | ||
@@ -199,5 +200,5 @@ | ||
var interval2 = makeInterval('xyz', 1, 2); | ||
t.throws(function() { | ||
Interval.coverage(interval1, interval2); | ||
}, errors.IntervalSourcesDontMatch); | ||
t.throws( | ||
function() { Interval.coverage(interval1, interval2) }, | ||
/Interval sources don't match/); | ||
}); | ||
@@ -410,3 +411,3 @@ | ||
it('unrecognized escape characters are parse errors', function() { | ||
t.throws(function() { ohm.grammar('G { r = "\\w" }'); }, /Failed to parse grammar/); | ||
t.throws(function() { ohm.grammar('G { r = "\\w" }'); }, /Expected an escape sequence/); | ||
}); | ||
@@ -835,9 +836,5 @@ | ||
it('duplicate property names are not allowed', function() { | ||
try { | ||
m = ohm.grammar('M { duh = {x: 1, x: 2, y: 3, ...} }'); | ||
t.fail('Expected an exception to be thrown'); | ||
} catch (e) { | ||
t.ok(e instanceof errors.DuplicatePropertyNames); | ||
t.deepEqual(e.duplicates, ['x']); | ||
} | ||
t.throws( | ||
function() { ohm.grammar('M { duh = {x: 1, x: 2, y: 3, ...} }'); }, | ||
/Object pattern has duplicate property names: x/); | ||
}); | ||
@@ -1168,19 +1165,11 @@ t.end(); | ||
it('no namespace', function() { | ||
try { | ||
ohm.grammar('G2 <: G1 {}'); | ||
t.fail('Expected an exception to be thrown'); | ||
} catch (e) { | ||
t.equal(e.constructor, errors.UndeclaredGrammar); | ||
t.equal(e.grammarName, 'G1'); | ||
}; | ||
t.throws( | ||
function() { ohm.grammar('G2 <: G1 {}'); }, | ||
/Grammar G1 is not declared/); | ||
}); | ||
it('empty namespace', function() { | ||
try { | ||
ohm.grammar('G2 <: G1 {}', {}); | ||
t.fail('Expected an exception to be thrown'); | ||
} catch (e) { | ||
t.equal(e.constructor, errors.UndeclaredGrammar); | ||
t.equal(e.grammarName, 'G1'); | ||
}; | ||
t.throws( | ||
function() { ohm.grammar('G2 <: G1 {}', {}); }, | ||
/Grammar G1 is not declared in namespace/); | ||
}); | ||
@@ -1192,15 +1181,10 @@ t.end(); | ||
it('should check that rule does not already exist in super-grammar', function() { | ||
var ns; | ||
try { | ||
ns = makeGrammars([ | ||
'G1 { foo = "foo" }', | ||
'G2 <: G1 { foo = "bar" }' | ||
]); | ||
t.fail('Expected an exception to be thrown'); | ||
} catch (e) { | ||
t.equal(e.constructor, errors.DuplicateRuleDeclaration); | ||
t.equal(e.ruleName, 'foo'); | ||
t.equal(e.offendingGrammarName, 'G2'); | ||
t.equal(e.declGrammarName, 'G1'); | ||
}; | ||
t.throws( | ||
function() { | ||
makeGrammars([ | ||
'G1 { foo = "foo" }', | ||
'G2 <: G1 { foo = "bar" }' | ||
]); | ||
}, | ||
/Duplicate declaration for rule 'foo' in grammar 'G2' \(originally declared in 'G1'\)/); | ||
}); | ||
@@ -1215,10 +1199,5 @@ t.end(); | ||
it('should check that rule exists in super-grammar', function() { | ||
try { | ||
ns.G3 = ohm.grammar('G3 <: G1 { foo := "foo" }', ns); | ||
t.fail('Expected an exception to be thrown'); | ||
} catch (e) { | ||
t.equal(e.constructor, errors.CannotOverrideUndeclaredRule); | ||
t.equal(e.ruleName, 'foo'); | ||
t.equal(e.grammarName, 'G1'); | ||
}; | ||
t.throws( | ||
function() { ohm.grammar('G3 <: G1 { foo := "foo" }', ns); }, | ||
/Cannot override rule foo because it is not declared in G1/); | ||
}); | ||
@@ -1291,10 +1270,5 @@ | ||
it('should check that rule exists in super-grammar', function() { | ||
try { | ||
ohm.grammar('G3 <: G1 { bar += "bar" }', ns); | ||
t.fail('Expected an exception to be thrown'); | ||
} catch (e) { | ||
t.equal(e.constructor, errors.CannotExtendUndeclaredRule); | ||
t.equal(e.ruleName, 'bar'); | ||
t.equal(e.grammarName, 'G1'); | ||
} | ||
t.throws( | ||
function() { ohm.grammar('G3 <: G1 { bar += "bar" }', ns); }, | ||
/Cannot extend rule bar because it is not declared in G1/); | ||
}); | ||
@@ -1309,23 +1283,12 @@ | ||
ns.M1 = ohm.grammar('M1 { foo = "foo" bar = "bar" baz = "baz" }'); | ||
try { | ||
ohm.grammar('M2 <: M1 { foo += bar baz }', ns); | ||
t.fail('Expected an exception to be thrown'); | ||
} catch (e) { | ||
t.equal(e.constructor, errors.InconsistentArity); | ||
t.equal(e.ruleName, 'foo'); | ||
t.equal(e.expected, 1); | ||
t.equal(e.actual, 2); | ||
} | ||
t.throws( | ||
function() { ohm.grammar('M2 <: M1 { foo += bar baz }', ns); }, | ||
/Rule foo involves an alternation which has inconsistent arity \(expected 1, got 2\)/); | ||
// Too few: | ||
ns.M3 = ohm.grammar('M3 { foo = digit digit }'); | ||
try { | ||
ohm.grammar('M4 <: M3 { foo += digit }', ns); | ||
t.fail('Expected an exception to be thrown'); | ||
} catch (e) { | ||
t.equal(e.constructor, errors.InconsistentArity); | ||
t.equal(e.ruleName, 'foo'); | ||
t.equal(e.expected, 2); | ||
t.equal(e.actual, 1); | ||
} | ||
t.throws( | ||
function() { ohm.grammar('M4 <: M3 { foo += digit }', ns); }, | ||
/Rule foo involves an alternation which has inconsistent arity \(expected 2, got 1\)/); | ||
}); | ||
@@ -1344,11 +1307,5 @@ | ||
it('inconsistent arity in alts is an error', function() { | ||
try { | ||
ohm.grammar('G { foo = "a" "c" | "b" }'); | ||
t.fail('Expected an exception to be thrown'); | ||
} catch (e) { | ||
t.equal(e.constructor, errors.InconsistentArity); | ||
t.equal(e.ruleName, 'foo'); | ||
t.deepEqual(e.expected, 2); | ||
t.deepEqual(e.actual, 1); | ||
} | ||
t.throws( | ||
function() { ohm.grammar('G { foo = "a" "c" | "b" }'); }, | ||
/Rule foo involves an alternation which has inconsistent arity \(expected 2, got 1\)/); | ||
}); | ||
@@ -1457,11 +1414,6 @@ | ||
try { | ||
ohm.grammar('Bad <: Arithmetic { addExp += addExp "~" mulExp -- minus }', ns); | ||
t.fail('Expected an exception to be thrown'); | ||
} catch (e) { | ||
t.ok(e instanceof errors.DuplicateRuleDeclaration); | ||
t.equal(e.ruleName, 'addExp_minus'); | ||
t.equal(e.offendingGrammarName, 'Bad'); | ||
t.equal(e.declGrammarName, 'Arithmetic'); | ||
}; | ||
t.throws( | ||
function() { ohm.grammar('Bad <: Arithmetic { addExp += addExp "~" mulExp -- minus }', ns); }, | ||
/rule 'addExp_minus' in grammar 'Bad' \(originally declared in 'Arithmetic'\)/); | ||
t.end(); | ||
@@ -1666,9 +1618,5 @@ }); | ||
t.ok(ns2); | ||
try { | ||
ohm.grammar('ccc { bar = "bar" }', ns2); | ||
t.fail('throws exception on duplicate grammar'); | ||
} catch (e) { | ||
t.equal(e.constructor, errors.DuplicateGrammarDeclaration); | ||
t.equal(e.grammarName, 'ccc'); | ||
} | ||
t.throws( | ||
function() { ohm.grammar('ccc { bar = "bar" }', ns2); }, | ||
/Grammar ccc is already declared in this namespace/); | ||
t.ok(ns2.G, 'ns2 delegates to ns1'); | ||
@@ -1675,0 +1623,0 @@ |
@@ -33,4 +33,4 @@ /* eslint-env node*/ | ||
// Make `require('ohm')` work properly from inside Markdown. | ||
moduleAliases: {ohm: '..'}, | ||
// Make `require('ohm-js')` work properly from inside Markdown. | ||
moduleAliases: {'ohm-js': '..'}, | ||
workingDir: scriptRel('data') | ||
@@ -37,0 +37,0 @@ }; |
@@ -10,3 +10,2 @@ 'use strict'; | ||
var ohm = require('..'); | ||
var errors = require('../src/errors'); | ||
@@ -68,11 +67,6 @@ // -------------------------------------------------------------------- | ||
test('undeclared rules', function(t) { | ||
t.throws(function() { makeRuleWithBody('undeclaredRule'); }, errors.UndeclaredRule); | ||
t.throws( | ||
function() { makeRuleWithBody('undeclaredRule'); }, | ||
/Rule undeclaredRule is not declared in grammar G/); | ||
try { | ||
makeRuleWithBody('undeclaredRule'); | ||
t.fail('Expected an exception to be thrown'); | ||
} catch (e) { | ||
t.equal(e.message, 'Rule undeclaredRule is not declared in grammar G'); | ||
} | ||
t.end(); | ||
@@ -82,6 +76,14 @@ }); | ||
test('many expressions with nullable operands', function(t) { | ||
t.throws(function() { makeRuleWithBody('("a"*)*'); }, errors.ManyExprHasNullableOperand); | ||
t.throws(function() { makeRuleWithBody('("a"?)*'); }, errors.ManyExprHasNullableOperand); | ||
t.throws(function() { makeRuleWithBody('("a"*)+'); }, errors.ManyExprHasNullableOperand); | ||
t.throws(function() { makeRuleWithBody('("a"?)+'); }, errors.ManyExprHasNullableOperand); | ||
t.throws( | ||
function() { makeRuleWithBody('("a"*)*'); }, | ||
/Nullable expression "a"\* is not allowed inside '\*'/); | ||
t.throws( | ||
function() { makeRuleWithBody('("a"?)*'); }, | ||
/Nullable expression "a"\? is not allowed inside '\*'/); | ||
t.throws( | ||
function() { makeRuleWithBody('("a"*)+'); }, | ||
/Nullable expression "a"\* is not allowed inside '\+'/); | ||
t.throws( | ||
function() { makeRuleWithBody('("a"?)+'); }, | ||
/Nullable expression "a"\? is not allowed inside '\+'/); | ||
@@ -112,3 +114,3 @@ try { | ||
function() { ohm.grammar('G { x = y+ y = undeclaredRule }'); }, | ||
errors.UndeclaredRule, | ||
/Rule * is not declared in grammar G/, | ||
'undeclared rule prevents ManyExprHasNullableOperand check'); | ||
@@ -140,3 +142,2 @@ | ||
t.equal(e.message, [ | ||
'Failed to parse grammar:', | ||
'Line 1, col 4:', | ||
@@ -158,3 +159,2 @@ '> 1 | G {', | ||
t.equal(e.message, [ | ||
'Failed to parse grammar:', | ||
'Line 1, col 19:', | ||
@@ -161,0 +161,0 @@ '> 1 | G { start = "hello' + bes + 'world" }', |
@@ -11,3 +11,2 @@ /* eslint-env node */ | ||
var errors = require('../src/errors'); | ||
var testUtil = require('./testUtil'); | ||
@@ -25,6 +24,6 @@ | ||
function() { testUtil.makeGrammar('G2 <: G { Foo<x> := "oops!" }', ns); }, | ||
errors.WrongNumberOfParameters); | ||
/Wrong number of parameters for rule Foo \(expected 2, got 1\)/); | ||
t.throws( | ||
function() { testUtil.makeGrammar('G2 <: G { Foo<x> += "oops!" }', ns); }, | ||
errors.WrongNumberOfParameters); | ||
/Wrong number of parameters for rule Foo \(expected 2, got 1\)/); | ||
@@ -34,6 +33,6 @@ // Too many arguments | ||
function() { testUtil.makeGrammar('G2 <: G { Foo<x, y, z> := "oops!" }', ns); }, | ||
errors.WrongNumberOfParameters); | ||
/Wrong number of parameters for rule Foo \(expected 2, got 3\)/); | ||
t.throws( | ||
function() { testUtil.makeGrammar('G2 <: G { Foo<x, y, z> += "oops!" }', ns); }, | ||
errors.WrongNumberOfParameters); | ||
/Wrong number of parameters for rule Foo \(expected 2, got 3\)/); | ||
@@ -49,7 +48,7 @@ // Just right | ||
t.throws( | ||
function() { testUtil.makeGrammar('G2 <: G { Foo<x> += "oops!" }', ns); }, | ||
errors.WrongNumberOfParameters); | ||
function() { testUtil.makeGrammar('G2 <: G { Bar = Foo<"a"> }', ns); }, | ||
/Wrong number of parameters for rule Foo \(expected 2, got 1\)/); | ||
t.throws( | ||
function() { testUtil.makeGrammar('G2 <: G { Foo<x, y, z> += "oops!" }', ns); }, | ||
errors.WrongNumberOfParameters); | ||
function() { testUtil.makeGrammar('G2 <: G { Bar = Foo<"a", "b", "c"> }', ns); }, | ||
/Wrong number of parameters for rule Foo \(expected 2, got 3\)/); | ||
t.end(); | ||
@@ -67,3 +66,3 @@ }); | ||
}, | ||
errors.InvalidParameter); | ||
/Invalid parameter to rule Foo/); | ||
t.end(); | ||
@@ -80,3 +79,3 @@ }); | ||
}, | ||
errors.UndeclaredRule); | ||
/Rule asdlfk is not declared in grammar G/); | ||
t.end(); | ||
@@ -83,0 +82,0 @@ }); |
@@ -351,3 +351,11 @@ /* eslint-env browser */ | ||
function isPrimitive(expr) { | ||
return expr.constructor.name.indexOf('Prim') >= 0; | ||
switch (expr.constructor.name) { | ||
case 'Prim': | ||
case 'Range': | ||
case 'StringPrim': | ||
case 'UnicodeChar': | ||
return true; | ||
default: | ||
return false; | ||
} | ||
} | ||
@@ -354,0 +362,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
NPM Shrinkwrap
Supply chain riskPackage contains a shrinkwrap file. This may allow the package to bypass normal install procedures.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2406153
128
200
14
23296
2
17