Skip to content

Commit

Permalink
[Truffle] Very basic keyword arguments.
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisseaton committed Dec 11, 2014
1 parent c217f32 commit a560b2e
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private static RubyRootNode makeGenericMethod(RubyContext context, MethodDetails
optional = methodDetails.getMethodAnnotation().optional();
}

final Arity arity = new Arity(required, optional, methodDetails.getMethodAnnotation().argumentsAsArray());
final Arity arity = new Arity(required, optional, methodDetails.getMethodAnnotation().argumentsAsArray(), false);

final List<RubyNode> argumentsNodes = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public RubyNilClass attrReader(RubyModule module, Object[] args) {
public static void attrReader(RubyNode currentNode, RubyContext context, SourceSection sourceSection, RubyModule module, String name) {
CompilerDirectives.transferToInterpreter();

final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(0, 0, false));
final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(0, 0, false, false));

final SelfNode self = new SelfNode(context, sourceSection);
final ReadInstanceVariableNode readInstanceVariable = new ReadInstanceVariableNode(context, sourceSection, "@" + name, self, false);
Expand Down Expand Up @@ -313,7 +313,7 @@ public RubyNilClass attrWriter(RubyModule module, Object[] args) {
public static void attrWriter(RubyNode currentNode, RubyContext context, SourceSection sourceSection, RubyModule module, String name) {
CompilerDirectives.transferToInterpreter();

final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(1, 0, false));
final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(1, 0, false, false));

final SelfNode self = new SelfNode(context, sourceSection);
final ReadPreArgumentNode readArgument = new ReadPreArgumentNode(context, sourceSection, 0, MissingArgumentBehaviour.RUNTIME_ERROR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,17 @@ public void executeVoid(VirtualFrame frame) {
}

private boolean checkArity(int given) {
if (arity.getRequired() != 0 && given < arity.getRequired()) {
return false;
if (arity.hasKeywords()) {
given -= 1;
}

if (!arity.allowsMore() && given > arity.getRequired() + arity.getOptional()) {
if (arity.getRequired() != 0 && given < arity.getRequired()) {
return false;
} else if (!arity.allowsMore() && given > arity.getRequired() + arity.getOptional()) {
return false;
} else {
return true;
}

return true;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.methods.arguments;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.BranchProfile;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.RubyValueProfile;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.UndefinedPlaceholder;
import org.jruby.truffle.runtime.core.RubyHash;
import org.jruby.truffle.runtime.core.RubyString;

import java.util.Map;

public class ReadKeywordArgumentNode extends RubyNode {

private final String name;
@Child protected RubyNode defaultValue;

public ReadKeywordArgumentNode(RubyContext context, SourceSection sourceSection, String name, RubyNode defaultValue) {
super(context, sourceSection);
this.name = name;
this.defaultValue = defaultValue;
}

@Override
public Object execute(VirtualFrame frame) {
notDesignedForCompilation();

final int last = RubyArguments.getUserArgumentsCount(frame.getArguments()) - 1;

if (last == -1) {
return defaultValue.execute(frame);
}

final Object hashValue = RubyArguments.getUserArgument(frame.getArguments(), last);

if (!(hashValue instanceof RubyHash)) {
return defaultValue.execute(frame);
}

final RubyHash hash = (RubyHash) hashValue;

Object value = null;

for (Map.Entry<Object, Object> entry : hash.slowToMap().entrySet()) {
if (entry.getKey().toString().equals(name)) {
value = entry.getValue();
break;
}
}

if (value == null) {
return defaultValue.execute(frame);
}

return value;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ public class Arity {
private final int required;
private final int optional;
private final boolean allowsMore;
private final boolean hasKeywords;

public Arity(int required, int optional, boolean allowsMore) {
public Arity(int required, int optional, boolean allowsMore, boolean hasKeywords) {
this.required = required;
this.optional = optional;
this.allowsMore = allowsMore;
this.hasKeywords = hasKeywords;
}

public int getRequired() {
Expand All @@ -33,4 +35,8 @@ public boolean allowsMore() {
return allowsMore;
}

public boolean hasKeywords() {
return hasKeywords;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.jruby.truffle.nodes.control.SequenceNode;
import org.jruby.truffle.nodes.core.ArrayIndexNodeFactory;
import org.jruby.truffle.nodes.core.ArraySliceNodeFactory;
import org.jruby.truffle.nodes.literal.NilLiteralNode;
import org.jruby.truffle.nodes.methods.arguments.*;
import org.jruby.truffle.nodes.methods.locals.ReadLocalVariableNodeFactory;
import org.jruby.truffle.nodes.methods.locals.WriteLocalVariableNodeFactory;
Expand Down Expand Up @@ -91,13 +92,56 @@ public RubyNode visitArgsNode(org.jruby.ast.ArgsNode node) {
}
}

if (node.hasKwargs() && node.getKeywords() != null) {
for (org.jruby.ast.Node arg : node.getKeywords().childNodes()) {
sequence.add(arg.accept(this));
}
}

if (node.getBlock() != null) {
sequence.add(node.getBlock().accept(this));
}

return SequenceNode.sequence(context, sourceSection, sequence);
}

@Override
public RubyNode visitKeywordArgNode(org.jruby.ast.KeywordArgNode node) {
final SourceSection sourceSection = translate(node.getPosition());

final String name;
final RubyNode defaultValue;

final org.jruby.ast.Node firstChild = node.childNodes().get(0);

if (firstChild instanceof org.jruby.ast.LocalAsgnNode) {
final org.jruby.ast.LocalAsgnNode localAsgnNode = (org.jruby.ast.LocalAsgnNode) firstChild;
name = localAsgnNode.getName();

if (localAsgnNode.getValueNode() == null) {
defaultValue = new NilLiteralNode(context, sourceSection);
} else {
defaultValue = localAsgnNode.getValueNode().accept(this);
}
} else if (firstChild instanceof org.jruby.ast.DAsgnNode) {
final org.jruby.ast.DAsgnNode dAsgnNode = (org.jruby.ast.DAsgnNode) firstChild;
name = dAsgnNode.getName();

if (dAsgnNode.getValueNode() == null) {
defaultValue = new NilLiteralNode(context, sourceSection);
} else {
defaultValue = dAsgnNode.getValueNode().accept(this);
}
} else {
throw new UnsupportedOperationException();
}

final RubyNode readNode = new ReadKeywordArgumentNode(context, sourceSection, name, defaultValue);
final FrameSlot slot = methodBodyTranslator.getEnvironment().getFrameDescriptor().findFrameSlot(name);

return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode);
}

@Override
public RubyNode visitArgumentNode(org.jruby.ast.ArgumentNode node) {
final SourceSection sourceSection = translate(node.getPosition());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public MethodDefinitionNode compileFunctionNode(SourceSection sourceSection, Str
*/

if (isBlock && argsNode.childNodes().size() == 2 && argsNode.getRestArgNode() instanceof org.jruby.ast.UnnamedRestArgNode) {
arityForCheck = new Arity(arity.getRequired(), 0, false);
arityForCheck = new Arity(arity.getRequired(), 0, false, false);
} else {
arityForCheck = arity;
}
Expand Down Expand Up @@ -201,7 +201,7 @@ private CallTarget withoutBlockDestructureSemantics(CallTarget callTarget) {
private static Arity getArity(org.jruby.ast.ArgsNode argsNode) {
final int minimum = argsNode.getRequiredArgsCount();
final int maximum = argsNode.getMaxArgumentsCount();
return new Arity(minimum, argsNode.getOptionalArgsCount(), maximum == -1);
return new Arity(minimum, argsNode.getOptionalArgsCount(), maximum == -1, argsNode.hasKwargs());
}

@Override
Expand Down
4 changes: 0 additions & 4 deletions spec/truffle/tags/language/lambda_tags.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> ((*a, b)) { [a, b] }"
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> (a:) { a }"
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> (a: 1) { a }"
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> (**) { }"
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> (**k) { k }"
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> ((a, b, *c, d), (*e, f, g), (*h)) do\n [a, b, c, d, e, f, g, h]\n end"
Expand All @@ -10,12 +9,10 @@ fails:"A lambda literal -> () { } assigns variables from parameters for definiti
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> (a:, b: 1) { [a, b] }"
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> (a: 1, b:) { [a, b] }"
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> (a: @a = -> (a: 1) { a }, b:) do\n [a, b]\n end"
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> (a: 1, b: 2) { [a, b] }"
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> (a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l) do\n [a, b, c, d, e, f, g, h, k, l]\n end"
fails:"A lambda literal -> () { } assigns variables from parameters for definition \n @a = -> a, b=1, *c, d, e:, f: 2, g:, **k, &l do\n [a, b, c, d, e, f, g, k, l]\n end"
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda { |(*a, b)| [a, b] }"
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda { |a:| a }"
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda { |a: 1| a }"
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda { |**| }"
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda { |**k| k }"
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda do |(a, b, *c, d), (*e, f, g), (*h)|\n [a, b, c, d, e, f, g, h]\n end"
Expand All @@ -26,6 +23,5 @@ fails:"A lambda expression 'lambda { ... }' assigns variables from parameters fo
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda { |a:, b: 1| [a, b] }"
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda { |a: 1, b:| [a, b] }"
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda do |a: (@a = -> (a: 1) { a }), b:|\n [a, b]\n end"
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda { |a: 1, b: 2| [a, b] }"
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda do |a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l|\n [a, b, c, d, e, f, g, h, k, l]\n end"
fails:"A lambda expression 'lambda { ... }' assigns variables from parameters for definition \n @a = lambda do |a, b=1, *c, d, e:, f: 2, g:, **k, &l|\n [a, b, c, d, e, f, g, k, l]\n end"
7 changes: 0 additions & 7 deletions spec/truffle/tags/language/method_tags.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ fails:An attribute assignment method send with a middle splatted Object argument
fails:An attribute assignment method send with a trailing splatted Object argument raises a TypeError if #to_a does not return an Array
fails:"A method assigns local variables from method parameters for definition \n def m((*a, b)) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a:) a end"
fails:"A method assigns local variables from method parameters for definition \n def m(a: 1) a end"
fails:"A method assigns local variables from method parameters for definition \n def m(**) end"
fails:"A method assigns local variables from method parameters for definition \n def m(**k) k end"
fails:"A method assigns local variables from method parameters for definition \n def m((*a), (*b)) [a, b] end"
Expand All @@ -43,11 +42,9 @@ fails:"A method assigns local variables from method parameters for definition \n
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, (b, *c), (d, (*e, f)))\n [a, b, c, d, e, f]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, b:) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, b: 2) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, **) a end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, **k) [a, k] end"
fails:"A method assigns local variables from method parameters for definition \n def m(*, a:) a end"
fails:"A method assigns local variables from method parameters for definition \n def m(*a, b:) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(*, a: 1) a end"
fails:"A method assigns local variables from method parameters for definition \n def m(*a, b: 1) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(*, **) end"
fails:"A method assigns local variables from method parameters for definition \n def m(*a, **) a end"
Expand All @@ -60,11 +57,7 @@ fails:"A method assigns local variables from method parameters for definition \n
fails:"A method assigns local variables from method parameters for definition \n def m(a:, &b) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a: 1, b:) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a: def m(a: 1) a end, b:)\n [a, b]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m(a: 1, b: 2) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a: 1, **) a end"
fails:"A method assigns local variables from method parameters for definition \n def m(a: 1, **k) [a, k] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a: 1, &b) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(**, &b) b end"
fails:"A method assigns local variables from method parameters for definition \n def m(**k, &b) [k, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l)\n [a, b, c, d, e, f, g, h, k, l]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m a, b=1, *c, d, e:, f: 2, g:, **k, &l\n [a, b, c, d, e, f, g, k, l]\n end"
4 changes: 2 additions & 2 deletions spec/truffle/tags/language/versions/def_2.0_tags.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ fails:An instance method with keyword arguments treats a sole hash argument corr
fails:An instance method with keyword arguments correctly distinguishes between optional and keyword arguments
fails:An instance method with keyword arguments correctly distinguishes between rest and keyword arguments
fails:An instance method with keyword arguments should allow keyword rest arguments
fails:An instance method with keyword arguments when there is a single keyword argument evaluates to the default when a value isn't provided
fails:An instance method with keyword arguments when there is a single keyword argument evaluates to the provided value
fails:An instance method with keyword arguments when there is a single keyword argument raises an argument error when a non-keyword argument is provided
fails:An instance method with keyword arguments when there is a single keyword argument raises an argument error when an unknown keyword argument is provided

0 comments on commit a560b2e

Please sign in to comment.