You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2019/09/27 07:08:41 UTC
[groovy] 02/02: enabling antlr4 based curly counting lexer in
groovysh when needed (closes #1010)
This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit e5e97dbd0feee907881cd4d4a7e2e1132cab5e6c
Author: Paul King <pa...@asert.com.au>
AuthorDate: Fri Sep 27 17:08:24 2019 +1000
enabling antlr4 based curly counting lexer in groovysh when needed (closes #1010)
---
src/antlr/GroovyLexer.g4 | 14 +++-
.../org/apache/groovy/groovysh/Groovysh.groovy | 86 ++++++++++++----------
2 files changed, 60 insertions(+), 40 deletions(-)
diff --git a/src/antlr/GroovyLexer.g4 b/src/antlr/GroovyLexer.g4
index 41ba5a7..2d1797c 100644
--- a/src/antlr/GroovyLexer.g4
+++ b/src/antlr/GroovyLexer.g4
@@ -143,20 +143,28 @@ options {
}
});
+ protected void enterParenCallback(String text) {}
+
+ protected void exitParenCallback(String text) {}
+
private final Deque<Paren> parenStack = new ArrayDeque<>(32);
+
private void enterParen() {
- parenStack.push(new Paren(getText(), this.lastTokenType, getLine(), getCharPositionInLine()));
+ String text = getText();
+ enterParenCallback(text);
+ parenStack.push(new Paren(text, this.lastTokenType, getLine(), getCharPositionInLine()));
}
+
private void exitParen() {
Paren paren = parenStack.peek();
String text = getText();
-
require(null != paren, "Too many '" + text + "'");
require(text.equals(PAREN_MAP.get(paren.getText())),
"'" + paren.getText() + "'" + new PositionInfo(paren.getLine(), paren.getColumn()) + " can not match '" + text + "'", -1);
-
+ exitParenCallback(text);
parenStack.pop();
}
+
private boolean isInsideParens() {
Paren paren = parenStack.peek();
diff --git a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Groovysh.groovy b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Groovysh.groovy
index 4b5c388..5a73b5b 100644
--- a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Groovysh.groovy
+++ b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/Groovysh.groovy
@@ -23,6 +23,15 @@ import groovy.transform.CompileStatic
import jline.Terminal
import jline.WindowsTerminal
import jline.console.history.FileHistory
+import org.apache.groovy.groovysh.commands.LoadCommand
+import org.apache.groovy.groovysh.commands.RecordCommand
+import org.apache.groovy.groovysh.util.CurlyCountingGroovyLexer
+import org.apache.groovy.groovysh.util.DefaultCommandsRegistrar
+import org.apache.groovy.groovysh.util.PackageHelper
+import org.apache.groovy.groovysh.util.PackageHelperImpl
+import org.apache.groovy.groovysh.util.ScriptVariableAnalyzer
+import org.apache.groovy.groovysh.util.XmlCommandRegistrar
+import org.apache.groovy.parser.antlr4.Antlr4PluginFactory
import org.codehaus.groovy.control.CompilationFailedException
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.ErrorCollector
@@ -30,17 +39,9 @@ import org.codehaus.groovy.control.MultipleCompilationErrorsException
import org.codehaus.groovy.control.messages.Message
import org.codehaus.groovy.runtime.InvokerHelper
import org.codehaus.groovy.runtime.StackTraceUtils
-import org.apache.groovy.groovysh.commands.LoadCommand
-import org.apache.groovy.groovysh.commands.RecordCommand
-import org.apache.groovy.groovysh.util.CurlyCountingGroovyLexer
-import org.apache.groovy.groovysh.util.DefaultCommandsRegistrar
import org.codehaus.groovy.tools.shell.IO
import org.codehaus.groovy.tools.shell.util.MessageSource
-import org.apache.groovy.groovysh.util.PackageHelper
-import org.apache.groovy.groovysh.util.PackageHelperImpl
import org.codehaus.groovy.tools.shell.util.Preferences
-import org.apache.groovy.groovysh.util.ScriptVariableAnalyzer
-import org.apache.groovy.groovysh.util.XmlCommandRegistrar
import org.fusesource.jansi.AnsiRenderer
import java.util.regex.Pattern
@@ -93,15 +94,16 @@ class Groovysh extends Shell {
String evictedLine // remembers the command which will get evicted if history is full
PackageHelper packageHelper
+ private CompilerConfiguration configuration
Groovysh(final ClassLoader classLoader, final Binding binding, final IO io, final Closure registrar) {
- this(classLoader, binding, io, registrar, null)
+ this(classLoader, binding, io, registrar, CompilerConfiguration.DEFAULT)
}
Groovysh(final ClassLoader classLoader, final Binding binding, final IO io, final Closure registrar, CompilerConfiguration configuration) {
- this(classLoader, binding, io, registrar, configuration, new Interpreter(classLoader, binding, configuration))
+ this(classLoader, binding, io, registrar, configuration, new Interpreter(classLoader, binding, configuration))
}
-
+
Groovysh(final ClassLoader classLoader, final Binding binding, final IO io, final Closure registrar, CompilerConfiguration configuration, Interpreter interpreter) {
super(io)
assert classLoader
@@ -111,10 +113,11 @@ class Groovysh extends Shell {
interp = interpreter
actualRegistrar.call(this)
this.packageHelper = new PackageHelperImpl(classLoader)
+ this.configuration = configuration
}
private static Closure createDefaultRegistrar(final ClassLoader classLoader) {
- return {Groovysh shell ->
+ return { Groovysh shell ->
URL xmlCommandResource = getClass().getResource('commands.xml')
if (xmlCommandResource != null) {
def r = new XmlCommandRegistrar(shell, classLoader)
@@ -198,10 +201,10 @@ class Groovysh extends Shell {
if (!Boolean.valueOf(getPreference(INTERPRETER_MODE_PREFERENCE_KEY, 'false')) || isTypeOrMethodDeclaration(current)) {
// Evaluate the current buffer w/imports and dummy statement
- List buff = [importsSpec] + [ 'true' ] + current
+ List buff = [importsSpec] + ['true'] + current
try {
setLastResult(result = interp.evaluate(buff))
- } catch(MultipleCompilationErrorsException t) {
+ } catch (MultipleCompilationErrorsException t) {
// TODO antlr4 parser errors pop out here - can we rework to be like antlr2?
if (t.message.contains('Unexpected input:') || t.message.contains("Missing ')'")) {
// treat like INCOMPLETE case
@@ -214,7 +217,7 @@ class Groovysh extends Shell {
// Evaluate Buffer wrapped with code storing bounded vars
try {
result = evaluateWithStoredBoundVars(importsSpec, current)
- } catch(MultipleCompilationErrorsException t) {
+ } catch (MultipleCompilationErrorsException t) {
// TODO antlr4 parser errors pop out here - can we rework to be like antlr2?
if (t.message.contains('Unexpected input:') || t.message.contains("Missing ')'")) {
// treat like INCOMPLETE case
@@ -258,6 +261,7 @@ class Groovysh extends Shell {
* to simulate an interpreter mode, this method wraps the statements into a try/finally block that
* stores bound variables like unbound variables
*/
+
private Object evaluateWithStoredBoundVars(String importsSpec, final List<String> current) {
Object result
String variableBlocks = null
@@ -292,7 +296,6 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
}
-
protected Object executeCommand(final String line) {
return super.execute(line)
}
@@ -311,7 +314,7 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
}
String getImportStatements() {
- return this.imports.collect({String it -> "import $it;"}).join('')
+ return this.imports.collect({ String it -> "import $it;" }).join('')
}
//
@@ -329,6 +332,7 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
The code will always assume you want the line number in the prompt. To implement differently overhead the render
prompt variable.
*/
+
private String buildPrompt() {
def lineNum = formatLineNumber(buffers.current().size())
@@ -338,7 +342,7 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
}
def groovyshellEnv = System.getenv('GROOVYSH_PROMPT')
if (groovyshellEnv) {
- return "@|bold ${groovyshellEnv}:|@${lineNum}@|bold >|@ "
+ return "@|bold ${groovyshellEnv}:|@${lineNum}@|bold >|@ "
}
return "@|bold groovy:|@${lineNum}@|bold >|@ "
@@ -355,27 +359,36 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
return ''
}
StringBuilder src = new StringBuilder()
- for (String line: buffer) {
+ for (String line : buffer) {
src.append(line).append('\n')
}
- // not sure whether the same Lexer instance could be reused.
- def lexer = CurlyCountingGroovyLexer.createGroovyLexer(src.toString())
+ int curlyLevel
+ boolean antlr4ParserEnabled = configuration.getPluginFactory() instanceof Antlr4PluginFactory
+ if (antlr4ParserEnabled) {
+ def lexer = org.apache.groovy.groovysh.util.antlr4.CurlyCountingGroovyLexer.createGroovyLexer(src.toString())
+ curlyLevel = lexer.countCurlyLevel()
+ } else {
+ // not sure whether the same Lexer instance could be reused.
+ def lexer = CurlyCountingGroovyLexer.createGroovyLexer(src.toString())
- // read all tokens
- try {
- while (lexer.nextToken().getType() != CurlyCountingGroovyLexer.EOF) {}
- } catch (TokenStreamException e) {
- // pass
+ // read all tokens
+ try {
+ while (lexer.nextToken().getType() != CurlyCountingGroovyLexer.EOF) {
+ }
+ } catch (TokenStreamException e) {
+ // pass
+ }
+ curlyLevel = lexer.getParenLevel()
}
- int parenIndent = (lexer.getParenLevel()) * indentSize
+ int parenIndent = curlyLevel * indentSize
// dedent after closing brackets
return ' ' * Math.max(parenIndent, 0)
}
public String renderPrompt() {
- return prompt.render( buildPrompt() )
+ return prompt.render(buildPrompt())
}
/**
@@ -415,7 +428,7 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
// Disable the result hook for profile scripts
def previousHook = resultHook
- resultHook = { result -> /* nothing */}
+ resultHook = { result -> /* nothing */ }
try {
command.load(file.toURI().toURL())
@@ -465,7 +478,7 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
// Hooks
//
- final Closure defaultResultHook = {Object result ->
+ final Closure defaultResultHook = { Object result ->
boolean showLastResult = !io.quiet && (io.verbose || getPreference(SHOW_LAST_RESULT_PREFERENCE_KEY, 'false'))
if (showLastResult) {
// avoid String.valueOf here because it bypasses pretty-printing of Collections,
@@ -481,7 +494,7 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
throw new IllegalStateException('Result hook is not set')
}
- resultHook.call((Object)result)
+ resultHook.call((Object) result)
interp.context['_'] = result
@@ -491,7 +504,7 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
final Closure defaultErrorHook = { Throwable cause ->
assert cause != null
- if (log.debug || ! (cause instanceof CompilationFailedException)) {
+ if (log.debug || !(cause instanceof CompilationFailedException)) {
// For CompilationErrors, the Exception Class is usually not useful to the user
io.err.println("@|bold,red ERROR|@ ${cause.getClass().name}:")
}
@@ -518,8 +531,7 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
if (log.debug) {
// If we have debug enabled then skip the fancy bits below
log.debug(cause)
- }
- else {
+ } else {
boolean sanitize = getPreference(SANITIZE_PREFERENCE_KEY, 'false')
// Sanitize the stack trace unless we are in verbose mode, or the user has request otherwise
@@ -584,8 +596,8 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
}
/**
- * Run Interactive Shell with optional initial script and files to load
- */
+ * Run Interactive Shell with optional initial script and files to load
+ */
int run(final String evalString, final List<String> filenames) {
List<String> startCommands = []
@@ -593,7 +605,7 @@ try {$COLLECTED_BOUND_VARS_MAP_VARNAME[\"$varname\"] = $varname;
startCommands.add(evalString)
}
if (filenames != null && filenames.size() > 0) {
- startCommands.addAll(filenames.collect({String it -> "${LoadCommand.COMMAND_NAME} $it"}))
+ startCommands.addAll(filenames.collect({ String it -> "${LoadCommand.COMMAND_NAME} $it" }))
}
return run(startCommands.join('\n'))
}