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

Add Newspeak type annotation support and local variable initializer expressions #175

Merged
merged 8 commits into from
Jul 26, 2017
30 changes: 30 additions & 0 deletions core-lib/TestSuite/LanguageTests.som
Original file line number Diff line number Diff line change
Expand Up @@ -709,4 +709,34 @@ class LanguageTests usingPlatform: platform testFramework: minitest = Value (
self assert: 42 equals: (a at: i) aNumber ]
)
) : ( TEST_CONTEXT = () )

public class InitializerForLocalVariables = TestContext ()(
public testSimpleInitializers = (
| cnt ::= 0.
inc = 1. |

assert: cnt equals: 0.
assert: inc equals: 1.

cnt:: cnt + inc.
cnt:: cnt + inc.

assert: cnt equals: 2.
assert: inc equals: 1.
)

private myself = ( ^ self )
private ret: o = ( ^ o )

public testMoreComplexExpressions = (
| zero ::= 4 - 8 + 4.
obj = myself.
o = ret: obj. |

assert: zero equals: 0.
assert: obj is: self.

assert: o is: self.
)
) : ( TEST_CONTEXT = () )
)
8 changes: 5 additions & 3 deletions src/som/compiler/Lexer.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ int incPtr(final int val) {
}
}

private final String content;
protected final String content;

private boolean peekDone;
private LexerState state;
Expand Down Expand Up @@ -194,7 +194,7 @@ private Symbol doSym() {
if (isDigit(nextChar())) {
lexNumber();
} else {
match(Symbol.Minus);
lexOperator();
}
} else if (currentChar() == '<') {
state.incPtr();
Expand All @@ -213,7 +213,7 @@ private Symbol doSym() {
}
} else if (isOperator(currentChar())) {
lexOperator();
} else if (Character.isLetter(currentChar())) {
} else if (Character.isLetter(currentChar()) || currentChar() == '_') {
state.set(Symbol.Identifier);
while (isIdentifierChar(currentChar())) {
state.text.append(bufchar(state.incPtr()));
Expand Down Expand Up @@ -325,6 +325,8 @@ private void lexOperator() {
match(Symbol.At);
} else if (currentChar() == '%') {
match(Symbol.Per);
} else if (currentChar() == '-') {
match(Symbol.Minus);
}
}

Expand Down
17 changes: 12 additions & 5 deletions src/som/compiler/MethodBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@
import som.compiler.MixinBuilder.MixinDefinitionId;
import som.compiler.ProgramDefinitionError.SemanticDefinitionError;
import som.compiler.Variable.Argument;
import som.compiler.Variable.ImmutableLocal;
import som.compiler.Variable.Internal;
import som.compiler.Variable.Local;
import som.compiler.Variable.MutableLocal;
import som.interpreter.InliningVisitor;
import som.interpreter.LexicalScope.MethodScope;
import som.interpreter.LexicalScope.MixinScope;
Expand Down Expand Up @@ -337,21 +339,26 @@ public void addArgument(final String arg, final SourceSection source) {
arguments.put(arg, argument);
}

public Local addLocal(final String name, final SourceSection source)
throws MethodDefinitionError {
public Local addLocal(final String name, final boolean immutable,
final SourceSection source) throws MethodDefinitionError {
if (arguments.containsKey(name)) {
throw new MethodDefinitionError("Method already defines argument " + name + ". Can't define local variable with same name.", source);
}

Local l = new Local(name, source);
Local l;
if (immutable) {
l = new ImmutableLocal(name, source);
} else {
l = new MutableLocal(name, source);
}
l.init(currentScope.getFrameDescriptor().addFrameSlot(l));
locals.put(name, l);
return l;
}

public Local addLocalAndUpdateScope(final String name,
public Local addLocalAndUpdateScope(final String name, final boolean immutable,
final SourceSection source) throws MethodDefinitionError {
Local l = addLocal(name, source);
Local l = addLocal(name, immutable, source);
currentScope.addVariable(l);
return l;
}
Expand Down
10 changes: 9 additions & 1 deletion src/som/compiler/NumeralParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import java.math.BigInteger;

import som.VM;


public class NumeralParser {

Expand Down Expand Up @@ -88,7 +90,13 @@ private long calculateNumberWithRadixLong() {
v = c - 'A' + 10 /* A has value 10 */;
}

result = Math.addExact(result, (long) (Math.pow(r, length - i - 1) * v));
try {
result = Math.addExact(result, (long) (Math.pow(r, length - i - 1) * v));
} catch (ArithmeticException e) {
// TODO: need to overflow into BigInteger
VM.errorPrintln("Warning: Parsed Integer literal which did not fit into long. " + lexer.getCurrentLineNumber() + ":" + lexer.getCurrentColumn());
return result;
}
}
return result;
}
Expand Down
105 changes: 89 additions & 16 deletions src/som/compiler/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,12 @@

public class Parser {

private final Lexer lexer;
protected final Lexer lexer;
private final Source source;

private final SomLanguage language;

private Symbol sym;
protected Symbol sym;
private String text;
private Symbol nextSym;
private String nextText;
Expand Down Expand Up @@ -288,7 +288,16 @@ public SourceCoordinate getCoordinate() {
return lexer.getStartCoordinate();
}

private void compatibilityNewspeakVersionAndFileCategory() throws ParseError {
if (sym == Identifier && "Newspeak3".equals(text)) {
expect(Identifier, KeywordTag.class);
expect(STString, LiteralTag.class);
}
}

public MixinBuilder moduleDeclaration() throws ProgramDefinitionError {
compatibilityNewspeakVersionAndFileCategory();

comments();
return classDeclaration(null, AccessModifier.PUBLIC);
}
Expand Down Expand Up @@ -579,6 +588,8 @@ private void slotDefinition(final MixinBuilder mxnBuilder)
SourceCoordinate coord = getCoordinate();
AccessModifier acccessModifier = accessModifier();

comments();

String slotName = slotDecl();
boolean immutable;
ExpressionNode init;
Expand Down Expand Up @@ -609,7 +620,10 @@ private AccessModifier accessModifier() {
}

private String slotDecl() throws ParseError {
return identifier();
String id = identifier();

new TypeParser(this).parseType();
return id;
}

private void initExprs(final MixinBuilder mxnBuilder) throws ProgramDefinitionError {
Expand Down Expand Up @@ -726,7 +740,7 @@ private void expect(final Symbol s, final String msg,
throw new ParseError(msg, s, this);
}

private void expect(final Symbol s, final Class<? extends Tags> tag) throws ParseError {
protected void expect(final Symbol s, final Class<? extends Tags> tag) throws ParseError {
expect(s, "Unexpected symbol. Expected %(expected)s, but found %(found)s", tag);
}

Expand Down Expand Up @@ -784,6 +798,8 @@ private void messagePattern(final MethodBuilder builder) throws ParseError {
binaryPattern(builder);
break;
}

new TypeParser(this).parseReturnType();
}

protected void unaryPattern(final MethodBuilder builder) throws ParseError {
Expand Down Expand Up @@ -873,34 +889,74 @@ protected String setterKeyword() throws ParseError {
private String argument() throws ParseError {
SourceCoordinate coord = getCoordinate();
String id = identifier();

new TypeParser(this).parseType();

language.getVM().reportSyntaxElement(ArgumentTag.class, getSource(coord));
return id;
}

private ExpressionNode blockContents(final MethodBuilder builder)
throws ProgramDefinitionError {
comments();
List<ExpressionNode> expressions = new ArrayList<ExpressionNode>();

if (accept(Or, DelimiterOpeningTag.class)) {
locals(builder);
locals(builder, expressions);
expect(Or, DelimiterClosingTag.class);
}
builder.setVarsOnMethodScope();
return blockBody(builder);
return blockBody(builder, expressions);
}

private void locals(final MethodBuilder builder) throws ParseError, MethodDefinitionError {
while (sym == Identifier) {
SourceCoordinate coord = getCoordinate();
String id = identifier();
SourceSection source = getSource(coord);
builder.addLocal(id, source);
language.getVM().reportSyntaxElement(LocalVariableTag.class, source);
private void locals(final MethodBuilder builder,
final List<ExpressionNode> expressions) throws ProgramDefinitionError {
// Newspeak-speak: we do not support simSlotDecls, i.e.,
// simultaneous slots clauses (spec 6.3.2)
while (sym != Or) {
localDefinition(builder, expressions);
}
}

private ExpressionNode blockBody(final MethodBuilder builder) throws ProgramDefinitionError {
private void localDefinition(final MethodBuilder builder,
final List<ExpressionNode> expressions) throws ProgramDefinitionError {
comments();
if (sym == Or) { return; }

SourceCoordinate coord = getCoordinate();
String slotName = slotDecl();
SourceSection source = getSource(coord);

language.getVM().reportSyntaxElement(LocalVariableTag.class, source);

boolean immutable;
ExpressionNode initializer;

if (accept(Equal, KeywordTag.class)) {
immutable = true;
initializer = expression(builder);
expect(Period, StatementSeparatorTag.class);
} else if (accept(SlotMutableAssign, KeywordTag.class)) {
immutable = false;
initializer = expression(builder);
expect(Period, StatementSeparatorTag.class);
} else {
immutable = false;
initializer = null;
}

Local local = builder.addLocal(slotName, immutable, source);

if (initializer != null) {
SourceSection write = getSource(coord);
ExpressionNode writeNode = local.getWriteNode(0, initializer, write);
expressions.add(writeNode);
}
}

private ExpressionNode blockBody(final MethodBuilder builder,
final List<ExpressionNode> expressions) throws ProgramDefinitionError {
SourceCoordinate coord = getCoordinate();
List<ExpressionNode> expressions = new ArrayList<ExpressionNode>();

boolean sawPeriod = true;

Expand Down Expand Up @@ -1006,19 +1062,27 @@ private ExpressionNode primary(final MethodBuilder builder) throws ProgramDefini
// Parse true, false, and nil as keyword-like constructs
// (cf. Newspeak spec on reserved words)
if (acceptIdentifier("true", LiteralTag.class)) {
comments();
return new TrueLiteralNode(getSource(coord));
}

if (acceptIdentifier("false", LiteralTag.class)) {
comments();
return new FalseLiteralNode(getSource(coord));
}

if (acceptIdentifier("nil", LiteralTag.class)) {
comments();
return new NilLiteralNode(getSource(coord));
}
if ("outer".equals(text)) {
return outerSend(builder);
}

SSymbol selector = unarySelector();

comments();

return builder.getImplicitReceiverSend(selector, getSource(coord));
}
case NewTerm: {
Expand Down Expand Up @@ -1059,8 +1123,12 @@ private ExpressionNode outerSend(final MethodBuilder builder)
expectIdentifier("outer", KeywordTag.class);
String outer = identifier();

comments();

ExpressionNode operand = builder.getOuterRead(outer, getSource(coord));
operand = binaryConsecutiveMessages(builder, operand, false, null);

comments();
return operand;
}

Expand Down Expand Up @@ -1117,6 +1185,8 @@ protected ExpressionNode unaryMessage(final ExpressionNode receiver,
final boolean eventualSend, final SourceSection sendOperator) throws ParseError {
SourceCoordinate coord = getCoordinate();
SSymbol selector = unarySelector();

comments();
return createMessageSend(selector, new ExpressionNode[] {receiver},
eventualSend, getSource(coord), sendOperator, language);
}
Expand Down Expand Up @@ -1187,7 +1257,10 @@ protected ExpressionNode keywordMessage(final MethodBuilder builder,

do {
kw.append(keyword());
comments();

arguments.add(formula(builder));
comments();
}
while (sym == Keyword);

Expand Down Expand Up @@ -1314,7 +1387,7 @@ private Local getLoopIdx(final MethodBuilder builder,
// if it is a literal, we still need a memory location for counting, so,
// add a synthetic local
loopIdx = builder.addLocalAndUpdateScope(
"!i" + SourceCoordinate.getLocationQualifier(source), source);
"!i" + SourceCoordinate.getLocationQualifier(source), false, source);
}
return loopIdx;
}
Expand Down
Loading