Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Finished multi-script support.

Fixed a few minor issues with common compiled terms.
Made script parser pass script information rather then use a global.
  • Loading branch information...
commit 0bea2b4c80e42fd7000412528b0589d9e9b243cd 1 parent 1deb96b
Steven Adams authored
9 README.markdown
View
@@ -42,9 +42,18 @@ Example of Use
script = parser.ParseScriptString(scriptText);
} );
+### Multiple scripting language in one script:
+
+ parser = compiler.CreateParser( interpreter, events );
+ parser.IncludeLanguage( anotherParser );
+
+ <bnf>
+ <syntax> ::= <myscript> #otherScript | <myscript>
+
### See examples:
- [Calculator](https://github.com/navstev0/nodebnf/tree/master/examples/calculator)
+- [Multi-Script](https://github.com/navstev0/nodebnf/tree/master/examples/multi-script)
License
-------
4 examples/calculator/calc.bnf
View
@@ -1,4 +1,4 @@
-<syntax> ::= <expression>
+<syntax> ::= <expression> | <expression> <_crlf> <syntax>
<expression> ::= <number> <type> <number>
-<number> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
+<number> ::= <_digits>
<type> ::= "+" | "-" | "*" | "/"
4 examples/calculator/calc.calc
View
@@ -1 +1,3 @@
-1+1
+1+1
+2+2
+222+222
26 examples/calculator/calculator.bnf.js
View
@@ -1,26 +0,0 @@
-var languageObject = require( "../../lib/parser.js" ).LanguageObject;
-var i = new languageObject( "calculator" );
-
-var r = i.syntaxObject;
-
-i.AddRule( "expression" );
-i.AddRule( "number" );
-i.AddRule( "type" );
-i.AddRule( "nextLine" );
-i.AddRule( "posNumber" );
-i.AddRule( "negNumber" );
-i.AddRule( "digit" );
-i.AddRule( "digits" );
-i.IndexTokenIdList();
-
-i.WriteRule( "syntax", i.Or( r.expression, i.And( r.expression, r.nextLine, r.syntax ) ) );
-i.WriteRule( "expression", i.And( r.number, r.type, r.number ) );
-i.WriteRule( "number", i.Or( r.posNumber, r.negNumber ) );
-i.WriteRule( "posNumber", r.digits );
-i.WriteRule( "negNumber", i.And( "-", r.digits ) );
-i.WriteRule( "digits", i.Or( r.digit, i.And( r.digit, r.digits ) ) );
-i.WriteRule( "digit", i.Or( "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ) );
-i.WriteRule( "type", i.Or( "+", "-", "*", "/" ) );
-i.WriteRule( "nextLine", i.Or( i.And( "\r", "\n" ), "\n" ) );
-
-exports.interpreter = i;
5 examples/calculator/index.js
View
@@ -9,13 +9,14 @@ var events = { "expression":function( token ){
//Equate
var equate = -1;
eval( "equate = " + left + type + right + ";" );
- console.log( "File found syntax " + equate );
+ console.log( "File found expression: " + equate );
} };
var parser = null;
var Compiler = require('../../lib/compiler.js').Compiler;
var compiler = new Compiler();
-compiler.CompileScript( __dirname + "/calc.bnf", "doq", function( interpreter ){
+
+compiler.CompileScript( __dirname + "/calc.bnf", "calc", function( interpreter ){
var parser = compiler.CreateParser( interpreter, events );
parser.ParseScript( __dirname + "/calc.calc" );
} );
5 examples/multi-script/calc.bnf
View
@@ -0,0 +1,5 @@
+<syntax> ::= <expression> | <expression> <_crlf> <syntax>
+<expression> ::= <number> <type> <number> <func>
+<func> ::= "" | #func
+<number> ::= <_digits>
+<type> ::= "+" | "-" | "*" | "/"
2  examples/multi-script/calc.calc
View
@@ -0,0 +1,2 @@
+1+1
+2+2?pie()
2  examples/multi-script/functions.bnf
View
@@ -0,0 +1,2 @@
+<syntax> ::= "?" <function>
+<function> ::= <_text> "." <_text> "(" <_owsp> ")" | <_text> "(" <_owsp> ")"
26 examples/multi-script/index.js
View
@@ -0,0 +1,26 @@
+
+console.log( "This is a simple calculator language, but it adds in another language for function output.");
+
+var events = { "expression":function( token ){
+ var left = this.SeekNext( "number" ).text;
+ var type = this.SeekNext( "type" ).text;
+ var right = this.SeekNext( "number" ).text;
+
+ //Equate
+ var equate = -1;
+ eval( "equate = " + left + type + right + ";" );
+ console.log( "File found expression: " + equate );
+} };
+
+var parser = null;
+var Compiler = require('../../lib/compiler.js').Compiler;
+var compiler = new Compiler();
+
+compiler.CompileScript( __dirname + "/functions.bnf", "func", function( interpreter ){
+ var fparser = compiler.CreateParser( interpreter, { "function":function( token ){ console.log( token.text + " found in function." ); } } );
+ compiler.CompileScript( __dirname + "/calc.bnf", "calc", function( interpreter ){
+ var parser = compiler.CreateParser( interpreter, events );
+ parser.IncludeLanguage( fparser );
+ parser.ParseScript( __dirname + "/calc.calc" );
+ } );
+} );
4 lib/compiler.js
View
@@ -81,7 +81,7 @@ exports.Compiler = function( ){
var _standardRules = {
"_char":'i.Or( i.CharGroup( "A", "Z" ), i.CharGroup( "a", "z" ), "_" )',
"_text":'i.Or( r._char, i.And( r._char, r._text ) )',
- "_digit":'i.CharGroup( "0", "9" )',
+ "_digit":'i.Or( i.CharGroup( "0", "9" ) )', //This should work outside of a i.Or but dosn't, fix it//
"_cr":'"\\r"',
"_lf":'"\\n"',
"_crlf":'i.Or( i.And( r._cr, r._lf ), i.Or( r._cr, r._lf ) )',
@@ -127,7 +127,7 @@ exports.Compiler = function( ){
*/
function _CompileObjectScript( script, id, callback ){
var cacheScript = 'var languageObject = require( "../parser.js" ).LanguageObject;\n';
- cacheScript += 'var i = new languageObject( "bnf" );\n';
+ cacheScript += 'var i = new languageObject( "'+id+'" );\n';
cacheScript += 'var r = i.syntaxObject;\n';
//Rule Names
for( var i in script.ruleContainer ){
241 lib/parser.js
View
@@ -20,7 +20,7 @@ exports.LanguageObject = function( ){
*/
var Constructor = function( name ){
_name = name;
- this.WriteRule( "script", this.And( this.syntaxObject.syntax, { name:'endOfFile', method:function( token ){ return ( token.charPtr + token.length == this.endOfSource ); } } ) );
+ this.WriteRule( "script", this.And( this.syntaxObject.syntax, { name:'endOfFile', method:function( token, script ){ return ( token.charPtr + token.length == script.endOfSource ); } } ) );
};
/////////////////////
//////PRIVATE VARIABLES//
@@ -144,9 +144,17 @@ exports.LanguageObject = function( ){
* @param token Token - The token with bound events.
*/
this.FireToken = function( token, script ){
- for( var i = 0; i < _tokenIdList[token.id].binding.length; i++ ){
+ var binding = [];
+ if( token.i ){
+ binding = token.i.ReturnTokenAtId( token.id ).binding;
+ }
+ else{
+ binding = _tokenIdList[token.id].binding;
+ }
+
+ for( var i = 0; i < binding.length; i++ ){
token.fired = true;
- _tokenIdList[token.id].binding[i].call( script, token );
+ binding[i].call( script, token );
}
};
/**
@@ -178,6 +186,17 @@ exports.LanguageObject = function( ){
}
return firedToken;
};
+ /**
+ * Gets the name of the language and returns it.
+ * @returns string - The name of the language.
+ */
+ this.GetName = function(){
+ return _name;
+ };
+ /**
+ * Short cut for creating a "include" rule.
+ * @param [mixed][...] - More rules
+ */
this.Include = function(){
return { type:this.ruleList.include, syntax:_ArgumentArray.call( this, arguments ) };
};
@@ -208,6 +227,14 @@ exports.LanguageObject = function( ){
this.Repeat = function( ){
return { type:this.ruleList.repeat, syntax:_ArgumentArray.call( this, arguments ) };
};
+ /**
+ * Returns the token at the id, this does not fire or eat the token.
+ * @param id - The id of the token to be returned.
+ * @returns TokenSetting - The token setting object.
+ */
+ this.ReturnTokenAtId = function( id ){
+ return _tokenIdList[id];
+ };
//@TODO ABNF notations
/*
@@ -227,7 +254,7 @@ exports.LanguageObject = function( ){
while( !foundToken && ( reToken = this.GetToken( token, script ) ) != null ){
if( reToken.name == tokenToFind ){
foundToken = true;
- }
+ };
}
return reToken;
@@ -266,12 +293,13 @@ Script = function( ){
* Constructor of the object
* @param Token - Script token
*/
- function Constructor( parser, interpreter, ruleToken ){
+ function Constructor( parser, interpreter, ruleToken, scriptSettings ){
this.rootToken = ruleToken;
this.currentToken = this.rootToken;
_parser = parser;
_interpreter = interpreter;
- this.rawScript = _parser.GetRawScript();
+ this.settings = scriptSettings;
+ this.rawScript = scriptSettings.rawScript;
};
/////////////////////
//////PRIVATE VARIABLES//
@@ -304,6 +332,11 @@ Script = function( ){
* @type Token
*/
this.rootToken = null;
+ /**
+ * Settings of the script object.
+ * @type Object
+ */
+ this.settings = null;
//////////////////
//////PUBLIC METHODS//
//////////////////
@@ -317,7 +350,7 @@ Script = function( ){
}
}
else{
- _parser.GetErrorOutput( this.rootToken );
+ _parser.GetErrorOutput( this.rootToken, this.settings );
}
};
@@ -355,7 +388,7 @@ Script = function( ){
};
//CALL TO CONSTRUCTOR//
- Constructor.call( this, arguments[0], arguments[1], arguments[2] );
+ Constructor.call( this, arguments[0], arguments[1], arguments[2], arguments[3] );
//CALL TO CONSTRUCTOR//
};
@@ -380,35 +413,30 @@ Token = function(){
//////PUBLIC VARIABLES//
////////////////////
/**
- * Name of the type of token.
- * @type string
- */
- this.name = "";
- /**
- * The id of the type of token.
+ * Point in the raw source where the token starts.
* @type integer
*/
- this.id = -1;
+ this.charPtr = 0;
/**
- * Full text from the token and its children.
- * @type string
+ * What types of tokens where expected.
+ * @type Array
*/
- this.text = "";
+ this.expected = [];
/**
- * Child tokens.
- * @type Array
+ * Did the token fire? Preventing it from firing twice.
+ * @type boolean
*/
- this.tokens = [];
+ this.fired = false;
/**
- * Point in the raw source where the token starts.
+ * The id of the type of token.
* @type integer
*/
- this.charPtr = 0;
+ this.id = -1;
/**
- * The current line number in the raw source.
- * @type integer
+ * An attached interpreter if it differs from the script interpreter.
+ * @type LanguageObject - The interpreter this token should be bound to.
*/
- this.line = 0;
+ this.i = null;
/**
* Length of the text in the token and its children.
* @deprecated can do a string check on check for length.
@@ -416,31 +444,41 @@ Token = function(){
*/
this.length = 0;
/**
- * Check to see if the token validated.
- * @type boolean
+ * The current line number in the raw source.
+ * @type integer
*/
- this.validated = false;
+ this.line = 0;
+ /**
+ * Name of the type of token.
+ * @type string
+ */
+ this.name = "";
/**
* Execution offset, to check which child is next in the execution tree.
* @type integer
*/
this.offset = -1;
/**
- * Did the token fire? Preventing it from firing twice.
+ * Full text from the token and its children.
+ * @type string
+ */
+ this.text = "";
+ /**
+ * Child tokens.
+ * @type Array
+ */
+ this.tokens = [];
+ /**
+ * Check to see if the token validated.
* @type boolean
*/
- this.fired = false;
+ this.validated = false;
/**
* How complex was the validation of the token?
* More complex validations have more validations and carry more weight in the OR
* @type integer
*/
this.validations = 0;
- /**
- * What types of tokens where expected.
- * @type Array
- */
- this.expected = [];
//CALL TO CONSTRUCTOR//
Constructor.call( this, arguments[0], arguments[1], arguments[2] );
@@ -463,16 +501,16 @@ exports.parser = function(){
//////PRIVATE VARIABLES//
/////////////////////
/**
+ * Parsers attached to the parser for compute multi-language.
+ * @type - AssoArray
+ */
+ var _includedParsers = {};
+ /**
* Interpreter and language object used to parse, and then execute the script.
* @type LanguageObject
*/
var _interpreter = null;
/**
- * Raw script file in string from for parsing.
- * @type string
- */
- var _rawScript = "";
- /**
* Short cut to interpreters rule list enumeration.
* @see LanguageObject.ruleList
* @type Object
@@ -538,41 +576,44 @@ exports.parser = function(){
* @param charPtr integer - Point to start reading the raw script for the token text.
* @returns Token
*/
- function _GenerateToken( rulePath, charPtr ){
+ function _GenerateToken( rulePath, charPtr, scriptSettings ){
var token = new Token( rulePath.name, rulePath.id, charPtr );
+ if( scriptSettings.subScript ){
+ token.i = _interpreter;
+ }
return token;
};
/**
- * Gets the raw language script
- * @returns string - Of the raw script
- */
- this.GetRawScript = function(){
- return _rawScript;
- };
- /**
* Gets an error output for a token set
* @param Token ruleToken - The top of the token tree to report the error on.
*/
- this.GetErrorOutput = function( ruleToken ){
+ this.GetErrorOutput = function( ruleToken, scriptSettings ){
console.log( "Script didn't evaluate correctly." );
var failedOutput = _ErrorExpactation.call( this, ruleToken.expected );
console.log( "Failed @line:" + failedOutput.line + " @charicter:" + failedOutput.charPtr );
console.log( "Expected tokens [" + failedOutput.tokenArray.join( "," ) + "]" );
- console.log( "Recived " + _rawScript.substr( failedOutput.charPtr, 1 ) );
+ console.log( "Recived " + scriptSettings.rawScript.substr( failedOutput.charPtr, 1 ) );
+ };
+ /**
+ * Get the name of the language from the interpreter.
+ * @returns string - The name of the language.
+ */
+ this.GetLanguageName = function(){
+ return _interpreter.GetName();
};
/**
* Attempts to create a token at the given grammar rule set and add it to the parent token.
* @param grammar Object - The grammar rule to parse the raw script with.
* @param ruleContainer Token - The parent token which will contain the parsed script token.
*/
- function _ProcessRuleGrammar( grammar, ruleContainer ){
+ function _ProcessRuleGrammar( grammar, ruleContainer, scriptSettings ){
switch( grammar.type ){
case _rl.and:
ruleContainer.validated = true;
for( var i = 0; i < grammar.syntax.length && ruleContainer.validated == true; i++ ){
if( grammar.syntax[i].id != undefined ){ //Grammar object
- var ruleToken = _GenerateToken.call( this, grammar.syntax[i], ruleContainer.length + ruleContainer.charPtr );
- _ProcessRuleGrammar.call( this, grammar.syntax[i].grammar, ruleToken );
+ var ruleToken = _GenerateToken.call( this, grammar.syntax[i], ruleContainer.length + ruleContainer.charPtr, scriptSettings );
+ _ProcessRuleGrammar.call( this, grammar.syntax[i].grammar, ruleToken, scriptSettings );
if( ruleToken.validated ){
ruleContainer.tokens.push( ruleToken );
ruleContainer.length += ruleToken.length;
@@ -585,18 +626,18 @@ exports.parser = function(){
}
}
else if( grammar.syntax[i].type != undefined ){ //Multi-level grammar
- _ProcessRuleGrammar.call( this, grammar.syntax[i], ruleContainer );
+ _ProcessRuleGrammar.call( this, grammar.syntax[i], ruleContainer, scriptSettings );
}
else if( grammar.syntax[i].method != undefined ){ //Method call
- var evaluated = grammar.syntax[i].method.call( this, ruleContainer );
+ var evaluated = grammar.syntax[i].method.call( this, ruleContainer, scriptSettings );
if( !evaluated ){
ruleContainer.validated = false;
ruleContainer.expected.push( { grammar: grammar.syntax[i].name, token:null } );
}
}
else if( grammar.syntax[i].length ){ //Text
- if( _rawScript.substr( ruleContainer.length + ruleContainer.charPtr , grammar.syntax[i].length ) == grammar.syntax[i] ){
- ruleContainer.text += _rawScript.substr( ruleContainer.length + ruleContainer.charPtr, grammar.syntax[i].length );
+ if( scriptSettings.rawScript.substr( ruleContainer.length + ruleContainer.charPtr , grammar.syntax[i].length ) == grammar.syntax[i] ){
+ ruleContainer.text += scriptSettings.rawScript.substr( ruleContainer.length + ruleContainer.charPtr, grammar.syntax[i].length );
ruleContainer.length += grammar.syntax[i].length;
ruleContainer.validations++;
}
@@ -615,8 +656,8 @@ exports.parser = function(){
for( var i = 0; i < grammar.syntax.length; i++ ){
var subContainer = { length: ruleContainer.length, charPtr:ruleContainer.charPtr , text:ruleContainer.text , tokens:[], validations:0, validated:false, expected:[] };
if( grammar.syntax[i].id ){ //Grammar object
- var ruleToken = _GenerateToken.call( this, grammar.syntax[i], subContainer.charPtr );
- _ProcessRuleGrammar.call( this, grammar.syntax[i].grammar, ruleToken );
+ var ruleToken = _GenerateToken.call( this, grammar.syntax[i], subContainer.charPtr, scriptSettings );
+ _ProcessRuleGrammar.call( this, grammar.syntax[i].grammar, ruleToken, scriptSettings );
if( ruleToken.validated ){
subContainer.tokens.push( ruleToken );
subContainer.length += ruleToken.length;
@@ -629,10 +670,10 @@ exports.parser = function(){
}
}
else if( grammar.syntax[i].type ){ //Multi-level grammar
- _ProcessRuleGrammar.call( this, grammar.syntax[i], subContainer );
+ _ProcessRuleGrammar.call( this, grammar.syntax[i], subContainer, scriptSettings );
}
else if( grammar.syntax[i].method != undefined ){ //Method call
- var evaluated = grammar.syntax[i].method.call( this, subContainer );
+ var evaluated = grammar.syntax[i].method.call( this, subContainer, scriptSettings );
if( evaluated ){
subContainer.validated = true;
}
@@ -641,9 +682,9 @@ exports.parser = function(){
}
}
else{ //Text
- if( _rawScript.substr( subContainer.charPtr + subContainer.length, grammar.syntax[i].length ) == grammar.syntax[i] ){
+ if( scriptSettings.rawScript.substr( subContainer.charPtr + subContainer.length, grammar.syntax[i].length ) == grammar.syntax[i] ){
validatedRule = i;
- subContainer.text += _rawScript.substr( subContainer.charPtr + subContainer.length, grammar.syntax[i].length );
+ subContainer.text += scriptSettings.rawScript.substr( subContainer.charPtr + subContainer.length, grammar.syntax[i].length );
subContainer.length += grammar.syntax[i].length;
subContainer.validated = true;
subContainer.validations++;
@@ -669,7 +710,7 @@ exports.parser = function(){
}
break;
case _rl.cgroup:
- var char = _rawScript.substr( ruleContainer.charPtr + ruleContainer.length, 1 );
+ var char = scriptSettings.rawScript.substr( ruleContainer.charPtr + ruleContainer.length, 1 );
var charNumber = char.charCodeAt( 0 );
if( charNumber >= grammar.low && charNumber <= grammar.high ){
ruleContainer.length += 1;
@@ -688,8 +729,25 @@ exports.parser = function(){
console.log( "optional" );
break;
case _rl.include:
- console.log( "include" );
- process.exit();
+ if( _includedParsers[grammar.syntax[0]] ){
+ var subContainer = { name:grammar.syntax[0], length: ruleContainer.length, charPtr:ruleContainer.charPtr , text:ruleContainer.text , tokens:[], validations:0, validated:false, expected:[] };
+ _includedParsers[grammar.syntax[0]].ParseIncludedInto( subContainer, scriptSettings );
+ if( subContainer.validated == true ){
+ ruleContainer.name = subContainer.name;
+ ruleContainer.length = subContainer.length;
+ ruleContainer.text = subContainer.text;
+ ruleContainer.validations = subContainer.validations;
+ ruleContainer.validated = true;
+ for( var t = 0; t < subContainer.tokens.length; t++ ){
+ ruleContainer.tokens.push( subContainer.tokens[t] );
+ }
+ }
+ }
+ else{
+ console.log( "Call to include grammar of unknown type. '"+grammar.syntax[0]+"'" );
+ process.exit();
+ }
+
break;
default:
console.log( "Unknown grammar type, '"+grammar.type+"' please add a case for the grammar." );
@@ -703,29 +761,32 @@ exports.parser = function(){
* @param rulePath Object( @see syntaxObject ) - The syntax object rule path to start the parser.
* @returns Script - The compiled script object.
*/
- function _Tokenize( rulePath ){
+ function _Tokenize( rulePath, scriptSettings ){
//Generate the core token//
- var ruleToken = _GenerateToken( _interpreter.syntaxObject.script, 0 );
+ var ruleToken = _GenerateToken( _interpreter.syntaxObject.script, 0, scriptSettings );
//Go over token rules in the core grammar//
- _ProcessRuleGrammar.call( this, rulePath.grammar, ruleToken );
+ _ProcessRuleGrammar.call( this, rulePath.grammar, ruleToken, scriptSettings );
//Did our script return valid?//
- var script = new Script( this, _interpreter, ruleToken );
+ var script = new Script( this, _interpreter, ruleToken, scriptSettings );
return script;
};
////////////////////
//////PUBLIC VARIABLES//
////////////////////
- /**
- * The length equal to the end of the raw source.
- * @type integer
- */
- this.endOfSource = 0;
+
//////////////////
//////PUBLIC METHODS//
//////////////////
/**
+ * Creates a connection between this parser and another.
+ * @param includedParser parser - The parser to include.
+ */
+ this.IncludeLanguage = function( includedParser ){
+ _includedParsers[includedParser.GetLanguageName()] = includedParser;
+ };
+ /**
* Parses a given script file.
* @param scriptFile string - Name of script file to parse.
*/
@@ -744,14 +805,34 @@ exports.parser = function(){
* @param scriptString string - Raw string of the script to parse.
*/
this.ParseScriptString = function( scriptString ){
- _rawScript = scriptString.toString();
- this.endOfSource = scriptString.length;
- _lineNumber = 1;
+ var scriptSettings = {
+ rawScript:scriptString.toString(),
+ endOfSource:scriptString.length,
+ lineNumber:0
+ };
_tokenContainer = null;
- var script = _Tokenize.call( this, _interpreter.syntaxObject.script );
+ var script = _Tokenize.call( this, _interpreter.syntaxObject.script, scriptSettings );
script.Execute();
return script;
};
+ /**
+ * Call from another parser telling this parser to compile from syntax token using an existing script, and return the returns to the calling parser.
+ * @param subContainer Token - The token container to be attached.
+ * @param scriptSettings Object - The settings of the script from the caller.
+ */
+ this.ParseIncludedInto = function( subContainer, scriptSettings ){
+ var nScriptSettings = {};
+ if( !scriptSettings.subScript ){
+ for( i in scriptSettings ){
+ nScriptSettings[i] = scriptSettings[i];
+ }
+ nScriptSettings.subScript = true;
+ }
+ else{
+ nScriptSettings = scriptSettings;
+ }
+ _ProcessRuleGrammar.call( this, _interpreter.syntaxObject.syntax.grammar, subContainer, nScriptSettings );
+ };
//CALL TO CONSTRUCTOR//
Constructor.call( this, arguments[0] );
Please sign in to comment.
Something went wrong with that request. Please try again.