Skip to content

Commit 6bd1db6

Browse files
committed
gdded variable assignment and compare (Refs #51)[8]
* tsconfig.json: added ignoreDeprecations to for backward compatibility with moduleresolution * new files: py_environment * Introduced for simple handling of variable declarations TODO: undeclared variable error
1 parent 63a35ed commit 6bd1db6

File tree

7 files changed

+207
-29
lines changed

7 files changed

+207
-29
lines changed

src/cse-machine/py_context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Stash, Value } from './stash';
22
import { PyControl, PyControlItem } from './py_control';
3-
import { createSimpleEnvironment, createProgramEnvironment, Environment } from './environment';
3+
import { createSimpleEnvironment, createProgramEnvironment, Environment } from './py_environment';
44
import { CseError } from './error';
55
import { Heap } from './heap';
66
import { PyNode } from './py_types';

src/cse-machine/py_environment.ts

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { Value } from './stash';
2+
import { Heap } from './heap';
3+
// import { Closure } from './closure';
4+
import { PyContext } from './py_context';
5+
import { PyNode } from './py_types';
6+
import { ExprNS } from '../ast-types';
7+
8+
9+
export interface Frame {
10+
[name: string]: any
11+
}
12+
13+
export interface Environment {
14+
readonly id: string
15+
name: string
16+
tail: Environment | null
17+
callExpression?: ExprNS.Call;
18+
head: Frame
19+
heap: Heap
20+
thisContext?: Value
21+
}
22+
23+
export const uniqueId = (context: PyContext): string => {
24+
return `${context.runtime.objectCount++}`
25+
}
26+
27+
// export const createEnvironment = (
28+
// context: PyContext,
29+
// closure: Closure,
30+
// args: Value[],
31+
// callExpression: ExprNS.Call
32+
// ): Environment => {
33+
// const environment: Environment = {
34+
// // TODO: name
35+
// name: '',
36+
// tail: closure.environment,
37+
// head: {},
38+
// heap: new Heap(),
39+
// id: uniqueId(context),
40+
// callExpression: {
41+
// ...callExpression,
42+
// //arguments: args.map(ast.primitive)
43+
// }
44+
// }
45+
46+
// // console.info('closure.node.params:', closure.node.params);
47+
// // console.info('Number of params:', closure.node.params.length);
48+
49+
// closure.node.params.forEach((param, index) => {
50+
// if (isRestElement(param)) {
51+
// const array = args.slice(index)
52+
// handleArrayCreation(context, array, environment)
53+
// environment.head[(param.argument as es.Identifier).name] = array
54+
// } else {
55+
// environment.head[(param as es.Identifier).name] = args[index]
56+
// }
57+
// })
58+
// return environment
59+
// }
60+
61+
export const createSimpleEnvironment = (
62+
context: PyContext,
63+
name: string,
64+
tail: Environment | null = null
65+
): Environment => {
66+
return {
67+
id: uniqueId(context),
68+
name,
69+
tail,
70+
head: {},
71+
heap: new Heap(),
72+
// TODO: callExpression and thisContext are optional and can be provided as needed.
73+
};
74+
};
75+
76+
export const createProgramEnvironment = (context: PyContext, isPrelude: boolean): Environment => {
77+
return createSimpleEnvironment(context, isPrelude ? 'prelude' : 'programEnvironment');
78+
};
79+
80+
export const createBlockEnvironment = (
81+
context: PyContext,
82+
name = 'blockEnvironment'
83+
): Environment => {
84+
return {
85+
name,
86+
tail: currentEnvironment(context),
87+
head: {},
88+
heap: new Heap(),
89+
id: uniqueId(context)
90+
}
91+
}
92+
93+
// export const isRestElement = (node: Node): node is es.RestElement => {
94+
// return (node as es.RestElement).type === 'RestElement';
95+
// };
96+
97+
// export const handleArrayCreation = (
98+
// context: PyContext,
99+
// array: any[],
100+
// envOverride?: Environment
101+
// ): void => {
102+
// const environment = envOverride ?? currentEnvironment(context);
103+
// Object.defineProperties(array, {
104+
// id: { value: uniqueId(context) },
105+
// environment: { value: environment, writable: true }
106+
// });
107+
// environment.heap.add(array as any);
108+
// };
109+
110+
export const currentEnvironment = (context: PyContext): Environment => {
111+
return context.runtime.environments[0];
112+
};
113+
114+
export const popEnvironment = (context: PyContext) => context.runtime.environments.shift()
115+
116+
export const pushEnvironment = (context: PyContext, environment: Environment) => {
117+
context.runtime.environments.unshift(environment)
118+
context.runtime.environmentTree.insert(environment)
119+
}

src/cse-machine/py_interpreter.ts

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { StmtNS, ExprNS } from '../ast-types';
22
import { PyContext } from './py_context';
33
import { PyControl, PyControlItem } from './py_control';
4-
import { PyNode, Instr, InstrType, UnOpInstr, BinOpInstr, BoolOpInstr } from './py_types';
4+
import { PyNode, Instr, InstrType, UnOpInstr, BinOpInstr, BoolOpInstr, AssmtInstr } from './py_types';
55
import { Stash, Value, ErrorValue } from './stash';
66
import { IOptions } from '..';
77
import * as instr from './py_instrCreator';
@@ -10,6 +10,7 @@ import { TokenType } from '../tokens';
1010
import { Token } from '../tokenizer';
1111
import { Result, Finished, CSEBreak, Representation} from '../types';
1212
import { toPythonString } from '../stdlib'
13+
import { pyGetVariable, pyDefineVariable } from './py_utils';
1314

1415
type CmdEvaluator = (
1516
command: PyControlItem,
@@ -149,20 +150,44 @@ const pyCmdEvaluators: { [type: string]: CmdEvaluator } = {
149150
},
150151

151152
'Variable': (command, context, control, stash, isPrelude) => {
152-
const variable = command as ExprNS.Variable;
153-
const name = variable.name.lexeme;
154-
// For now, we only handle built in constants.
155-
// In a future commit, we will look up variables in the environment.
153+
const variableNode = command as ExprNS.Variable;
154+
const name = variableNode.name.lexeme;
155+
156156
if (name === 'True') {
157157
stash.push({ type: 'bool', value: true });
158158
} else if (name === 'False') {
159159
stash.push({ type: 'bool', value: false });
160160
} else {
161-
// Throw an error for undefined variables for now
162-
throw new Error(`NameError: name '${name}' is not defined`);
161+
// if not built in, look up in environment
162+
const value = pyGetVariable(context, name, variableNode)
163+
stash.push(value);
163164
}
164165
},
165166

167+
'Compare': (command, context, control, stash, isPrelude) => {
168+
const compareNode = command as ExprNS.Compare;
169+
// For now, we only handle simple, single comparisons.
170+
const opStr = mapOperatorToPyOperator(compareNode.operator);
171+
const op_instr = instr.binOpInstr(opStr, compareNode);
172+
control.push(op_instr);
173+
control.push(compareNode.right);
174+
control.push(compareNode.left);
175+
},
176+
177+
'Assign': (command, context, control, stash, isPrelude) => {
178+
const assignNode = command as StmtNS.Assign;
179+
180+
const assmtInstr = instr.assmtInstr(
181+
assignNode.name.lexeme,
182+
false,
183+
true,
184+
assignNode
185+
);
186+
187+
control.push(assmtInstr);
188+
control.push(assignNode.value);
189+
},
190+
166191
/**
167192
* Instruction Handlers
168193
*/
@@ -242,4 +267,13 @@ const pyCmdEvaluators: { [type: string]: CmdEvaluator } = {
242267
throw new Error(`Unsupported boolean operator: ${instr.symbol}`);
243268
}
244269
},
270+
271+
[InstrType.ASSIGNMENT]: (command, context, control, stash, isPrelude) => {
272+
const instr = command as AssmtInstr;
273+
const value = stash.pop(); // Get the evaluated value from the stash
274+
275+
if (value) {
276+
pyDefineVariable(context, instr.symbol, value);
277+
}
278+
},
245279
};

src/cse-machine/py_operators.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ export function evaluateBinaryExpression(code: string, command: ExprNS.Expr, con
433433
}
434434
}
435435
// Same type Integer Operations
436-
else if (left.type === 'bigint' && right.type ==='bigint') {
436+
else {
437437
const leftBigInt = left.value as bigint;
438438
const rightBigInt = right.value as bigint;
439439
let result: bigint | boolean;

src/cse-machine/py_utils.ts

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
import { Context } from "./context";
2-
import { ExprNS } from "../ast-types";
1+
import { PyContext } from "./py_context";
2+
import { Value } from "./stash";
3+
import { PyNode } from "./py_types";
34
import { TokenType } from "../tokens";
4-
import { UnsupportedOperandTypeError } from "../errors/errors";
5+
import { RuntimeSourceError } from "../errors/runtimeSourceError";
6+
import { currentEnvironment, Environment } from "./py_environment";
7+
import { TypeError } from "../errors/errors";
58

6-
export function handleRuntimeError (context: Context, error: any) {
9+
10+
11+
export function handleRuntimeError (context: PyContext, error: RuntimeSourceError) {
12+
context.errors.push(error);
713
throw error;
814
}
915

@@ -29,23 +35,21 @@ export function typeTranslator(type: string): string {
2935
// TODO: properly adapt for the rest, string is passed in to cater for __py_adder etc...
3036
export function operandTranslator(operand: TokenType | string) {
3137
if (typeof operand === 'string') {
32-
return operand;
38+
switch (operand) {
39+
case "__py_adder": return "+";
40+
case "__py_minuser": return "-";
41+
case "__py_multiplier": return "*";
42+
case "__py_divider": return "/";
43+
case "__py_modder": return "%";
44+
case "__py_powerer": return "**";
45+
default: return operand;
46+
}
3347
}
3448
switch (operand) {
35-
case TokenType.PLUS:
36-
return '+';
37-
case TokenType.MINUS:
38-
return '-';
39-
case TokenType.STAR:
40-
return '*';
41-
case TokenType.SLASH:
42-
return '/';
4349
case TokenType.LESS:
4450
return '<';
4551
case TokenType.GREATER:
4652
return '>';
47-
case TokenType.PERCENT:
48-
return '%';
4953
case TokenType.DOUBLEEQUAL:
5054
return '==';
5155
case TokenType.NOTEQUAL:
@@ -54,12 +58,8 @@ export function operandTranslator(operand: TokenType | string) {
5458
return '<=';
5559
case TokenType.GREATEREQUAL:
5660
return '>=';
57-
case TokenType.DOUBLESTAR:
58-
return '**';
5961
case TokenType.NOT:
6062
return 'not';
61-
case TokenType.DOUBLESLASH:
62-
return '//';
6363
default:
6464
return String(operand);
6565
}
@@ -73,3 +73,26 @@ export function pythonMod(a: any, b: any): any {
7373
return mod + b;
7474
}
7575
}
76+
77+
export function pyDefineVariable(context: PyContext, name: string, value: Value) {
78+
const environment = currentEnvironment(context);
79+
Object.defineProperty(environment.head, name, {
80+
value: value,
81+
writable: true,
82+
enumerable: true
83+
});
84+
}
85+
86+
export function pyGetVariable(context: PyContext, name: string, node: PyNode): Value {
87+
let environment: Environment | null = currentEnvironment(context);
88+
while (environment) {
89+
if (Object.prototype.hasOwnProperty.call(environment.head, name)) {
90+
return environment.head[name];
91+
} else {
92+
environment = environment.tail;
93+
}
94+
}
95+
// For now, we throw an error. We can change this to return undefined if needed.
96+
handleRuntimeError(context, new TypeError(`name '${name} is not defined`, node as any, context as any, '', ''));
97+
return { type: 'error', message: 'unreachable' };
98+
}

src/errors/errors.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,11 @@ export class TypeError extends RuntimeSourceError {
261261
?? '<unknown source>';
262262
let hint = "TypeError: '" + originalType + "' cannot be interpreted as an '" + targetType + "'.";
263263
const offset = fullLine.indexOf(snippet);
264+
const adjustedOffset = offset >= 0 ? offset : 0;
264265
const indicator = createErrorIndicator(snippet, '@');
265266
const name = "TypeError";
266267
const suggestion = ' Make sure the value you are passing is compatible with the expected type.';
267-
const msg = name + " at line " + line + "\n\n " + fullLine + "\n " + " ".repeat(offset) + indicator + "\n" + hint + suggestion;
268+
const msg = name + " at line " + line + "\n\n " + fullLine + "\n " + " ".repeat(adjustedOffset) + indicator + "\n" + hint + suggestion;
268269
this.message = msg;
269270
}
270271
}

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"module": "commonjs",//"ESNext",// /* Specify what module code is generated. */
3737
"rootDir": "src", /* Specify the root folder within your source files. */
3838
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
39+
"ignoreDeprecations": "6.0",
3940
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
4041
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
4142
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */

0 commit comments

Comments
 (0)