diff --git a/src/MoonSharp.Interpreter/Tree/Lexer/Lexer.cs b/src/MoonSharp.Interpreter/Tree/Lexer/Lexer.cs index de47d322..7082f621 100755 --- a/src/MoonSharp.Interpreter/Tree/Lexer/Lexer.cs +++ b/src/MoonSharp.Interpreter/Tree/Lexer/Lexer.cs @@ -175,14 +175,14 @@ private Token ReadToken() { char next = CursorCharNext(); if (next == '.') - return PotentiallyDoubleCharOperator('.', TokenType.Op_Concat, TokenType.VarArgs, fromLine, fromCol); + return PotentiallyDoubleCharOperator(TokenType.Op_Concat, '.', TokenType.VarArgs, "...", '=', TokenType.Op_Assignment, "..=", fromLine, fromCol); else if (LexerUtils.CharIsDigit(next)) return ReadNumberToken(fromLine, fromCol, true); else return CreateToken(TokenType.Dot, fromLine, fromCol, "."); } case '+': - return CreateSingleCharToken(TokenType.Op_Add, fromLine, fromCol); + return PotentiallyDoubleCharOperator('=', TokenType.Op_Add, TokenType.Op_Assignment, fromLine, fromCol); case '-': { char next = CursorCharNext(); @@ -190,19 +190,24 @@ private Token ReadToken() { return ReadComment(fromLine, fromCol); } + else if (next == '=') + { + CursorCharNext(); + return CreateToken(TokenType.Op_Assignment, fromLine, fromCol, "-="); + } else { return CreateToken(TokenType.Op_MinusOrSub, fromLine, fromCol, "-"); } } case '*': - return CreateSingleCharToken(TokenType.Op_Mul, fromLine, fromCol); + return PotentiallyDoubleCharOperator('=', TokenType.Op_Mul, TokenType.Op_Assignment, fromLine, fromCol); case '/': - return CreateSingleCharToken(TokenType.Op_Div, fromLine, fromCol); + return PotentiallyDoubleCharOperator('=', TokenType.Op_Div, TokenType.Op_Assignment, fromLine, fromCol); case '%': - return CreateSingleCharToken(TokenType.Op_Mod, fromLine, fromCol); + return PotentiallyDoubleCharOperator('=', TokenType.Op_Mod, TokenType.Op_Assignment, fromLine, fromCol); case '^': - return CreateSingleCharToken(TokenType.Op_Pwr, fromLine, fromCol); + return PotentiallyDoubleCharOperator('=', TokenType.Op_Pwr, TokenType.Op_Assignment, fromLine, fromCol); case '$': return PotentiallyDoubleCharOperator('{', TokenType.Op_Dollar, TokenType.Brk_Open_Curly_Shared, fromLine, fromCol); case '#': @@ -540,8 +545,25 @@ private Token PotentiallyDoubleCharOperator(char expectedSecondChar, TokenType s else return CreateToken(singleCharToken, fromLine, fromCol, op); } + private Token PotentiallyDoubleCharOperator(TokenType singleCharToken, char expectedSecondChar, TokenType doubleCharToken, string doubleCharText, char alternateExpectedSecondChar, TokenType alternateDoubleCharToken, string alternateDoubleCharText, int fromLine, int fromCol) + { + string op = CursorChar().ToString(); + CursorCharNext(); + if (CursorChar() == expectedSecondChar) + { + CursorCharNext(); + return CreateToken(doubleCharToken, fromLine, fromCol, doubleCharText); + } + else if (CursorChar() == alternateExpectedSecondChar) + { + CursorCharNext(); + return CreateToken(alternateDoubleCharToken, fromLine, fromCol, alternateDoubleCharText); + } + else + return CreateToken(singleCharToken, fromLine, fromCol, op); + } private Token CreateNameToken(string name, int fromLine, int fromCol) { @@ -557,7 +579,6 @@ private Token CreateNameToken(string name, int fromLine, int fromCol) } } - private Token CreateToken(TokenType tokenType, int fromLine, int fromCol, string text = null) { Token t = new Token(tokenType, m_SourceId, fromLine, fromCol, m_Line, m_Col, m_PrevLineTo, m_PrevColTo) @@ -583,9 +604,5 @@ private string ReadNameToken() return name.ToString(); } - - - - } } diff --git a/src/MoonSharp.Interpreter/Tree/Statements/AssignmentStatement.cs b/src/MoonSharp.Interpreter/Tree/Statements/AssignmentStatement.cs index a82fe67a..0c96d347 100644 --- a/src/MoonSharp.Interpreter/Tree/Statements/AssignmentStatement.cs +++ b/src/MoonSharp.Interpreter/Tree/Statements/AssignmentStatement.cs @@ -61,21 +61,53 @@ public AssignmentStatement(ScriptLoadingContext lcontext, Expression firstExpres { m_LValues.Add(CheckVar(lcontext, firstExpression)); + List leftExpressions = new() {firstExpression}; + while (lcontext.Lexer.Current.Type == TokenType.Comma) { lcontext.Lexer.Next(); - Expression e = Expression.PrimaryExp(lcontext); - m_LValues.Add(CheckVar(lcontext, e)); + Expression exp = Expression.PrimaryExp(lcontext); + m_LValues.Add(CheckVar(lcontext, exp)); + leftExpressions.Add(exp); } + string assignmentType = lcontext.Lexer.Current.Text; + CheckTokenType(lcontext, TokenType.Op_Assignment); m_RValues = Expression.ExprList(lcontext); + // Replace e.g. "a += b" with "a = a + b" + if (assignmentType != "=") + { + TokenType operationTokenType = assignmentType switch + { + "+=" => TokenType.Op_Add, + "-=" => TokenType.Op_MinusOrSub, + "*=" => TokenType.Op_Mul, + "/=" => TokenType.Op_Div, + "%=" => TokenType.Op_Mod, + "^=" => TokenType.Op_Pwr, + "..=" => TokenType.Op_Concat, + _ => throw new InternalErrorException($"Assignment operator not recognised: {assignmentType}"), + }; + + for (int valueIndex = 0; valueIndex < m_RValues.Count; valueIndex++) + { + if (leftExpressions.Count > valueIndex) + { + object operatorChain = BinaryOperatorExpression.BeginOperatorChain(); + BinaryOperatorExpression.AddExpressionToChain(operatorChain, leftExpressions[valueIndex]); + BinaryOperatorExpression.AddOperatorToChain(operatorChain, new Token(operationTokenType, first.SourceId, first.FromLine, first.FromCol, first.ToLine, first.ToCol, first.PrevLine, first.PrevCol)); + BinaryOperatorExpression.AddExpressionToChain(operatorChain, m_RValues[valueIndex]); + m_RValues[valueIndex] = BinaryOperatorExpression.CommitOperatorChain(operatorChain, lcontext); + } + } + } + Token last = lcontext.Lexer.Current; m_Ref = first.GetSourceRefUpTo(last); lcontext.Source.Refs.Add(m_Ref); - } private IVariable CheckVar(ScriptLoadingContext lcontext, Expression firstExpression) @@ -88,7 +120,6 @@ private IVariable CheckVar(ScriptLoadingContext lcontext, Expression firstExpres return v; } - public override void Compile(Execution.VM.ByteCode bc) { using (bc.EnterSource(m_Ref)) @@ -106,6 +137,5 @@ public override void Compile(Execution.VM.ByteCode bc) bc.Emit_Pop(m_RValues.Count); } } - } }