Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement message cascade #176

Merged
merged 1 commit into from
Jul 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ the specifications are as follows:

- local variables in methods do not yet support the full slotDeclartion style

- as in SOM method chains are not supported

- as in SOM, blocks can only have 3 arguments (counting `self`)

Obtaining and Running SOMns
Expand Down
57 changes: 57 additions & 0 deletions core-lib/TestSuite/LanguageTests.som
Original file line number Diff line number Diff line change
Expand Up @@ -739,4 +739,61 @@ class LanguageTests usingPlatform: platform testFramework: minitest = Value (
assert: o is: self.
)
) : ( TEST_CONTEXT = () )

public class MessageCascades = TestContext ()(
class Cnt = (| public v ::= 0. |)(
public inc = ( v:: v + 1 )
)

class Setters = (|
public slot1
public slot2
|)(
public runCascadeForEffect = (
self slot1: 23;
slot2: 42
)

public runCascadeForValue = (
^ self slot1;
slot2
)
)

public testCascadeOnFieldRead = (
| c = Cnt new. |
assert: c v equals: 0.
c inc; inc; inc.
assert: c v equals: 3.
)

public testCascadeOnMultipleSends = (
| c = Cnt new. |
assert: c v equals: 0.
c inc inc inc;
inc;
inc;
inc.
assert: c v equals: 6.
)

public testKeywordCascade = (
| arr = Array new: 3. |

arr at: 1 put: 2;
at: 2 put: 3;
at: 3 put: 4.
assert: (arr at: 1) equals: 2.
assert: (arr at: 2) equals: 3.
assert: (arr at: 3) equals: 4.
)

public testSetter = (
| setter = Setters new. |
setter runCascadeForEffect.
assert: setter slot1 equals: 23.
assert: setter slot2 equals: 42.
assert: setter runCascadeForValue equals: 42.
)
) : ( TEST_CONTEXT = () )
)
2 changes: 2 additions & 0 deletions src/som/compiler/Lexer.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ private Symbol doSym() {
} else if (currentChar() == '*' && nextChar() == ')') {
state.incPtr(2);
state.set(Symbol.EndComment, '\0', "*)");
} else if (currentChar() == ';') {
match(Symbol.Semicolon);
} else if (currentChar() == ')') {
match(Symbol.EndTerm);
} else if (currentChar() == '#') {
Expand Down
7 changes: 7 additions & 0 deletions src/som/compiler/MethodBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public final class MethodBuilder {

private final List<SInvokable> embeddedBlockMethods;

private int cascadeId;


public MethodBuilder(final MixinBuilder holder, final MixinScope clsScope) {
this(holder, clsScope, null, false, holder.getLanguage());
Expand Down Expand Up @@ -339,6 +341,11 @@ public void addArgument(final String arg, final SourceSection source) {
arguments.put(arg, argument);
}

public Local addMessageCascadeTemp(final SourceSection source) throws MethodDefinitionError {
cascadeId += 1;
return addLocal("$cascadeTmp" + cascadeId, true, source);
}

public Local addLocal(final String name, final boolean immutable,
final SourceSection source) throws MethodDefinitionError {
if (arguments.containsKey(name)) {
Expand Down
62 changes: 57 additions & 5 deletions src/som/compiler/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import static som.compiler.Symbol.Pound;
import static som.compiler.Symbol.RCurly;
import static som.compiler.Symbol.STString;
import static som.compiler.Symbol.Semicolon;
import static som.compiler.Symbol.SlotMutableAssign;
import static som.compiler.Symbol.Star;
import static som.interpreter.SNodeFactory.createImplicitReceiverSend;
Expand Down Expand Up @@ -408,7 +409,7 @@ private void mixinApplication(final MixinBuilder mxnBuilder, final int mixinId)
if (sym != NewTerm && sym != MixinOperator && sym != Period) {
mixinFactorySend = (AbstractUninitializedMessageSendNode) messages(
mxnBuilder.getInitializerMethodBuilder(),
mxnBuilder.getInitializerMethodBuilder().getSelfRead(getSource(coord)));
new ExpressionNode[] {mxnBuilder.getInitializerMethodBuilder().getSelfRead(getSource(coord))});

uniqueInitName = MixinBuilder.getInitializerName(
mixinFactorySend.getSelector(), mixinId);
Expand Down Expand Up @@ -439,7 +440,7 @@ private void inheritanceClause(final MixinBuilder mxnBuilder)
// used to create the proper initialize method, on which we rely here.
ExpressionNode superFactorySend = messages(
mxnBuilder.getInitializerMethodBuilder(),
mxnBuilder.getInitializerMethodBuilder().getSuperReadNode(getEmptySource()));
new ExpressionNode[] {mxnBuilder.getInitializerMethodBuilder().getSuperReadNode(getEmptySource())});

SSymbol initializerName = MixinBuilder.getInitializerName(
((AbstractUninitializedMessageSendNode) superFactorySend).getSelector());
Expand Down Expand Up @@ -1044,12 +1045,59 @@ private ExpressionNode evaluation(final MethodBuilder builder)
} else {
exp = primary(builder);
}

if (symIsMessageSend()) {
exp = messages(builder, exp);
SourceCoordinate coord = getCoordinate();
ExpressionNode[] lastReceiver = new ExpressionNode[] {exp};

exp = messages(builder, lastReceiver);

if (sym == Semicolon) {
exp = msgCascade(exp, lastReceiver[0], builder, coord);
}
}
return exp;
}

private ExpressionNode msgCascade(final ExpressionNode nonEmptyMessage,
final ExpressionNode lastReceiver, final MethodBuilder builder,
final SourceCoordinate coord) throws ProgramDefinitionError {
List<ExpressionNode> cascade = new ArrayList<>();
SourceSection tmpSource = getSource(coord);

nonEmptyMessage.adoptChildren();

// first, create a temp variable, to which the result of the receiver is
// written, and then replace receiver use with reads from temp
Local tmp = builder.addMessageCascadeTemp(tmpSource);

// evaluate last receiver, and write value to temp
cascade.add(tmp.getWriteNode(0, lastReceiver, tmpSource));

// replace receiver with read from temp
lastReceiver.replace(tmp.getReadNode(0, tmpSource));

// add the initial message of the cascade, it is now send to the temp read
cascade.add(nonEmptyMessage);

while (sym == Semicolon) {
expect(Semicolon, KeywordTag.class);

ExpressionNode exp;
if (sym == Keyword) {
exp = keywordMessage(builder, tmp.getReadNode(0, tmpSource), false, false, null);
} else if (sym == OperatorSequence || symIn(binaryOpSyms)) {
exp = binaryMessage(builder, tmp.getReadNode(0, tmpSource), false, null);
} else {
assert sym == Identifier;
exp = unaryMessage(tmp.getReadNode(0, tmpSource), false, null);
}
cascade.add(exp);
}

return createSequence(cascade, getSource(coord));
}

private boolean symIsMessageSend() {
return sym == Identifier || sym == Keyword || sym == OperatorSequence
|| symIn(binaryOpSyms) || sym == EventualSend;
Expand Down Expand Up @@ -1148,8 +1196,9 @@ protected ExpressionNode binaryConsecutiveMessages(
}

private ExpressionNode messages(final MethodBuilder builder,
final ExpressionNode receiver) throws ProgramDefinitionError {
ExpressionNode msg = receiver;
final ExpressionNode[] lastReceiver) throws ProgramDefinitionError {
ExpressionNode msg = lastReceiver[0];

SourceCoordinate coord = getCoordinate();
boolean eventualSend = accept(EventualSend, KeywordTag.class);

Expand All @@ -1159,6 +1208,7 @@ private ExpressionNode messages(final MethodBuilder builder,
}

while (sym == Identifier) {
lastReceiver[0] = msg;
msg = unaryMessage(msg, eventualSend, sendOp);
eventualSend = accept(EventualSend, KeywordTag.class);
if (eventualSend) {
Expand All @@ -1167,6 +1217,7 @@ private ExpressionNode messages(final MethodBuilder builder,
}

if (sym == OperatorSequence || symIn(binaryOpSyms)) {
lastReceiver[0] = msg;
msg = binaryConsecutiveMessages(builder, msg, eventualSend, sendOp);
eventualSend = accept(EventualSend, KeywordTag.class);
if (eventualSend) {
Expand All @@ -1175,6 +1226,7 @@ private ExpressionNode messages(final MethodBuilder builder,
}

if (sym == Keyword) {
lastReceiver[0] = msg;
msg = keywordMessage(builder, msg, true, eventualSend, sendOp);
}

Expand Down
2 changes: 1 addition & 1 deletion src/som/compiler/Symbol.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

enum Symbol {
NONE, Numeral, Not, And, Or, Star, Div, Mod, Plus, Minus, Equal, More, Less,
Comma, At, Per, NewBlock, EndBlock, LCurly, RCurly, Colon, Period, Exit, NewTerm,
Comma, At, Per, NewBlock, EndBlock, LCurly, RCurly, Colon, Semicolon, Period, Exit, NewTerm,
EndTerm, Pound, STString,
BeginComment, EndComment, SlotMutableAssign, EventualSend, MixinOperator,
Identifier, Keyword, SetterKeyword,
Expand Down