Skip to content

Commit 4a944fc

Browse files
committed
[GR-20373] Implement -n command line feature (#1532).
PullRequest: truffleruby/1221
2 parents 6c34ed2 + 114f7c7 commit 4a944fc

File tree

8 files changed

+60
-6
lines changed

8 files changed

+60
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ Compatibility:
7878
* Implemented `Enumerator::Chain`, `Enumerator#+`, and `Enumerable#chain` (#1859, #1858).
7979
* Implemented `Thread#backtrace_locations` and `Exception#backtrace_locations` (#1556).
8080
* Implemented `rb_module_new`, `rb_define_class_id`, `rb_define_module_id`, (#1876, @chrisseaton).
81+
* Implemented `-n` CLI option (#1532).
8182

8283
Performance:
8384

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
fails:The -n command line option runs the code in loop conditional on Kernel.gets()
2-
fails:The -n command line option only evaluates BEGIN blocks once
3-
fails:The -n command line option only evaluates END blocks once
4-
fails:The -n command line option allows summing over a whole file
1+
slow:The -n command line option runs the code in loop conditional on Kernel.gets()
2+
slow:The -n command line option only evaluates BEGIN blocks once
3+
slow:The -n command line option only evaluates END blocks once
4+
slow:The -n command line option allows summing over a whole file

src/launcher/java/org/truffleruby/launcher/CommandLineParser.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,8 @@ private void processArgument() throws CommandLineException {
305305
throw notImplemented("-l");
306306
case 'n':
307307
disallowedInRubyOpts(argument);
308-
throw notImplemented("-n");
308+
config.setOption(OptionsCatalog.GETS_LOOP, true);
309+
break;
309310
case 'p':
310311
disallowedInRubyOpts(argument);
311312
throw notImplemented("-p");
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 2.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
package org.truffleruby.core.kernel;
11+
12+
import com.oracle.truffle.api.CompilerDirectives;
13+
import com.oracle.truffle.api.frame.VirtualFrame;
14+
import org.truffleruby.language.RubyNode;
15+
import org.truffleruby.language.dispatch.CallDispatchHeadNode;
16+
17+
public class KernelGetsNode extends RubyNode {
18+
19+
@Child private CallDispatchHeadNode callGetsNode;
20+
21+
@Override
22+
public Object execute(VirtualFrame frame) {
23+
if (callGetsNode == null) {
24+
CompilerDirectives.transferToInterpreterAndInvalidate();
25+
callGetsNode = insert(CallDispatchHeadNode.createPrivate());
26+
}
27+
return callGetsNode.dispatch(frame, coreLibrary().kernelModule, "gets", null, EMPTY_ARGUMENTS);
28+
}
29+
30+
}

src/main/java/org/truffleruby/options/Options.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ public class Options {
132132
public final boolean CEXTS_LOG_WARNINGS;
133133
/** --argv-globals=false */
134134
public final boolean ARGV_GLOBALS;
135+
/** --gets-loop=false */
136+
public final boolean GETS_LOOP;
135137
/** --ignore-lines-before-ruby-shebang=false */
136138
public final boolean IGNORE_LINES_BEFORE_RUBY_SHEBANG;
137139
/** --syntax-check=false */
@@ -321,6 +323,7 @@ public Options(Env env, OptionValues options) {
321323
CEXTS_LOG_LOAD = options.get(OptionsCatalog.CEXTS_LOG_LOAD_KEY);
322324
CEXTS_LOG_WARNINGS = options.get(OptionsCatalog.CEXTS_LOG_WARNINGS_KEY);
323325
ARGV_GLOBALS = options.get(OptionsCatalog.ARGV_GLOBALS_KEY);
326+
GETS_LOOP = options.get(OptionsCatalog.GETS_LOOP_KEY);
324327
IGNORE_LINES_BEFORE_RUBY_SHEBANG = options.get(OptionsCatalog.IGNORE_LINES_BEFORE_RUBY_SHEBANG_KEY);
325328
SYNTAX_CHECK = options.get(OptionsCatalog.SYNTAX_CHECK_KEY);
326329
ARGV_GLOBAL_VALUES = options.get(OptionsCatalog.ARGV_GLOBAL_VALUES_KEY);
@@ -501,6 +504,8 @@ public Object fromDescriptor(OptionDescriptor descriptor) {
501504
return CEXTS_LOG_WARNINGS;
502505
case "ruby.argv-globals":
503506
return ARGV_GLOBALS;
507+
case "ruby.gets-loop":
508+
return GETS_LOOP;
504509
case "ruby.ignore-lines-before-ruby-shebang":
505510
return IGNORE_LINES_BEFORE_RUBY_SHEBANG;
506511
case "ruby.syntax-check":

src/main/java/org/truffleruby/parser/TranslatorDriver.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.truffleruby.RubyContext;
4545
import org.truffleruby.aot.ParserCache;
4646
import org.truffleruby.core.LoadRequiredLibrariesNode;
47+
import org.truffleruby.core.kernel.KernelGetsNode;
4748
import org.truffleruby.language.DataNode;
4849
import org.truffleruby.language.LexicalScope;
4950
import org.truffleruby.language.RubyNode;
@@ -55,6 +56,7 @@
5556
import org.truffleruby.language.arguments.ReadPreArgumentNode;
5657
import org.truffleruby.language.arguments.RubyArguments;
5758
import org.truffleruby.language.control.RaiseException;
59+
import org.truffleruby.language.control.WhileNode;
5860
import org.truffleruby.language.exceptions.TopLevelRaiseHandler;
5961
import org.truffleruby.language.locals.WriteLocalVariableNode;
6062
import org.truffleruby.language.methods.Arity;
@@ -275,6 +277,10 @@ public RubyRootNode parse(RubySource rubySource, ParserContext parserContext, St
275277
Arrays.asList(translator.initFlipFlopStates(sourceIndexLength), truffleNode));
276278
}
277279

280+
if (parserContext == ParserContext.TOP_LEVEL_FIRST && context.getOptions().GETS_LOOP) {
281+
truffleNode = new WhileNode(new WhileNode.WhileRepeatingNode(context, new KernelGetsNode(), truffleNode));
282+
}
283+
278284
if (beginNode != null) {
279285
truffleNode = Translator.sequence(
280286
sourceIndexLength,

src/options.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ USER:
66
WORKING_DIRECTORY: [[working-directory, -C], string, '', Interpreter will switch to this directory]
77
DEBUG: [[debug, -d], boolean, false, 'Sets $DEBUG to this value']
88
VERBOSITY: [[verbose, -v, -w, -W], enum/verbosity, false, 'Sets $VERBOSE to this value']
9-
109
SOURCE_ENCODING: [[source-encoding, -K], string, '', Source encoding]
1110
INTERNAL_ENCODING: [[internal-encoding, -E, -U], string, '', Internal encoding]
1211
EXTERNAL_ENCODING: [[external-encoding, -E], string, '', External encoding]
@@ -81,6 +80,7 @@ INTERNAL: # Options for debugging the TruffleRuby implementation
8180
EXPERIMENTAL:
8281
# Options corresponding to MRI options, which are only meaningful when using the TruffleRuby launcher
8382
ARGV_GLOBALS: [[argv-globals, -s], boolean, false, Parse options in script argv into global variables]
83+
GETS_LOOP: [[gets-loop, -n], boolean, false, 'assume while gets(); ... end loop around your script']
8484
IGNORE_LINES_BEFORE_RUBY_SHEBANG: [[ignore-lines-before-ruby-shebang, -x], boolean, false, "strip off text before #!ruby line"]
8585
SYNTAX_CHECK: [[syntax-check, -c], boolean, false, Do not execute just check syntax]
8686

src/shared/java/org/truffleruby/shared/options/OptionsCatalog.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public class OptionsCatalog {
7575
public static final OptionKey<Boolean> CEXTS_LOG_LOAD_KEY = new OptionKey<>(false);
7676
public static final OptionKey<Boolean> CEXTS_LOG_WARNINGS_KEY = new OptionKey<>(false);
7777
public static final OptionKey<Boolean> ARGV_GLOBALS_KEY = new OptionKey<>(false);
78+
public static final OptionKey<Boolean> GETS_LOOP_KEY = new OptionKey<>(false);
7879
public static final OptionKey<Boolean> IGNORE_LINES_BEFORE_RUBY_SHEBANG_KEY = new OptionKey<>(false);
7980
public static final OptionKey<Boolean> SYNTAX_CHECK_KEY = new OptionKey<>(false);
8081
public static final OptionKey<String[]> ARGV_GLOBAL_VALUES_KEY = new OptionKey<>(new String[]{}, StringArrayOptionType.INSTANCE);
@@ -527,6 +528,13 @@ public class OptionsCatalog {
527528
.stability(OptionStability.EXPERIMENTAL)
528529
.build();
529530

531+
public static final OptionDescriptor GETS_LOOP = OptionDescriptor
532+
.newBuilder(GETS_LOOP_KEY, "ruby.gets-loop")
533+
.help("assume while gets(); ... end loop around your script (configured by the -n Ruby option)")
534+
.category(OptionCategory.INTERNAL)
535+
.stability(OptionStability.EXPERIMENTAL)
536+
.build();
537+
530538
public static final OptionDescriptor IGNORE_LINES_BEFORE_RUBY_SHEBANG = OptionDescriptor
531539
.newBuilder(IGNORE_LINES_BEFORE_RUBY_SHEBANG_KEY, "ruby.ignore-lines-before-ruby-shebang")
532540
.help("strip off text before #!ruby line (configured by the -x Ruby option)")
@@ -1101,6 +1109,8 @@ public static OptionDescriptor fromName(String name) {
11011109
return CEXTS_LOG_WARNINGS;
11021110
case "ruby.argv-globals":
11031111
return ARGV_GLOBALS;
1112+
case "ruby.gets-loop":
1113+
return GETS_LOOP;
11041114
case "ruby.ignore-lines-before-ruby-shebang":
11051115
return IGNORE_LINES_BEFORE_RUBY_SHEBANG;
11061116
case "ruby.syntax-check":
@@ -1295,6 +1305,7 @@ public static OptionDescriptor[] allDescriptors() {
12951305
CEXTS_LOG_LOAD,
12961306
CEXTS_LOG_WARNINGS,
12971307
ARGV_GLOBALS,
1308+
GETS_LOOP,
12981309
IGNORE_LINES_BEFORE_RUBY_SHEBANG,
12991310
SYNTAX_CHECK,
13001311
ARGV_GLOBAL_VALUES,

0 commit comments

Comments
 (0)