Skip to content

Commit f696d45

Browse files
committed
Merge pull request moonsharp-devs#323 from Joy-less/compound-assignment
2 parents 57bec5b + cbe1820 commit f696d45

3 files changed

Lines changed: 62 additions & 47 deletions

File tree

src/MoonSharp.Interpreter/Tree/Lexer/Lexer.cs

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -154,30 +154,17 @@ private Token ReadToken()
154154
{
155155
case '|':
156156
CursorCharNext();
157-
return CreateToken(TokenType.Op_BitwiseOr_Or_Lambda, fromLine, fromCol, "|");
157+
return CreateToken(TokenType.Lambda, fromLine, fromCol, "|");
158158
case ';':
159159
CursorCharNext();
160160
return CreateToken(TokenType.SemiColon, fromLine, fromCol, ";");
161161
case '=':
162162
return PotentiallyDoubleCharOperator('=', TokenType.Op_Assignment, TokenType.Op_Equal, fromLine, fromCol);
163-
case '&':
164-
CursorCharNext();
165-
return CreateToken(TokenType.Op_BitwiseAnd, fromLine, fromCol, "&");
166163
case '<':
167-
return PotentiallyDoubleCharOperator('=', '<', TokenType.Op_LessThan, TokenType.Op_LessThanEqual, TokenType.Op_BitwiseLShift, fromLine, fromCol);
164+
return PotentiallyDoubleCharOperator('=', TokenType.Op_LessThan, TokenType.Op_LessThanEqual, fromLine, fromCol);
168165
case '>':
169-
return PotentiallyDoubleCharOperator('=', '>', TokenType.Op_GreaterThan, TokenType.Op_GreaterThanEqual, TokenType.Op_BitwiseRShift, fromLine, fromCol);
166+
return PotentiallyDoubleCharOperator('=', TokenType.Op_GreaterThan, TokenType.Op_GreaterThanEqual, fromLine, fromCol);
170167
case '~':
171-
if (CursorCharNext() != '=')
172-
{
173-
CursorCharNext();
174-
return CreateToken(TokenType.Op_BitwiseXor_Or_BitwiseNot, fromLine, fromCol, "~");
175-
}
176-
else
177-
{
178-
CursorCharNext();
179-
return CreateToken(TokenType.Op_NotEqual, fromLine, fromCol, "~=");
180-
}
181168
case '!':
182169
if (CursorCharNext() != '=')
183170
throw new SyntaxErrorException(CreateToken(TokenType.Invalid, fromLine, fromCol), "unexpected symbol near '{0}'", c);
@@ -188,34 +175,39 @@ private Token ReadToken()
188175
{
189176
char next = CursorCharNext();
190177
if (next == '.')
191-
return PotentiallyDoubleCharOperator('.', TokenType.Op_Concat, TokenType.VarArgs, fromLine, fromCol);
178+
return PotentiallyDoubleCharOperator(TokenType.Op_Concat, '.', TokenType.VarArgs, "...", '=', TokenType.Op_Assignment, "..=", fromLine, fromCol);
192179
else if (LexerUtils.CharIsDigit(next))
193180
return ReadNumberToken(fromLine, fromCol, true);
194181
else
195182
return CreateToken(TokenType.Dot, fromLine, fromCol, ".");
196183
}
197184
case '+':
198-
return CreateSingleCharToken(TokenType.Op_Add, fromLine, fromCol);
185+
return PotentiallyDoubleCharOperator('=', TokenType.Op_Add, TokenType.Op_Assignment, fromLine, fromCol);
199186
case '-':
200187
{
201188
char next = CursorCharNext();
202189
if (next == '-')
203190
{
204191
return ReadComment(fromLine, fromCol);
205192
}
193+
else if (next == '=')
194+
{
195+
CursorCharNext();
196+
return CreateToken(TokenType.Op_Assignment, fromLine, fromCol, "-=");
197+
}
206198
else
207199
{
208200
return CreateToken(TokenType.Op_MinusOrSub, fromLine, fromCol, "-");
209201
}
210202
}
211203
case '*':
212-
return CreateSingleCharToken(TokenType.Op_Mul, fromLine, fromCol);
204+
return PotentiallyDoubleCharOperator('=', TokenType.Op_Mul, TokenType.Op_Assignment, fromLine, fromCol);
213205
case '/':
214-
return PotentiallyDoubleCharOperator('/', TokenType.Op_Div, TokenType.Op_FloorDiv, fromLine, fromCol);
206+
return PotentiallyDoubleCharOperator('=', TokenType.Op_Div, TokenType.Op_Assignment, fromLine, fromCol);
215207
case '%':
216-
return CreateSingleCharToken(TokenType.Op_Mod, fromLine, fromCol);
208+
return PotentiallyDoubleCharOperator('=', TokenType.Op_Mod, TokenType.Op_Assignment, fromLine, fromCol);
217209
case '^':
218-
return CreateSingleCharToken(TokenType.Op_Pwr, fromLine, fromCol);
210+
return PotentiallyDoubleCharOperator('=', TokenType.Op_Pwr, TokenType.Op_Assignment, fromLine, fromCol);
219211
case '$':
220212
return PotentiallyDoubleCharOperator('{', TokenType.Op_Dollar, TokenType.Brk_Open_Curly_Shared, fromLine, fromCol);
221213
case '#':
@@ -553,31 +545,26 @@ private Token PotentiallyDoubleCharOperator(char expectedSecondChar, TokenType s
553545
else
554546
return CreateToken(singleCharToken, fromLine, fromCol, op);
555547
}
556-
557-
private Token PotentiallyDoubleCharOperator(char expectedSecondChar, char expectedSecondChar2, TokenType singleCharToken, TokenType doubleCharToken, TokenType doubleCharToken2, int fromLine, int fromCol)
548+
private Token PotentiallyDoubleCharOperator(TokenType singleCharToken, char expectedSecondChar, TokenType doubleCharToken, string doubleCharText, char alternateExpectedSecondChar, TokenType alternateDoubleCharToken, string alternateDoubleCharText, int fromLine, int fromCol)
558549
{
559550
string op = CursorChar().ToString();
560551

561552
CursorCharNext();
562553

563-
var ch = CursorChar();
564-
565-
if (ch == expectedSecondChar)
554+
if (CursorChar() == expectedSecondChar)
566555
{
567556
CursorCharNext();
568-
return CreateToken(doubleCharToken, fromLine, fromCol, op + expectedSecondChar);
557+
return CreateToken(doubleCharToken, fromLine, fromCol, doubleCharText);
569558
}
570-
else if (ch == expectedSecondChar2)
559+
else if (CursorChar() == alternateExpectedSecondChar)
571560
{
572561
CursorCharNext();
573-
return CreateToken(doubleCharToken2, fromLine, fromCol, op + expectedSecondChar2);
562+
return CreateToken(alternateDoubleCharToken, fromLine, fromCol, alternateDoubleCharText);
574563
}
575564
else
576565
return CreateToken(singleCharToken, fromLine, fromCol, op);
577566
}
578567

579-
580-
581568
private Token CreateNameToken(string name, int fromLine, int fromCol)
582569
{
583570
TokenType? reservedType = Token.GetReservedTokenType(name);
@@ -592,7 +579,6 @@ private Token CreateNameToken(string name, int fromLine, int fromCol)
592579
}
593580
}
594581

595-
596582
private Token CreateToken(TokenType tokenType, int fromLine, int fromCol, string text = null)
597583
{
598584
Token t = new Token(tokenType, m_SourceId, fromLine, fromCol, m_Line, m_Col, m_PrevLineTo, m_PrevColTo)
@@ -618,9 +604,5 @@ private string ReadNameToken()
618604

619605
return name.ToString();
620606
}
621-
622-
623-
624-
625607
}
626608
}

src/MoonSharp.Interpreter/Tree/Statement.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ protected static Statement CreateStatement(ScriptLoadingContext lcontext, out bo
3939
case TokenType.Function:
4040
return new FunctionDefinitionStatement(lcontext, false, null);
4141
case TokenType.Local:
42-
Token localToken = lcontext.Lexer.Current;
42+
Token localToken = tkn;
4343
lcontext.Lexer.Next();
4444
if (lcontext.Lexer.Current.Type == TokenType.Function)
4545
return new FunctionDefinitionStatement(lcontext, true, localToken);
@@ -52,14 +52,12 @@ protected static Statement CreateStatement(ScriptLoadingContext lcontext, out bo
5252
return new BreakStatement(lcontext);
5353
default:
5454
{
55-
Token l = lcontext.Lexer.Current;
5655
Expression exp = Expression.PrimaryExp(lcontext);
57-
FunctionCallExpression fnexp = exp as FunctionCallExpression;
5856

59-
if (fnexp != null)
57+
if (exp is FunctionCallExpression fnexp)
6058
return new FunctionCallStatement(lcontext, fnexp);
6159
else
62-
return new AssignmentStatement(lcontext, exp, l);
60+
return new AssignmentStatement(lcontext, exp, tkn);
6361
}
6462
}
6563
}

src/MoonSharp.Interpreter/Tree/Statements/AssignmentStatement.cs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ public AssignmentStatement(ScriptLoadingContext lcontext, Token startToken)
3232
lcontext.Lexer.Next();
3333
}
3434

35+
if (first.Type == TokenType.Local && lcontext.Lexer.Current.Type == TokenType.Op_Assignment && lcontext.Lexer.Current.Text != "=") {
36+
throw new SyntaxErrorException(lcontext.Lexer.Current, $"Expected '=' after local, got '{lcontext.Lexer.Current.Text}'.");
37+
}
38+
3539
if (lcontext.Lexer.Current.Type == TokenType.Op_Assignment)
3640
{
3741
CheckTokenType(lcontext, TokenType.Op_Assignment);
@@ -62,21 +66,54 @@ public AssignmentStatement(ScriptLoadingContext lcontext, Expression firstExpres
6266
{
6367
m_LValues.Add(CheckVar(lcontext, firstExpression));
6468

69+
List<Expression> leftExpressions = new() {firstExpression};
70+
6571
while (lcontext.Lexer.Current.Type == TokenType.Comma)
6672
{
6773
lcontext.Lexer.Next();
68-
Expression e = Expression.PrimaryExp(lcontext);
69-
m_LValues.Add(CheckVar(lcontext, e));
74+
Expression exp = Expression.PrimaryExp(lcontext);
75+
m_LValues.Add(CheckVar(lcontext, exp));
76+
leftExpressions.Add(exp);
7077
}
7178

79+
Token assignmentType = lcontext.Lexer.Current;
80+
7281
CheckTokenType(lcontext, TokenType.Op_Assignment);
7382

7483
m_RValues = Expression.ExprList(lcontext);
7584

85+
// Replace e.g. "a += b" with "a = a + b"
86+
if (assignmentType.Text != "=")
87+
{
88+
TokenType operationTokenType = assignmentType.Text switch
89+
{
90+
"+=" => TokenType.Op_Add,
91+
"-=" => TokenType.Op_MinusOrSub,
92+
"*=" => TokenType.Op_Mul,
93+
"/=" => TokenType.Op_Div,
94+
"%=" => TokenType.Op_Mod,
95+
"^=" => TokenType.Op_Pwr,
96+
"..=" => TokenType.Op_Concat,
97+
_ => throw new InternalErrorException($"Assignment operator not recognised: {assignmentType.Text}"),
98+
};
99+
assignmentType.Text = "=";
100+
101+
for (int valueIndex = 0; valueIndex < m_RValues.Count; valueIndex++)
102+
{
103+
if (valueIndex < leftExpressions.Count)
104+
{
105+
object operatorChain = BinaryOperatorExpression.BeginOperatorChain();
106+
BinaryOperatorExpression.AddExpressionToChain(operatorChain, leftExpressions[valueIndex]);
107+
BinaryOperatorExpression.AddOperatorToChain(operatorChain, new Token(operationTokenType, first.SourceId, first.FromLine, first.FromCol, first.ToLine, first.ToCol, first.PrevLine, first.PrevCol));
108+
BinaryOperatorExpression.AddExpressionToChain(operatorChain, m_RValues[valueIndex]);
109+
m_RValues[valueIndex] = BinaryOperatorExpression.CommitOperatorChain(operatorChain, lcontext);
110+
}
111+
}
112+
}
113+
76114
Token last = lcontext.Lexer.Current;
77115
m_Ref = first.GetSourceRefUpTo(last);
78116
lcontext.Source.Refs.Add(m_Ref);
79-
80117
}
81118

82119
private IVariable CheckVar(ScriptLoadingContext lcontext, Expression firstExpression)
@@ -89,7 +126,6 @@ private IVariable CheckVar(ScriptLoadingContext lcontext, Expression firstExpres
89126
return v;
90127
}
91128

92-
93129
public override void Compile(Execution.VM.ByteCode bc)
94130
{
95131
using (bc.EnterSource(m_Ref))
@@ -107,6 +143,5 @@ public override void Compile(Execution.VM.ByteCode bc)
107143
bc.Emit_Pop(m_RValues.Count);
108144
}
109145
}
110-
111146
}
112147
}

0 commit comments

Comments
 (0)