From ef8570c6ade6c2cb508b412b5fe776e5d894616d Mon Sep 17 00:00:00 2001 From: ericvergnaud Date: Sat, 4 Oct 2014 02:00:14 +0800 Subject: [PATCH] first successful selenium/Safari test --- src/antlr4/FileStream.js | 8 +- src/antlr4/Parser.js | 3 +- src/antlr4/index.js | 9 +- src/lib/require.js | 213 ++++ .../codegen/JavaScript/JavaScript.stg | 8 +- .../antlr/v4/js/{ => node}/test/BaseTest.java | 2 +- .../{ => node}/test/PositionAdjustingLexer.g4 | 0 .../test/TestCompositeGrammars.java | 2 +- .../test/TestFullContextParsing.java | 2 +- .../js/{ => node}/test/TestLeftRecursion.java | 2 +- .../js/{ => node}/test/TestLexerActions.java | 2 +- .../js/{ => node}/test/TestLexerErrors.java | 2 +- .../v4/js/{ => node}/test/TestLexerExec.java | 2 +- .../v4/js/{ => node}/test/TestListeners.java | 2 +- .../js/{ => node}/test/TestParseErrors.java | 2 +- .../v4/js/{ => node}/test/TestParseTrees.java | 2 +- .../v4/js/{ => node}/test/TestParserExec.java | 2 +- .../{ => node}/test/TestSemPredEvalLexer.java | 2 +- .../test/TestSemPredEvalParser.java | 2 +- .../antlr/v4/js/{ => node}/test/TestSets.java | 2 +- .../org/antlr/v4/js/safari/test/BaseTest.java | 1009 +++++++++++++++++ .../v4/js/safari/test/TestParserExec.java | 84 ++ 22 files changed, 1336 insertions(+), 26 deletions(-) create mode 100644 src/lib/require.js rename tool/test/org/antlr/v4/js/{ => node}/test/BaseTest.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/PositionAdjustingLexer.g4 (100%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestCompositeGrammars.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestFullContextParsing.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestLeftRecursion.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestLexerActions.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestLexerErrors.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestLexerExec.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestListeners.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestParseErrors.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestParseTrees.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestParserExec.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestSemPredEvalLexer.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestSemPredEvalParser.java (99%) rename tool/test/org/antlr/v4/js/{ => node}/test/TestSets.java (99%) create mode 100644 tool/test/org/antlr/v4/js/safari/test/BaseTest.java create mode 100644 tool/test/org/antlr/v4/js/safari/test/TestParserExec.java diff --git a/src/antlr4/FileStream.js b/src/antlr4/FileStream.js index 031d4f25f1..67b1b642d0 100644 --- a/src/antlr4/FileStream.js +++ b/src/antlr4/FileStream.js @@ -34,8 +34,12 @@ // when you construct the object. // var InputStream = require('./InputStream').InputStream; -var fs = require("fs"); - +try { + var fs = require("fs"); +} catch(ex) { + // probably running from browser, no "Node.js/fs" makes sense +} + function FileStream(fileName) { var data = fs.readFileSync(fileName, "utf8"); InputStream.call(this, data); diff --git a/src/antlr4/Parser.js b/src/antlr4/Parser.js index 59e2973407..b12364a4c7 100644 --- a/src/antlr4/Parser.js +++ b/src/antlr4/Parser.js @@ -315,8 +315,7 @@ Parser.prototype.getATNWithBypassAlts = function() { var Lexer = require('./Lexer').Lexer; -Parser.prototype.compileParseTreePattern = function(pattern, patternRuleIndex, - lexer) { +Parser.prototype.compileParseTreePattern = function(pattern, patternRuleIndex, lexer) { lexer = lexer || null; if (lexer === null) { if (this.getTokenStream() !== null) { diff --git a/src/antlr4/index.js b/src/antlr4/index.js index dc16e55e19..75fb5de589 100644 --- a/src/antlr4/index.js +++ b/src/antlr4/index.js @@ -1,8 +1,9 @@ -exports.atn = require('./atn'); -exports.dfa = require('./dfa'); -exports.tree = require('./tree'); -exports.error = require('./error'); +exports.atn = require('./atn/index'); +exports.dfa = require('./dfa/index'); +exports.tree = require('./tree/index'); +exports.error = require('./error/index'); exports.Token = require('./Token').Token; +exports.InputStream = require('./InputStream').InputStream; exports.FileStream = require('./FileStream').FileStream; exports.CommonTokenStream = require('./CommonTokenStream').CommonTokenStream; exports.Lexer = require('./Lexer').Lexer; diff --git a/src/lib/require.js b/src/lib/require.js new file mode 100644 index 0000000000..0391317cee --- /dev/null +++ b/src/lib/require.js @@ -0,0 +1,213 @@ +// +// This file is part of Smoothie. +// +// Copyright (C) 2013,14 Torben Haase, Flowy Apps (torben@flowyapps.com) +// +// Smoothie is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +// A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details.You should have received a copy of the GNU Lesser General Public +// License along with Smoothie. If not, see . +// +//////////////////////////////////////////////////////////////////////////////// + +// INFO Standalone require() +// This is a stripped down standalone version of Smoothie's require +// function. If you also like to have a 'bootloader', which gives you some +// nice hooks to execute code on different loading states of the document +// and keeps your JavaScript completely separate from your HTML, I +// recommend to load smoothie.js from the library's root directory. + +// NOTE The load parameter points to the function, which prepares the +// environment for each module and runs its code. Scroll down to the end of +// the file to see the function definition. +(function(load) { 'use strict'; + +var SmoothieError = function(message, fileName, lineNumber) { + this.name = "SmoothieError"; + this.message = message; +} +SmoothieError.prototype = Object.create(Error.prototype); + +// INFO Smoothie options +// The values can be set by defining a object called Smoothie, which +// contains properties of the same name as the options to be changed. +// NOTE The Smoothe object has to be defined before this script here is loaded +// and changing the values in the Smoothie object will have no effect +// afterwards! + +// NOTE Global module paths +var paths = window.Smoothie&&window.Smoothie.paths!==undefined?window.Smoothie.paths.slice(0):['./']; + +// INFO Current module paths +// path[0] contains the path of the currently loaded module, path[1] +// contains the path its parent module and so on. + +var pwd = Array(''); + +// INFO Path parser +// A HTMLAnchorElement parses its href property automatically, so we use +// this functionality to parse our module paths instead of implemnting our +// own. + +var parser = document.createElement('A'); + +// INFO Module cache +// Contains getter functions for the exports objects of all the loaded +// modules. The getter for the module 'mymod' is name '$name' to prevent +// collisions with predefined object properties (see note below). +// As long as a module has not been loaded the getter is either undefined +// or contains the module code as a function (in case the module has been +// pre-laoded in a bundle). +// NOTE For IE8 the cache will be replaced by a HTMLDivElement-object later on, +// since defineProperty is only supported for DOM objects there. + +var cache = new Object(); +var locks = new Object(); + +// INFO Module getter +// Takes a module identifier, resolves it and gets the module code via an +// AJAX request from the module URI. If this was successful the code and +// some environment variables are passed to the load function. The return +// value is the module's `exports` object. If the cache already +// contains an object for the module id, this object is returned directly. +// NOTE If a callback function has been passed, the AJAX request is asynchronous +// and the mpdule exports are passed to the callback function after the +// module has been loaded. + +function require(identifier, callback) { + var descriptor = resolve(identifier); + var cacheid = '$'+descriptor.id; + + if (cache[cacheid]) { + if (typeof cache[cacheid] === 'string') + load(descriptor, cache, pwd, cache[cacheid]); + // NOTE The callback should always be called asynchronously to ensure + // that a cached call won't differ from an uncached one. + callback && setTimeout(function(){callback(cache[cacheid])}, 0); + return cache[cacheid]; + } + + var request = new XMLHttpRequest(); + + // NOTE IE8 doesn't support the onload event, therefore we use + // onreadystatechange as a fallback here. However, onreadystatechange + // shouldn't be used for all browsers, since at least mobile Safari + // seems to have an issue where onreadystatechange is called twice for + // readyState 4. + callback && (request[request.onload===null?'onload':'onreadystatechange'] = onLoad); + request.open('GET', descriptor.uri, !!callback); + // NOTE Sending the request causes the event loop to continue. Therefore + // pending AJAX load events for the same url might be executed before + // the synchronous onLoad is called. This should be no problem, but in + // Chrome the responseText of the sneaked in load events will be empty. + // Therefore we have to lock the loading while executong send(). + locks[cacheid] = locks[cacheid]++||1; + request.send(); + locks[cacheid]--; + !callback && onLoad(); + return cache[cacheid]; + + function onLoad() { + if (request.readyState != 4) + return; + if (request.status != 200 && request.status != 0 ) // 0 for Safari with file protocol + throw new SmoothieError('unable to load '+descriptor.id+" ("+request.status+" "+request.statusText+")"); + if (locks[cacheid]) { + console.warn("module locked: "+descriptor.id); + callback && setTimeout(onLoad, 0); + return; + } + if (!cache[cacheid]) + load(descriptor, cache, pwd, 'function(){\n'+request.responseText+'\n}'); + callback && callback(cache[cacheid]); + } +} + +// INFO Module resolver +// Takes a module identifier and resolves it to a module id and URI. Both +// values are returned as a module descriptor, which can be passed to +// `fetch` to load a module. + +function resolve(identifier) { + // NOTE Matches [1]:[..]/[path/to/][file][.js] + var m = identifier.match(/^(?:([^:\/]+):)?(\.\.?)?\/?((?:.*\/)?)([^\.]+)?(\..*)?$/); + // NOTE Matches [1]:[/path/to] + var p = pwd[0].match(/^(?:([^:\/]+):)?(.*)/); + var root = m[2] ? paths[p[1]?parseInt(p[1]):0] : paths[m[1]?parseInt(m[1]):0]; + parser.href = (m[2]?root+p[2]+m[2]+'/':root)+m[3]+(m[4]?m[4]:'index'); + var id = parser.href.replace(/^[^:]*:\/\/[^\/]*\/|\/(?=\/)/g, ''); + var uri = "/"+id+(m[5]?m[5]:'.js'); + root.replace(/[^\/]+\//g, function(r) { + id = (id.substr(0, r.length) == r) ?id.substr(r.length) : id = '../'+id; + }); + return {'id':id,'uri':uri}; +} + +// INFO Exporting require to global scope + +if (window.require !== undefined) + throw new SmoothieError('\'require\' already defined in global scope'); + +try { + Object.defineProperty(window, 'require', {'value':require}); + Object.defineProperty(window.require, 'resolve', {'value':resolve}); + Object.defineProperty(window.require, 'paths', {'get':function(){return paths.slice(0);}}); +} +catch (e) { + // NOTE IE8 can't use defineProperty on non-DOM objects, so we have to fall + // back to unsave property assignments in this case. + window.require = require; + window.require.resolve = resolve; + window.require.paths = paths.slice(0); + // NOTE We definetly need a getter for the cache, so we make the the cache a + // DOM-object in IE8. + cache = document.createElement('DIV'); +} + +// INFO Adding preloaded modules to cache + +for (var id in (window.Smoothie && window.Smoothie.preloaded)) + cache['$'+id] = window.Smoothie.preloaded[id].toString(); + +// INFO Parsing module root paths + +for (var i=0; i -var antlr4 = require('antlr4'); +var antlr4 = require('antlr4/index'); var Listener = require('./Listener').Listener; @@ -62,7 +62,7 @@ var Visitor = require('./Visitor'). -var antlr4 = require('antlr4'); +var antlr4 = require('antlr4/index'); // This class defines a complete listener for a parse tree produced by . function Listener() { @@ -90,7 +90,7 @@ exports.Listener = Listener; VisitorFile(file, header) ::= << -var antlr4 = require('antlr4'); +var antlr4 = require('antlr4/index');
// This class defines a complete generic visitor for a parse tree produced by . @@ -775,7 +775,7 @@ _prevctx = localctx; LexerFile(lexerFile, lexer, namedActions) ::= << -var antlr4 = require('antlr4'); +var antlr4 = require('antlr4/index'); diff --git a/tool/test/org/antlr/v4/js/test/BaseTest.java b/tool/test/org/antlr/v4/js/node/test/BaseTest.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/BaseTest.java rename to tool/test/org/antlr/v4/js/node/test/BaseTest.java index 70f2758354..52ef816802 100644 --- a/tool/test/org/antlr/v4/js/test/BaseTest.java +++ b/tool/test/org/antlr/v4/js/node/test/BaseTest.java @@ -27,7 +27,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; diff --git a/tool/test/org/antlr/v4/js/test/PositionAdjustingLexer.g4 b/tool/test/org/antlr/v4/js/node/test/PositionAdjustingLexer.g4 similarity index 100% rename from tool/test/org/antlr/v4/js/test/PositionAdjustingLexer.g4 rename to tool/test/org/antlr/v4/js/node/test/PositionAdjustingLexer.g4 diff --git a/tool/test/org/antlr/v4/js/test/TestCompositeGrammars.java b/tool/test/org/antlr/v4/js/node/test/TestCompositeGrammars.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestCompositeGrammars.java rename to tool/test/org/antlr/v4/js/node/test/TestCompositeGrammars.java index 54434ffc7f..42e0c38d79 100644 --- a/tool/test/org/antlr/v4/js/test/TestCompositeGrammars.java +++ b/tool/test/org/antlr/v4/js/node/test/TestCompositeGrammars.java @@ -28,7 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; diff --git a/tool/test/org/antlr/v4/js/test/TestFullContextParsing.java b/tool/test/org/antlr/v4/js/node/test/TestFullContextParsing.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestFullContextParsing.java rename to tool/test/org/antlr/v4/js/node/test/TestFullContextParsing.java index 3847957afb..6167115a6d 100644 --- a/tool/test/org/antlr/v4/js/test/TestFullContextParsing.java +++ b/tool/test/org/antlr/v4/js/node/test/TestFullContextParsing.java @@ -28,7 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import org.junit.Test; diff --git a/tool/test/org/antlr/v4/js/test/TestLeftRecursion.java b/tool/test/org/antlr/v4/js/node/test/TestLeftRecursion.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestLeftRecursion.java rename to tool/test/org/antlr/v4/js/node/test/TestLeftRecursion.java index 1cfd4da2ad..80522e5b37 100644 --- a/tool/test/org/antlr/v4/js/test/TestLeftRecursion.java +++ b/tool/test/org/antlr/v4/js/node/test/TestLeftRecursion.java @@ -28,7 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import org.antlr.v4.tool.ErrorType; import org.junit.Test; diff --git a/tool/test/org/antlr/v4/js/test/TestLexerActions.java b/tool/test/org/antlr/v4/js/node/test/TestLexerActions.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestLexerActions.java rename to tool/test/org/antlr/v4/js/node/test/TestLexerActions.java index 434ce5acdc..a2ea86d15e 100644 --- a/tool/test/org/antlr/v4/js/test/TestLexerActions.java +++ b/tool/test/org/antlr/v4/js/node/test/TestLexerActions.java @@ -1,4 +1,4 @@ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import org.junit.Test; diff --git a/tool/test/org/antlr/v4/js/test/TestLexerErrors.java b/tool/test/org/antlr/v4/js/node/test/TestLexerErrors.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestLexerErrors.java rename to tool/test/org/antlr/v4/js/node/test/TestLexerErrors.java index c89b9581dc..e7846dd17b 100644 --- a/tool/test/org/antlr/v4/js/test/TestLexerErrors.java +++ b/tool/test/org/antlr/v4/js/node/test/TestLexerErrors.java @@ -28,7 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import org.junit.Test; diff --git a/tool/test/org/antlr/v4/js/test/TestLexerExec.java b/tool/test/org/antlr/v4/js/node/test/TestLexerExec.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestLexerExec.java rename to tool/test/org/antlr/v4/js/node/test/TestLexerExec.java index 96bc060733..d3264f2f99 100644 --- a/tool/test/org/antlr/v4/js/test/TestLexerExec.java +++ b/tool/test/org/antlr/v4/js/node/test/TestLexerExec.java @@ -28,7 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import org.antlr.v4.runtime.misc.Nullable; import org.junit.Test; diff --git a/tool/test/org/antlr/v4/js/test/TestListeners.java b/tool/test/org/antlr/v4/js/node/test/TestListeners.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestListeners.java rename to tool/test/org/antlr/v4/js/node/test/TestListeners.java index d8ae545253..1bef550e59 100644 --- a/tool/test/org/antlr/v4/js/test/TestListeners.java +++ b/tool/test/org/antlr/v4/js/node/test/TestListeners.java @@ -28,7 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import org.junit.Test; diff --git a/tool/test/org/antlr/v4/js/test/TestParseErrors.java b/tool/test/org/antlr/v4/js/node/test/TestParseErrors.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestParseErrors.java rename to tool/test/org/antlr/v4/js/node/test/TestParseErrors.java index b0f1328bdf..fdf2e2bea3 100644 --- a/tool/test/org/antlr/v4/js/test/TestParseErrors.java +++ b/tool/test/org/antlr/v4/js/node/test/TestParseErrors.java @@ -28,7 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; diff --git a/tool/test/org/antlr/v4/js/test/TestParseTrees.java b/tool/test/org/antlr/v4/js/node/test/TestParseTrees.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestParseTrees.java rename to tool/test/org/antlr/v4/js/node/test/TestParseTrees.java index 95582c9c84..101f3b7c97 100644 --- a/tool/test/org/antlr/v4/js/test/TestParseTrees.java +++ b/tool/test/org/antlr/v4/js/node/test/TestParseTrees.java @@ -28,7 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import org.junit.Test; diff --git a/tool/test/org/antlr/v4/js/test/TestParserExec.java b/tool/test/org/antlr/v4/js/node/test/TestParserExec.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestParserExec.java rename to tool/test/org/antlr/v4/js/node/test/TestParserExec.java index a3a502cb3f..1288f9044c 100644 --- a/tool/test/org/antlr/v4/js/test/TestParserExec.java +++ b/tool/test/org/antlr/v4/js/node/test/TestParserExec.java @@ -28,7 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import org.junit.Ignore; import org.junit.Test; diff --git a/tool/test/org/antlr/v4/js/test/TestSemPredEvalLexer.java b/tool/test/org/antlr/v4/js/node/test/TestSemPredEvalLexer.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestSemPredEvalLexer.java rename to tool/test/org/antlr/v4/js/node/test/TestSemPredEvalLexer.java index 03c68e4366..88e0a80d9a 100644 --- a/tool/test/org/antlr/v4/js/test/TestSemPredEvalLexer.java +++ b/tool/test/org/antlr/v4/js/node/test/TestSemPredEvalLexer.java @@ -28,7 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import org.junit.Test; diff --git a/tool/test/org/antlr/v4/js/test/TestSemPredEvalParser.java b/tool/test/org/antlr/v4/js/node/test/TestSemPredEvalParser.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestSemPredEvalParser.java rename to tool/test/org/antlr/v4/js/node/test/TestSemPredEvalParser.java index 1b2d63c8a6..5f411c5111 100644 --- a/tool/test/org/antlr/v4/js/test/TestSemPredEvalParser.java +++ b/tool/test/org/antlr/v4/js/node/test/TestSemPredEvalParser.java @@ -28,7 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import org.junit.Test; diff --git a/tool/test/org/antlr/v4/js/test/TestSets.java b/tool/test/org/antlr/v4/js/node/test/TestSets.java similarity index 99% rename from tool/test/org/antlr/v4/js/test/TestSets.java rename to tool/test/org/antlr/v4/js/node/test/TestSets.java index bd39e0f19c..4a34181dfb 100644 --- a/tool/test/org/antlr/v4/js/test/TestSets.java +++ b/tool/test/org/antlr/v4/js/node/test/TestSets.java @@ -27,7 +27,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.antlr.v4.js.test; +package org.antlr.v4.js.node.test; import org.antlr.v4.tool.ErrorType; import org.junit.Test; diff --git a/tool/test/org/antlr/v4/js/safari/test/BaseTest.java b/tool/test/org/antlr/v4/js/safari/test/BaseTest.java new file mode 100644 index 0000000000..9e91c2efcb --- /dev/null +++ b/tool/test/org/antlr/v4/js/safari/test/BaseTest.java @@ -0,0 +1,1009 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.v4.js.safari.test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.antlr.v4.Tool; +import org.antlr.v4.automata.ATNFactory; +import org.antlr.v4.automata.ATNPrinter; +import org.antlr.v4.automata.LexerATNFactory; +import org.antlr.v4.automata.ParserATNFactory; +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CommonToken; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.IntStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.RuleContext; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenSource; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.WritableToken; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.ATNSerializer; +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.atn.LexerATNSimulator; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.misc.IntegerList; +import org.antlr.v4.runtime.misc.Interval; +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.misc.Nullable; +import org.antlr.v4.semantics.SemanticPipeline; +import org.antlr.v4.test.ErrorQueue; +import org.antlr.v4.tool.ANTLRMessage; +import org.antlr.v4.tool.DOTGenerator; +import org.antlr.v4.tool.DefaultToolListener; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.GrammarSemanticsMessage; +import org.antlr.v4.tool.LexerGrammar; +import org.antlr.v4.tool.Rule; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.junit.Before; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.openqa.selenium.By.ById; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.safari.SafariDriver; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupString; + +public abstract class BaseTest { + // -J-Dorg.antlr.v4.test.BaseTest.level=FINE + // private static final Logger LOGGER = Logger.getLogger(BaseTest.class.getName()); + + public static final String newline = System.getProperty("line.separator"); + public static final String pathSep = System.getProperty("path.separator"); + + public String tmpdir = null; + + /** If error during parser execution, store stderr here; can't return + * stdout and stderr. This doesn't trap errors from running antlr. + */ + protected String stderrDuringParse; + + @org.junit.Rule + public final TestRule testWatcher = new TestWatcher() { + + @Override + protected void succeeded(Description description) { + // remove tmpdir if no error. + eraseTempDir(); + } + + }; + + + @Before + public void setUp() throws Exception { + // new output dir for each test + String prop = System.getProperty("antlr-javascript-test-dir"); + if(prop!=null && prop.length()>0) + tmpdir = prop; + else + tmpdir = new File(System.getProperty("java.io.tmpdir"), getClass().getSimpleName()+"-"+System.currentTimeMillis()).getAbsolutePath(); + File dir = new File(tmpdir); + if(dir.exists()) + this.eraseFiles(dir); + } + + protected org.antlr.v4.Tool newTool(String[] args) { + Tool tool = new Tool(args); + return tool; + } + + protected Tool newTool() { + org.antlr.v4.Tool tool = new Tool(new String[] {"-o", tmpdir}); + return tool; + } + + protected ATN createATN(Grammar g, boolean useSerializer) { + if ( g.atn==null ) { + semanticProcess(g); + assertEquals(0, g.tool.getNumErrors()); + + ParserATNFactory f; + if ( g.isLexer() ) { + f = new LexerATNFactory((LexerGrammar)g); + } + else { + f = new ParserATNFactory(g); + } + + g.atn = f.createATN(); + assertEquals(0, g.tool.getNumErrors()); + } + + ATN atn = g.atn; + if (useSerializer) { + char[] serialized = ATNSerializer.getSerializedAsChars(atn); + return new ATNDeserializer().deserialize(serialized); + } + + return atn; + } + + protected void semanticProcess(Grammar g) { + if ( g.ast!=null && !g.ast.hasErrors ) { + System.out.println(g.ast.toStringTree()); + Tool antlr = new Tool(); + SemanticPipeline sem = new SemanticPipeline(g); + sem.process(); + if ( g.getImportedGrammars()!=null ) { // process imported grammars (if any) + for (Grammar imp : g.getImportedGrammars()) { + antlr.processNonCombinedGrammar(imp, false); + } + } + } + } + + + IntegerList getTypesFromString(Grammar g, String expecting) { + IntegerList expectingTokenTypes = new IntegerList(); + if ( expecting!=null && !expecting.trim().isEmpty() ) { + for (String tname : expecting.replace(" ", "").split(",")) { + int ttype = g.getTokenType(tname); + expectingTokenTypes.add(ttype); + } + } + return expectingTokenTypes; + } + + public IntegerList getTokenTypesViaATN(String input, LexerATNSimulator lexerATN) { + ANTLRInputStream in = new ANTLRInputStream(input); + IntegerList tokenTypes = new IntegerList(); + int ttype; + do { + ttype = lexerATN.match(in, Lexer.DEFAULT_MODE); + tokenTypes.add(ttype); + } while ( ttype!= Token.EOF ); + return tokenTypes; + } + + public List getTokenTypes(LexerGrammar lg, + ATN atn, + CharStream input) + { + LexerATNSimulator interp = new LexerATNSimulator(atn,new DFA[] { new DFA(atn.modeToStartState.get(Lexer.DEFAULT_MODE)) },null); + List tokenTypes = new ArrayList(); + int ttype; + boolean hitEOF = false; + do { + if ( hitEOF ) { + tokenTypes.add("EOF"); + break; + } + int t = input.LA(1); + ttype = interp.match(input, Lexer.DEFAULT_MODE); + if ( ttype == Token.EOF ) { + tokenTypes.add("EOF"); + } + else { + tokenTypes.add(lg.typeToTokenList.get(ttype)); + } + + if ( t==IntStream.EOF ) { + hitEOF = true; + } + } while ( ttype!=Token.EOF ); + return tokenTypes; + } + + + /** Return true if all is ok, no errors */ + protected ErrorQueue antlr(String fileName, String grammarFileName, String grammarStr, boolean defaultListener, String... extraOptions) { + String parserDir = tmpdir + "/parser"; + System.out.println("dir "+parserDir); + mkdir(parserDir); + writeFile(parserDir, fileName, grammarStr); + final List options = new ArrayList(); + Collections.addAll(options, extraOptions); + options.add("-Dlanguage=JavaScript"); + options.add("-o"); + options.add(parserDir); + options.add("-lib"); + options.add(parserDir); + options.add(new File(parserDir,grammarFileName).toString()); + + final String[] optionsA = new String[options.size()]; + options.toArray(optionsA); + Tool antlr = newTool(optionsA); + ErrorQueue equeue = new ErrorQueue(antlr); + antlr.addListener(equeue); + if (defaultListener) { + antlr.addListener(new DefaultToolListener(antlr)); + } + antlr.processGrammarsOnCommandLine(); + + if ( !defaultListener && !equeue.errors.isEmpty() ) { + System.err.println("antlr reports errors from "+options); + for (int i = 0; i < equeue.errors.size(); i++) { + ANTLRMessage msg = equeue.errors.get(i); + System.err.println(msg); + } + System.out.println("!!!\ngrammar:"); + System.out.println(grammarStr); + System.out.println("###"); + } + if ( !defaultListener && !equeue.warnings.isEmpty() ) { + System.err.println("antlr reports warnings from "+options); + for (int i = 0; i < equeue.warnings.size(); i++) { + ANTLRMessage msg = equeue.warnings.get(i); + System.err.println(msg); + } + } + + return equeue; + } + + protected String execLexer(String grammarFileName, + String grammarStr, + String lexerName, + String input) throws Exception + { + boolean success = rawGenerateAndBuildRecognizer(grammarFileName, + grammarStr, + null, + lexerName,"-no-listener"); + assertTrue(success); + writeLexerTestFile(lexerName); + String output = execHtmlPage("Test.html", input); + if ( stderrDuringParse!=null && stderrDuringParse.length()>0 ) { + System.err.println(stderrDuringParse); + } + return output; + } + + protected String execParser(String grammarFileName, + String grammarStr, + String parserName, + String lexerName, + String listenerName, + String visitorName, + String startRuleName, + String input) throws Exception + { + boolean success = rawGenerateAndBuildRecognizer(grammarFileName, + grammarStr, + parserName, + lexerName, + "-visitor"); + assertTrue(success); + rawBuildRecognizerTestFile(parserName, + lexerName, + listenerName, + visitorName, + startRuleName); + return execRecognizer(input); + } + + /** Return true if all is well */ + protected boolean rawGenerateAndBuildRecognizer(String grammarFileName, + String grammarStr, + @Nullable String parserName, + String lexerName, + String... extraOptions) + { + return rawGenerateAndBuildRecognizer(grammarFileName, grammarStr, parserName, lexerName, false, extraOptions); + } + + /** Return true if all is well */ + protected boolean rawGenerateAndBuildRecognizer(String grammarFileName, + String grammarStr, + @Nullable String parserName, + String lexerName, + boolean defaultListener, + String... extraOptions) + { + ErrorQueue equeue = + antlr(grammarFileName, grammarFileName, grammarStr, defaultListener, extraOptions); + if (!equeue.errors.isEmpty()) { + return false; + } + + List files = new ArrayList(); + if ( lexerName!=null ) { + files.add(lexerName+".js"); + } + if ( parserName!=null ) { + files.add(parserName+".js"); + Set optionsSet = new HashSet(Arrays.asList(extraOptions)); + if (!optionsSet.contains("-no-listener")) { + files.add(grammarFileName.substring(0, grammarFileName.lastIndexOf('.'))+"Listener.js"); + } + if (optionsSet.contains("-visitor")) { + files.add(grammarFileName.substring(0, grammarFileName.lastIndexOf('.'))+"Visitor.js"); + } + } + return true; // allIsWell: no compile + } + + protected void rawBuildRecognizerTestFile(String parserName, + String lexerName, + String listenerName, + String visitorName, + String parserStartRuleName) + { + this.stderrDuringParse = null; + if ( parserName==null ) { + writeLexerTestFile(lexerName); + } + else { + writeParserTestFile(parserName, + lexerName, + listenerName, + visitorName, + parserStartRuleName); + } + } + + public String execRecognizer(String input) throws Exception { + return execHtmlPage("Test.html", input); + } + + public String execHtmlPage(String fileName, String input) throws Exception { + String runtimePath = locateRuntime(); + Server server = new Server(8080); + try { + // start Jetty, since 'file' protocol is not supported by Selenium Safari driver + ResourceHandler rh1 = new ResourceHandler(); + rh1.setDirectoriesListed(false); + rh1.setResourceBase(tmpdir); + rh1.setWelcomeFiles(new String[] { fileName }); + ResourceHandler rh2 = new ResourceHandler(); + rh2.setDirectoriesListed(false); + rh2.setResourceBase(runtimePath); + HandlerList handlers = new HandlerList(); + handlers.setHandlers(new Handler[] { rh1, rh2, new DefaultHandler() }); + server.setHandler(handlers); + server.start(); + // System.setProperty("webdriver.safari.driver", "/Users/ericvergnaud/Desktop/TestSafari/org/openqa/selenium/safari/SafariDriver.safariextz"); + System.setProperty("webdriver.safari.noinstall", "true"); + WebDriver driver = new SafariDriver(); + try { + driver.get("http://localhost:8080/" + fileName); + driver.findElement(new ById("input")).sendKeys(input); + driver.findElement(new ById("submit")).click(); + String errors = driver.findElement(new ById("errors")).getText(); + if(errors!=null && errors.length()>0) { + this.stderrDuringParse = errors; + System.err.print(errors); + } + return driver.findElement(new ById("output")).getAttribute("value"); + } finally { + driver.close(); + } + } + catch (Exception e) { + System.err.println("can't exec recognizer"); + e.printStackTrace(System.err); + } finally { + server.stop(); + } + return null; + } + + private String locateRuntime() { + String propName = "antlr-javascript-runtime"; + String prop = System.getProperty(propName); + if(prop==null || prop.length()==0) + throw new RuntimeException("Missing system property:" + propName); + return prop; + } + + public void testErrors(String[] pairs, boolean printTree) { + for (int i = 0; i < pairs.length; i+=2) { + String input = pairs[i]; + String expect = pairs[i+1]; + + String[] lines = input.split("\n"); + String fileName = getFilenameFromFirstLineOfGrammar(lines[0]); + ErrorQueue equeue = antlr(fileName, fileName, input, false); + + String actual = equeue.toString(true); + actual = actual.replace(tmpdir + File.separator, ""); + System.err.println(actual); + String msg = input; + msg = msg.replace("\n","\\n"); + msg = msg.replace("\r","\\r"); + msg = msg.replace("\t","\\t"); + + assertEquals("error in: "+msg,expect,actual); + } + } + + public String getFilenameFromFirstLineOfGrammar(String line) { + String fileName = "A" + Tool.GRAMMAR_EXTENSION; + int grIndex = line.lastIndexOf("grammar"); + int semi = line.lastIndexOf(';'); + if ( grIndex>=0 && semi>=0 ) { + int space = line.indexOf(' ', grIndex); + fileName = line.substring(space+1, semi)+Tool.GRAMMAR_EXTENSION; + } + if ( fileName.length()==Tool.GRAMMAR_EXTENSION.length() ) fileName = "A" + Tool.GRAMMAR_EXTENSION; + return fileName; + } + +// void ambig(List msgs, int[] expectedAmbigAlts, String expectedAmbigInput) +// throws Exception +// { +// ambig(msgs, 0, expectedAmbigAlts, expectedAmbigInput); +// } + +// void ambig(List msgs, int i, int[] expectedAmbigAlts, String expectedAmbigInput) +// throws Exception +// { +// List amsgs = getMessagesOfType(msgs, AmbiguityMessage.class); +// AmbiguityMessage a = (AmbiguityMessage)amsgs.get(i); +// if ( a==null ) assertNull(expectedAmbigAlts); +// else { +// assertEquals(a.conflictingAlts.toString(), Arrays.toString(expectedAmbigAlts)); +// } +// assertEquals(expectedAmbigInput, a.input); +// } + +// void unreachable(List msgs, int[] expectedUnreachableAlts) +// throws Exception +// { +// unreachable(msgs, 0, expectedUnreachableAlts); +// } + +// void unreachable(List msgs, int i, int[] expectedUnreachableAlts) +// throws Exception +// { +// List amsgs = getMessagesOfType(msgs, UnreachableAltsMessage.class); +// UnreachableAltsMessage u = (UnreachableAltsMessage)amsgs.get(i); +// if ( u==null ) assertNull(expectedUnreachableAlts); +// else { +// assertEquals(u.conflictingAlts.toString(), Arrays.toString(expectedUnreachableAlts)); +// } +// } + + List getMessagesOfType(List msgs, Class c) { + List filtered = new ArrayList(); + for (ANTLRMessage m : msgs) { + if ( m.getClass() == c ) filtered.add(m); + } + return filtered; + } + + void checkRuleATN(Grammar g, String ruleName, String expecting) { + ParserATNFactory f = new ParserATNFactory(g); + ATN atn = f.createATN(); + + DOTGenerator dot = new DOTGenerator(g); + System.out.println(dot.getDOT(atn.ruleToStartState[g.getRule(ruleName).index])); + + Rule r = g.getRule(ruleName); + ATNState startState = atn.ruleToStartState[r.index]; + ATNPrinter serializer = new ATNPrinter(g, startState); + String result = serializer.asString(); + + //System.out.print(result); + assertEquals(expecting, result); + } + + public void testActions(String templates, String actionName, String action, String expected) throws org.antlr.runtime.RecognitionException { + int lp = templates.indexOf('('); + String name = templates.substring(0, lp); + STGroup group = new STGroupString(templates); + ST st = group.getInstanceOf(name); + st.add(actionName, action); + String grammar = st.render(); + ErrorQueue equeue = new ErrorQueue(); + Grammar g = new Grammar(grammar, equeue); + if ( g.ast!=null && !g.ast.hasErrors ) { + SemanticPipeline sem = new SemanticPipeline(g); + sem.process(); + + ATNFactory factory = new ParserATNFactory(g); + if ( g.isLexer() ) factory = new LexerATNFactory((LexerGrammar)g); + g.atn = factory.createATN(); + + CodeGenerator gen = new CodeGenerator(g); + ST outputFileST = gen.generateParser(); + String output = outputFileST.render(); + //System.out.println(output); + String b = "#" + actionName + "#"; + int start = output.indexOf(b); + String e = "#end-" + actionName + "#"; + int end = output.indexOf(e); + String snippet = output.substring(start+b.length(),end); + assertEquals(expected, snippet); + } + if ( equeue.size()>0 ) { + System.err.println(equeue.toString()); + } + } + + public static class StreamVacuum implements Runnable { + StringBuilder buf = new StringBuilder(); + BufferedReader in; + Thread sucker; + public StreamVacuum(InputStream in) { + this.in = new BufferedReader( new InputStreamReader(in) ); + } + public void start() { + sucker = new Thread(this); + sucker.start(); + } + @Override + public void run() { + try { + String line = in.readLine(); + while (line!=null) { + buf.append(line); + buf.append('\n'); + line = in.readLine(); + } + } + catch (IOException ioe) { + System.err.println("can't read output from process"); + } + } + /** wait for the thread to finish */ + public void join() throws InterruptedException { + sucker.join(); + } + @Override + public String toString() { + return buf.toString(); + } + } + + protected void checkGrammarSemanticsError(ErrorQueue equeue, + GrammarSemanticsMessage expectedMessage) + throws Exception + { + ANTLRMessage foundMsg = null; + for (int i = 0; i < equeue.errors.size(); i++) { + ANTLRMessage m = equeue.errors.get(i); + if (m.getErrorType()==expectedMessage.getErrorType() ) { + foundMsg = m; + } + } + assertNotNull("no error; "+expectedMessage.getErrorType()+" expected", foundMsg); + assertTrue("error is not a GrammarSemanticsMessage", + foundMsg instanceof GrammarSemanticsMessage); + assertEquals(Arrays.toString(expectedMessage.getArgs()), Arrays.toString(foundMsg.getArgs())); + if ( equeue.size()!=1 ) { + System.err.println(equeue); + } + } + + protected void checkGrammarSemanticsWarning(ErrorQueue equeue, + GrammarSemanticsMessage expectedMessage) + throws Exception + { + ANTLRMessage foundMsg = null; + for (int i = 0; i < equeue.warnings.size(); i++) { + ANTLRMessage m = equeue.warnings.get(i); + if (m.getErrorType()==expectedMessage.getErrorType() ) { + foundMsg = m; + } + } + assertNotNull("no error; "+expectedMessage.getErrorType()+" expected", foundMsg); + assertTrue("error is not a GrammarSemanticsMessage", + foundMsg instanceof GrammarSemanticsMessage); + assertEquals(Arrays.toString(expectedMessage.getArgs()), Arrays.toString(foundMsg.getArgs())); + if ( equeue.size()!=1 ) { + System.err.println(equeue); + } + } + + protected void checkError(ErrorQueue equeue, + ANTLRMessage expectedMessage) + throws Exception + { + //System.out.println("errors="+equeue); + ANTLRMessage foundMsg = null; + for (int i = 0; i < equeue.errors.size(); i++) { + ANTLRMessage m = equeue.errors.get(i); + if (m.getErrorType()==expectedMessage.getErrorType() ) { + foundMsg = m; + } + } + assertTrue("no error; "+expectedMessage.getErrorType()+" expected", !equeue.errors.isEmpty()); + assertTrue("too many errors; "+equeue.errors, equeue.errors.size()<=1); + assertNotNull("couldn't find expected error: "+expectedMessage.getErrorType(), foundMsg); + /* + assertTrue("error is not a GrammarSemanticsMessage", + foundMsg instanceof GrammarSemanticsMessage); + */ + assertArrayEquals(expectedMessage.getArgs(), foundMsg.getArgs()); + } + + public static class FilteringTokenStream extends CommonTokenStream { + public FilteringTokenStream(TokenSource src) { super(src); } + Set hide = new HashSet(); + @Override + protected boolean sync(int i) { + if (!super.sync(i)) { + return false; + } + + Token t = get(i); + if ( hide.contains(t.getType()) ) { + ((WritableToken)t).setChannel(Token.HIDDEN_CHANNEL); + } + + return true; + } + public void setTokenTypeChannel(int ttype, int channel) { + hide.add(ttype); + } + } + + public static void writeFile(String dir, String fileName, String content) { + try { + File f = new File(dir, fileName); + FileWriter w = new FileWriter(f); + BufferedWriter bw = new BufferedWriter(w); + bw.write(content); + bw.close(); + w.close(); + } + catch (IOException ioe) { + System.err.println("can't write file"); + ioe.printStackTrace(System.err); + } + } + + protected void mkdir(String dir) { + File f = new File(dir); + f.mkdirs(); + } + + protected void writeParserTestFile(String parserName, + String lexerName, + String listenerName, + String visitorName, + String parserStartRuleName) { + String html = "\r\n" + + "\r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + "
\r\n" + + "
\r\n" + + "
\r\n" + + "
\r\n" + + " \r\n" + + " \r\n" + + "\r\n"; + writeFile(tmpdir, "Test.html", html); + }; + + + protected void writeLexerTestFile(String lexerName) { + ST outputFileST = new ST( + "var antlr4 = require('antlr4');\n" + + "var = require('./');\n" + + "\n" + + "function main(argv) {\n" + + " var input = new antlr4.FileStream(argv[2]);\n" + + " var lexer = new .(input);\n" + + " var stream = new antlr4.CommonTokenStream(lexer);\n" + + " stream.fill();\n" + + " for(var i=0; i\\0) + doErase = Boolean.getBoolean(prop); + if(doErase) { + File tmpdirF = new File(tmpdir); + if ( tmpdirF.exists() ) { + eraseFiles(tmpdirF); + tmpdirF.delete(); + } + } + } + + public String getFirstLineOfException() { + if ( this.stderrDuringParse ==null ) { + return null; + } + String[] lines = this.stderrDuringParse.split("\n"); + String prefix="Exception in thread \"main\" "; + return lines[0].substring(prefix.length(),lines[0].length()); + } + + /** + * When looking at a result set that consists of a Map/HashTable + * we cannot rely on the output order, as the hashing algorithm or other aspects + * of the implementation may be different on differnt JDKs or platforms. Hence + * we take the Map, convert the keys to a List, sort them and Stringify the Map, which is a + * bit of a hack, but guarantees that we get the same order on all systems. We assume that + * the keys are strings. + * + * @param m The Map that contains keys we wish to return in sorted order + * @return A string that represents all the keys in sorted order. + */ + public String sortMapToString(Map m) { + // Pass in crap, and get nothing back + // + if (m == null) { + return null; + } + + System.out.println("Map toString looks like: " + m.toString()); + + // Sort the keys in the Map + // + TreeMap nset = new TreeMap(m); + + System.out.println("Tree map looks like: " + nset.toString()); + return nset.toString(); + } + + public List realElements(List elements) { + return elements.subList(Token.MIN_USER_TOKEN_TYPE, elements.size()); + } + + public void assertNotNullOrEmpty(String message, String text) { + assertNotNull(message, text); + assertFalse(message, text.isEmpty()); + } + + public void assertNotNullOrEmpty(String text) { + assertNotNull(text); + assertFalse(text.isEmpty()); + } + + public static class IntTokenStream implements TokenStream { + IntegerList types; + int p=0; + public IntTokenStream(IntegerList types) { this.types = types; } + + @Override + public void consume() { p++; } + + @Override + public int LA(int i) { return LT(i).getType(); } + + @Override + public int mark() { + return index(); + } + + @Override + public int index() { return p; } + + @Override + public void release(int marker) { + seek(marker); + } + + @Override + public void seek(int index) { + p = index; + } + + @Override + public int size() { + return types.size(); + } + + @Override + public String getSourceName() { + return null; + } + + @Override + public Token LT(int i) { + CommonToken t; + int rawIndex = p + i - 1; + if ( rawIndex>=types.size() ) t = new CommonToken(Token.EOF); + else t = new CommonToken(types.get(rawIndex)); + t.setTokenIndex(rawIndex); + return t; + } + + @Override + public Token get(int i) { + return new org.antlr.v4.runtime.CommonToken(types.get(i)); + } + + @Override + public TokenSource getTokenSource() { + return null; + } + + @NotNull + @Override + public String getText() { + throw new UnsupportedOperationException("can't give strings"); + } + + @NotNull + @Override + public String getText(Interval interval) { + throw new UnsupportedOperationException("can't give strings"); + } + + @NotNull + @Override + public String getText(RuleContext ctx) { + throw new UnsupportedOperationException("can't give strings"); + } + + @NotNull + @Override + public String getText(Token start, Token stop) { + throw new UnsupportedOperationException("can't give strings"); + } + } + + /** Sort a list */ + public > List sort(List data) { + List dup = new ArrayList(); + dup.addAll(data); + Collections.sort(dup); + return dup; + } + + /** Return map sorted by key */ + public ,V> LinkedHashMap sort(Map data) { + LinkedHashMap dup = new LinkedHashMap(); + List keys = new ArrayList(); + keys.addAll(data.keySet()); + Collections.sort(keys); + for (K k : keys) { + dup.put(k, data.get(k)); + } + return dup; + } +} diff --git a/tool/test/org/antlr/v4/js/safari/test/TestParserExec.java b/tool/test/org/antlr/v4/js/safari/test/TestParserExec.java new file mode 100644 index 0000000000..03ab0b7c31 --- /dev/null +++ b/tool/test/org/antlr/v4/js/safari/test/TestParserExec.java @@ -0,0 +1,84 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.antlr.v4.js.safari.test; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** Test parser execution. + * + * For the non-greedy stuff, the rule is that .* or any other non-greedy loop + * (any + or * loop that has an alternative with '.' in it is automatically + * non-greedy) never sees past the end of the rule containing that loop. + * There is no automatic way to detect when the exit branch of a non-greedy + * loop has seen enough input to determine how much the loop should consume + * yet still allow matching the entire input. Of course, this is extremely + * inefficient, particularly for things like + * + * block : '{' (block|.)* '}' ; + * + * that need only see one symbol to know when it hits a '}'. So, I + * came up with a practical solution. During prediction, the ATN + * simulator never fall off the end of a rule to compute the global + * FOLLOW. Instead, we terminate the loop, choosing the exit branch. + * Otherwise, we predict to reenter the loop. For example, input + * "{ foo }" will allow the loop to match foo, but that's it. During + * prediction, the ATN simulator will see that '}' reaches the end of a + * rule that contains a non-greedy loop and stop prediction. It will choose + * the exit branch of the inner loop. So, the way in which you construct + * the rule containing a non-greedy loop dictates how far it will scan ahead. + * Include everything after the non-greedy loop that you know it must scan + * in order to properly make a prediction decision. these beasts are tricky, + * so be careful. don't liberally sprinkle them around your code. + * + * To simulate filter mode, use ( .* (pattern1|pattern2|...) )* + * + * Nongreedy loops match as much input as possible while still allowing + * the remaining input to match. + */ +public class TestParserExec extends BaseTest { + + @Test public void testBasic() throws Exception { + String grammar = + "grammar T;\n" + + "a : ID INT {" + + "document.getElementById('output').value += $text + '\\n';" + + "} ;\n" + + "ID : 'a'..'z'+ ;\n" + + "INT : '0'..'9'+;\n" + + "WS : (' '|'\\n') -> skip ;\n"; + + String found = execParser("T.g4", grammar, "TParser", "TLexer", "TListener", "TVisitor", "a", "abc 34"); + assertEquals("abc34\n", found); + } + +}