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

Comma-list declarations issue 2856 #2975

Merged
merged 5 commits into from
Aug 1, 2021
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
17 changes: 17 additions & 0 deletions doc/source/language/variables.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ Any variable declared with ``DECLARE``, ``DECLARE LOCAL``, or ``LOCAL``
will only exist inside the code block section it was created in.
After that code block is finished, the variable will no longer exist.

It is also possible to declare multiple variables in a single ``DECLARE`` statement,
separated by commas, as shown below::

// These all do the exact same thing - make local variables:
DECLARE A IS 5, B TO 1, C TO "O".
LOCAL A IS 5, B TO 1, C TO "O".
DECLARE LOCAL A IS 5, B TO 1, C TO "O".

// These do the exact same thing - make global variables:
GLOBAL A IS 5, B TO 1, C TO "O".
DECLARE GLOBAL A IS 5, B TO 1, C TO "O".

See Scoping:
::::::::::::

Expand Down Expand Up @@ -268,6 +280,11 @@ This follows the :ref:`scoping rules explained below <scope>`. If the
variable can be found in the current local scope, or any scope higher
up, then it won't be created and instead the existing one will be used.

It is also possible to set the values of multiple variables in a single ``SET`` statement
by separating the assignments with commas, as shown below::

SET X TO 1, Y TO 5, S TO "abc".

.. _unset:

``UNSET``
Expand Down
30 changes: 30 additions & 0 deletions kerboscript_tests/declaration/multi_declaration_test.ks
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Tests comma-separated declarations of variables

print "Testing with 'SET'".
set a to 1, b to 2, c to "a".

print " 1. The first variable has the correct value: " + (a = 1).
print " 2. The second variable has the correct value: " + (b = 2).
print " 3. The third variable has the correct value: " + (c = "a").

print "Testing with 'GLOBAL'".
global d is 5, e to 2.

print " 1. The first variable has the correct value: " + (d = 5).
print " 2. The second variable has the correct value: " + (e = 2).

print "Testing with 'DECLARE'".

declare f is 12, g to 1.

print " 1. The first variable has the correct value: " + (f = 12).
print " 2. The second variable has the correct value: " + (g = 1).


print "Testing with 'DECLARE GLOBAL'".

declare h is 7, i to 15, j to 3.

print " 1. The first variable has the correct value: " + (h = 7).
print " 2. The second variable has the correct value: " + (i = 15).
print " 3. The thrid variable has the correct value: " + (j = 3).
12 changes: 9 additions & 3 deletions src/kOS.Safe/Compilation/KS/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2175,7 +2175,11 @@ private ParseNode DepthFirstLeftSearch(ParseNode node, TokenType tokType)
private void VisitSetStatement(ParseNode node)
{
NodeStartHousekeeping(node);
ProcessSetOperation(node.Nodes[1], node.Nodes[3]);

for (int i = 1; i < node.Nodes.Count; i += 4)
{
ProcessSetOperation(node.Nodes[i], node.Nodes[i + 2]);
}
}

/// <summary>
Expand Down Expand Up @@ -2687,8 +2691,10 @@ private void VisitDeclareStatement(ParseNode node)
// DECLARE [GLOBAL|LOCAL] identifier TO expr.
if (lastSubNode.Token.Type == TokenType.declare_identifier_clause)
{
VisitNode(lastSubNode.Nodes[2]);
AddOpcode(CreateAppropriateStoreCode(whereToStore, true, "$" + GetIdentifierText(lastSubNode.Nodes[0])));
for (int i = 0; i < lastSubNode.Nodes.Count; i += 4) {
VisitNode(lastSubNode.Nodes[i + 2]);
AddOpcode(CreateAppropriateStoreCode(whereToStore, true, "$" + GetIdentifierText(lastSubNode.Nodes[i])));
}
}

// If the declare statement is of the form:
Expand Down
92 changes: 92 additions & 0 deletions src/kOS.Safe/Compilation/KS/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,39 @@ private void Parseset_stmt(ParseNode parent) // NonTerminalSymbol: set_stmt
// Concat Rule
Parseexpr(node); // NonTerminal Rule: expr

// Concat Rule
tok = scanner.LookAhead(TokenType.COMMA); // ZeroOrMore Rule
while (tok.Type == TokenType.COMMA)
{

// Concat Rule
tok = scanner.Scan(TokenType.COMMA); // Terminal Rule: COMMA
n = node.CreateNode(tok, tok.ToString() );
node.Token.UpdateRange(tok);
node.Nodes.Add(n);
if (tok.Type != TokenType.COMMA) {
tree.Errors.Add(new ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.COMMA.ToString(), 0x1001, tok));
return;
}

// Concat Rule
Parsevaridentifier(node); // NonTerminal Rule: varidentifier

// Concat Rule
tok = scanner.Scan(TokenType.TO); // Terminal Rule: TO
n = node.CreateNode(tok, tok.ToString() );
node.Token.UpdateRange(tok);
node.Nodes.Add(n);
if (tok.Type != TokenType.TO) {
tree.Errors.Add(new ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.TO.ToString(), 0x1001, tok));
return;
}

// Concat Rule
Parseexpr(node); // NonTerminal Rule: expr
tok = scanner.LookAhead(TokenType.COMMA); // ZeroOrMore Rule
}

// Concat Rule
tok = scanner.Scan(TokenType.EOI); // Terminal Rule: EOI
n = node.CreateNode(tok, tok.ToString() );
Expand Down Expand Up @@ -1332,6 +1365,65 @@ private void Parsedeclare_identifier_clause(ParseNode parent) // NonTerminalSymb
// Concat Rule
Parseexpr(node); // NonTerminal Rule: expr

// Concat Rule
tok = scanner.LookAhead(TokenType.COMMA); // ZeroOrMore Rule
while (tok.Type == TokenType.COMMA)
{

// Concat Rule
tok = scanner.Scan(TokenType.COMMA); // Terminal Rule: COMMA
n = node.CreateNode(tok, tok.ToString() );
node.Token.UpdateRange(tok);
node.Nodes.Add(n);
if (tok.Type != TokenType.COMMA) {
tree.Errors.Add(new ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.COMMA.ToString(), 0x1001, tok));
return;
}

// Concat Rule
tok = scanner.Scan(TokenType.IDENTIFIER); // Terminal Rule: IDENTIFIER
n = node.CreateNode(tok, tok.ToString() );
node.Token.UpdateRange(tok);
node.Nodes.Add(n);
if (tok.Type != TokenType.IDENTIFIER) {
tree.Errors.Add(new ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.IDENTIFIER.ToString(), 0x1001, tok));
return;
}

// Concat Rule
tok = scanner.LookAhead(TokenType.TO, TokenType.IS); // Choice Rule
switch (tok.Type)
{ // Choice Rule
case TokenType.TO:
tok = scanner.Scan(TokenType.TO); // Terminal Rule: TO
n = node.CreateNode(tok, tok.ToString() );
node.Token.UpdateRange(tok);
node.Nodes.Add(n);
if (tok.Type != TokenType.TO) {
tree.Errors.Add(new ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.TO.ToString(), 0x1001, tok));
return;
}
break;
case TokenType.IS:
tok = scanner.Scan(TokenType.IS); // Terminal Rule: IS
n = node.CreateNode(tok, tok.ToString() );
node.Token.UpdateRange(tok);
node.Nodes.Add(n);
if (tok.Type != TokenType.IS) {
tree.Errors.Add(new ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.IS.ToString(), 0x1001, tok));
return;
}
break;
default:
tree.Errors.Add(new ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected TO or IS.", 0x0002, tok));
break;
} // Choice Rule

// Concat Rule
Parseexpr(node); // NonTerminal Rule: expr
tok = scanner.LookAhead(TokenType.COMMA); // ZeroOrMore Rule
}

// Concat Rule
tok = scanner.Scan(TokenType.EOI); // Terminal Rule: EOI
n = node.CreateNode(tok, tok.ToString() );
Expand Down
4 changes: 2 additions & 2 deletions src/kOS.Safe/Compilation/KS/kRISC.tpg
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ directive -> lazyglobal_directive; // Add to this list later if we ma
// ------------ statements --------------------

empty_stmt -> EOI;
set_stmt -> SET varidentifier TO expr EOI;
set_stmt -> SET varidentifier TO expr (COMMA varidentifier TO expr)* EOI;
if_stmt -> IF expr instruction EOI? (ELSE instruction EOI?)?;
until_stmt -> UNTIL expr instruction EOI?;
fromloop_stmt -> FROM instruction_block UNTIL expr STEP instruction_block DO instruction EOI?;
Expand All @@ -172,7 +172,7 @@ remove_stmt -> REMOVE expr EOI;
log_stmt -> LOG expr TO expr EOI;
break_stmt -> BREAK EOI;
preserve_stmt -> PRESERVE EOI;
declare_identifier_clause -> IDENTIFIER (TO|IS) expr EOI;
declare_identifier_clause -> IDENTIFIER (TO|IS) expr (COMMA IDENTIFIER (TO|IS) expr)* EOI;
declare_parameter_clause -> PARAMETER IDENTIFIER ((TO|IS) expr)? (COMMA IDENTIFIER ((TO|IS) expr)?)* EOI;
declare_function_clause -> FUNCTION IDENTIFIER instruction_block EOI?;
declare_lock_clause -> LOCK IDENTIFIER TO expr EOI;
Expand Down