Skip to content
This repository was archived by the owner on Oct 13, 2024. It is now read-only.

Commit fe625e9

Browse files
authored
Merge pull request #36 from SahilK-027/main
Feature: Add Compound Assignment Operators (+=, -=, =, /=, %=, ^= etc.)
2 parents 8e14604 + 17ede59 commit fe625e9

7 files changed

Lines changed: 153 additions & 21 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { CompoundAssignmentExpr, Identifier } from "../../../FrontEnd/AST.ts";
2+
import Environment from "../../Scope/environment.ts";
3+
import { MAKE_NUll, RuntimeVal } from "../../values.ts";
4+
import { evaluate } from "../interpreter.ts";
5+
6+
export const evaluate_compound_assignment_expression = (
7+
node: CompoundAssignmentExpr,
8+
env: Environment,
9+
): RuntimeVal => {
10+
if (node.assignee.kind === "Identifier") {
11+
const varname = (node.assignee as Identifier).symbol;
12+
const varValue = env.lookupVar(varname);
13+
const operator = node.operator;
14+
15+
// Evaluate the right-hand side expression once
16+
const rightValue = evaluate(node.val, env);
17+
18+
if (varValue.type === rightValue.type) {
19+
switch (operator) {
20+
case "+=":
21+
if (varValue.type === "number") {
22+
const value = varValue.value + rightValue.value;
23+
console.log(value);
24+
return env.assignVar(varname, value);
25+
}
26+
break;
27+
case "-=":
28+
if (varValue.type === "number") {
29+
const value = varValue.value - rightValue.value;
30+
console.log(value);
31+
return env.assignVar(varname, value);
32+
}
33+
break;
34+
case "*=":
35+
if (varValue.type === "number") {
36+
const value = varValue.value * rightValue.value;
37+
console.log(value);
38+
return env.assignVar(varname, value);
39+
}
40+
break;
41+
case "/=":
42+
if (varValue.type === "number") {
43+
const value = varValue.value / rightValue.value;
44+
console.log(value);
45+
return env.assignVar(varname, value);
46+
}
47+
break;
48+
case "%=":
49+
if (varValue.type === "number") {
50+
const value = varValue.value % rightValue.value;
51+
console.log(value);
52+
return env.assignVar(varname, value);
53+
}
54+
break;
55+
case "^=":
56+
if (varValue.type === "number") {
57+
const value = Math.pow(varValue.value, rightValue.value);
58+
console.log(value);
59+
return env.assignVar(varname, value);
60+
}
61+
break;
62+
default:
63+
throw new Error(
64+
`Operator '${operator}' is not supported for type '${varValue.type}'`,
65+
);
66+
}
67+
} else {
68+
throw new Error(
69+
`Type Mismatch: Cannot use operator '${operator}' with different types`,
70+
);
71+
}
72+
}
73+
74+
return MAKE_NUll();
75+
};

BackEnd/Interpreter/interpreter.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
BooleanLiteral,
1010
CallExpr,
1111
ComparisonExpression,
12+
CompoundAssignmentExpr,
1213
ElseStatement,
1314
ForEachLoopStatement,
1415
FunctionDefinition,
@@ -43,6 +44,7 @@ import {
4344
evaluate_unary_expr,
4445
} from "./Expressions/Boolean_expression.ts";
4546
import { evaluate_comparison_expression } from "./Expressions/Comparison_expression.ts";
47+
import { evaluate_compound_assignment_expression } from "./Expressions/Compound_assignment.ts";
4648
import {
4749
evaluate_assignment_expression,
4850
evaluate_member_expression,
@@ -200,6 +202,12 @@ export function evaluate(astNode: Stmt, env: Environment): RuntimeVal {
200202

201203
case "ReturnStatement":
202204
return evaluate_return_statement(astNode as ReturnStatement, env);
205+
206+
case "CompoundAssignmentExpr":
207+
return evaluate_compound_assignment_expression(
208+
astNode as CompoundAssignmentExpr,
209+
env,
210+
);
203211
default:
204212
// If the AST node has an unknown or unsupported kind, we log an error and exit the program.
205213
console.log(astNode);

FrontEnd/AST.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export type NodeType =
2525
| "LogicalExpression"
2626
| "UnaryExpr"
2727
| "MinusExpr"
28+
| "CompoundAssignmentExpr"
2829
// Literals
2930
| "Identifier"
3031
| "NumericLiteral"
@@ -379,6 +380,22 @@ export interface MinusExpr extends Expr {
379380
argument: Expr;
380381
}
381382

383+
/**
384+
* Represents a compound assignment expression.
385+
* The CompoundAssignmentExpr interface represents a compound assignment expression.
386+
* It has the following properties:
387+
* - kind: The type of the node ("CompoundAssignmentExpression").
388+
* - assignee: The expression being assigned.
389+
* - operator: The compound assignment operator used in the expression.
390+
* - val: The value being assigned.
391+
*/
392+
export interface CompoundAssignmentExpr extends Expr {
393+
kind: "CompoundAssignmentExpr";
394+
assignee: Expr;
395+
operator: string; // This can be "+=", "-=", "/=", etc.
396+
val: Expr;
397+
}
398+
382399
/**
383400
* ===========================================================================================
384401
* User Defined Functions

FrontEnd/Parser.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
BreakStatement,
99
CallExpr,
1010
ComparisonExpression,
11+
CompoundAssignmentExpr,
1112
ElseStatement,
1213
Expr,
1314
ForEachLoopStatement,
@@ -754,6 +755,16 @@ export default class Parser {
754755
assignee: left,
755756
kind: "AssignmentExpression",
756757
} as AssignmentExpression;
758+
} else if (this.at().type === TokenType.CompoundAssignmentOperator) {
759+
const operator = this.at().value;
760+
this.eat();
761+
const value = this.parse_assignment_expr();
762+
return {
763+
assignee: left,
764+
val: value,
765+
kind: "CompoundAssignmentExpr",
766+
operator: operator,
767+
} as CompoundAssignmentExpr;
757768
}
758769
return left;
759770
}

FrontEnd/lexer.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export enum TokenType {
3636
BinaryOperator,
3737
LogicalOperator,
3838
ComparisonOperator,
39+
CompoundAssignmentOperator,
3940
NotOperator,
4041
OpenParen, // (
4142
CloseParen, // )
@@ -173,6 +174,12 @@ export function tokenize(sourceCode: string): Token[] {
173174
">": TokenType.ComparisonOperator,
174175
"&&": TokenType.LogicalOperator,
175176
"||": TokenType.LogicalOperator,
177+
"+=": TokenType.CompoundAssignmentOperator,
178+
"*=": TokenType.CompoundAssignmentOperator,
179+
"/=": TokenType.CompoundAssignmentOperator,
180+
"-=": TokenType.CompoundAssignmentOperator,
181+
"%=": TokenType.CompoundAssignmentOperator,
182+
"^=": TokenType.CompoundAssignmentOperator,
176183
};
177184

178185
for (const operator in operators) {
@@ -199,9 +206,8 @@ export function tokenize(sourceCode: string): Token[] {
199206
if (src[0] === '"') {
200207
src.shift(); // Consume the closing double quote
201208
return getToken(string, TokenType.String, line_cnt);
202-
} else {
203-
throw `SyntaxError:line:${line_cnt}: missing terminating '"' character.`;
204209
}
210+
throw `SyntaxError:line:${line_cnt}: missing terminating '"' character.`;
205211
}
206212

207213
if (src[0] === "'") {
@@ -215,11 +221,20 @@ export function tokenize(sourceCode: string): Token[] {
215221
if (src[0] === "'") {
216222
src.shift(); // Consume the closing double quote
217223
return getToken(string, TokenType.String, line_cnt);
218-
} else {
219-
throw `SyntaxError:line:${line_cnt}: missing terminating ''' character.`;
220224
}
225+
throw `SyntaxError:line:${line_cnt}: missing terminating ''' character.`;
221226
}
222227

228+
if (
229+
src[0] === "+" ||
230+
src[0] === "-" ||
231+
src[0] === "*" ||
232+
src[0] === "/" ||
233+
src[0] === "%" ||
234+
src[0] === "^"
235+
) {
236+
return getToken(src.shift(), TokenType.BinaryOperator, line_cnt);
237+
}
223238
return null;
224239
}
225240

@@ -268,15 +283,6 @@ export function tokenize(sourceCode: string): Token[] {
268283
tokens.push(getToken(src.shift(), TokenType.OpenBracket, line_cnt));
269284
} else if (src[0] === "]") {
270285
tokens.push(getToken(src.shift(), TokenType.CloseBracket, line_cnt));
271-
} else if (
272-
src[0] === "+" ||
273-
src[0] === "-" ||
274-
src[0] === "*" ||
275-
src[0] === "/" ||
276-
src[0] === "%" ||
277-
src[0] === "^"
278-
) {
279-
tokens.push(getToken(src.shift(), TokenType.BinaryOperator, line_cnt));
280286
} else if (src[0] === ";") {
281287
tokens.push(getToken(src.shift(), TokenType.Semicolon, line_cnt));
282288
} else if (src[0] === "!") {
@@ -328,5 +334,6 @@ export function tokenize(sourceCode: string): Token[] {
328334

329335
// Push EOF token
330336
tokens.push({ type: TokenType.EOF, value: "EndOfFile", curr_line: line_cnt });
337+
331338
return tokens;
332339
}

TODO.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@
1818
- [x] Implement user defined functions
1919
- [x] Write test cases
2020
- [x] Documentation
21+
- [x] For loop new syntax
2122

2223
## Increment
2324

24-
- [x] For loop new syntax
25+
- [x] Add support for `+=` `-=` `*=` `/=` `%=` `^=` operator
2526

2627
## Backlog
2728

2829
- [ ] Implement objects
2930
- [ ] Implement error handling
3031
- [ ] Optimize performance
31-
- [ ] Add support for `+=` `-=` `*=` `/=` `%=` `^=` operator

feat.avenger

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1-
newAvenger i;
2-
wakandaFor( i = 0 ; 10 ;i = i + 1){
3-
newAvenger temp = 10;
4-
vision(i);
5-
vision(temp);
6-
}
1+
newAvenger i = 27;
2+
newAvenger j = 27;
3+
newAvenger k = 27;
4+
newAvenger l = 27;
5+
newAvenger m = 27;
6+
newAvenger n = 27;
7+
8+
i += 3;
9+
j -= 3;
10+
k *= 3;
11+
l /= 3;
12+
m %= 10;
13+
n ^= 2;
14+
15+
vision(i);
16+
vision(j);
17+
vision(k);
18+
vision(l);
19+
vision(m);
20+
vision(n);

0 commit comments

Comments
 (0)