Skip to content

Commit c9741c0

Browse files
WangYuyang1013WangdahaiWangdahaiWangdahai
authored
cse-machine-version independent from js-slang (#50)
* Implement translator feature * Implement translator feature * Implement translator feature * add commend on double to string * cse-machine-utils * cse-machine-utils fixed code error --------- Co-authored-by: Wangdahai <wangdahai@192.168.1.137> Co-authored-by: Wangdahai <wangdahai@WangdahaideMacBook-Air.local> Co-authored-by: Wangdahai <wangdahai@192.168.1.183>
1 parent bd87748 commit c9741c0

18 files changed

+1838
-7
lines changed

README.md

+1-7
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,7 @@
22

33
## What is py-slang?
44

5-
`py-slang` is a language frontend for the
6-
[js-slang](https://github.com/source-academy/js-slang) repository. It parses
7-
a restricted subset of Python (enough to complete SICP), and outputs an
8-
`estree`-compatible AST. [The grammar](./src/Grammar.gram) is a reduced
9-
version of [Python 3.7's](https://docs.python.org/3.7/reference/grammar.html).
10-
This project does not aim to be a full Python to JS transpiler, but aims
11-
to transpile just a small enough subset of Python.
5+
`py-slang` is a Python implementation developed specifically for the Source Academy online learning environment. Unlike previous versions where Python was treated as a subset within [js-slang](https://github.com/source-academy/js-slang), py-slang now stands as an independent language implementation. It features its own parser, csemachine, and runtime, designed to process a tailored subset of Python for educational purposes.
126

137
## Usage
148
For local testing:

src/cse-machine/ast-helper.ts

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// astHelpers.ts
2+
import type * as es from 'estree';
3+
import { StatementSequence } from './types';
4+
import { ControlItem } from './control';
5+
6+
/**
7+
* Create a StatementSequence node.
8+
*/
9+
export const statementSequence = (
10+
body: es.Statement[],
11+
loc?: es.SourceLocation | null
12+
): StatementSequence => ({
13+
type: 'StatementSequence',
14+
body,
15+
loc,
16+
innerComments: undefined,
17+
});
18+
19+
export const isNode = (item: any): item is es.Node => {
20+
return typeof item === 'object' && item !== null && 'type' in item;
21+
};
22+
23+
export const isBlockStatement = (node: es.Node | StatementSequence): node is es.BlockStatement => {
24+
return node.type === 'BlockStatement';
25+
};
26+
27+
export const hasDeclarations = (node: es.BlockStatement): boolean => {
28+
return node.body.some(stmt => stmt.type === 'VariableDeclaration' || stmt.type === 'FunctionDeclaration');
29+
};
30+
31+
export const blockArrowFunction = (
32+
params: es.Identifier[],
33+
body: es.Statement[] | es.BlockStatement | es.Expression,
34+
loc?: es.SourceLocation | null
35+
): es.ArrowFunctionExpression => ({
36+
type: 'ArrowFunctionExpression',
37+
expression: false,
38+
generator: false,
39+
params,
40+
body: Array.isArray(body) ? blockStatement(body) : body,
41+
loc
42+
})
43+
44+
export const blockStatement = (
45+
body: es.Statement[],
46+
loc?: es.SourceLocation | null
47+
): es.BlockStatement => ({
48+
type: 'BlockStatement',
49+
body,
50+
loc
51+
})
52+
53+
export const constantDeclaration = (
54+
name: string,
55+
init: es.Expression,
56+
loc?: es.SourceLocation | null
57+
) => declaration(name, 'declaration', init, loc)
58+
59+
export const declaration = (
60+
name: string,
61+
kind: AllowedDeclarations,
62+
init: es.Expression,
63+
loc?: es.SourceLocation | null
64+
): pyVariableDeclaration => ({
65+
type: 'VariableDeclaration',
66+
declarations: [
67+
{
68+
type: 'VariableDeclarator',
69+
id: identifier(name),
70+
init
71+
}
72+
],
73+
kind: 'declaration',
74+
loc
75+
})
76+
77+
type AllowedDeclarations = 'declaration' | 'const'
78+
79+
export interface pyVariableDeclaration {
80+
type: "VariableDeclaration";
81+
declarations: pyVariableDeclarator[];
82+
kind: "declaration" | "const";
83+
loc?: es.SourceLocation | null | undefined;
84+
range?: [number, number] | undefined;
85+
}
86+
87+
export interface pyVariableDeclarator {
88+
type: "VariableDeclarator";
89+
id: Pattern;
90+
init?: es.Expression | null | undefined;
91+
}
92+
93+
export type Pattern = es.Identifier | es.ObjectPattern | es.ArrayPattern | es.RestElement | es.AssignmentPattern | es.MemberExpression;
94+
95+
export const identifier = (name: string, loc?: es.SourceLocation | null): es.Identifier => ({
96+
type: 'Identifier',
97+
name,
98+
loc
99+
})
100+
101+
export const returnStatement = (
102+
argument: es.Expression,
103+
loc?: es.SourceLocation | null
104+
): es.ReturnStatement => ({
105+
type: 'ReturnStatement',
106+
argument,
107+
loc
108+
})
109+
110+
export const hasReturnStatement = (block: es.BlockStatement | StatementSequence): boolean => {
111+
let hasReturn = false
112+
for (const statement of block.body) {
113+
if (isReturnStatement(statement)) {
114+
hasReturn = true
115+
} else if (isIfStatement(statement)) {
116+
// Parser enforces that if/else have braces (block statement)
117+
hasReturn = hasReturn || hasReturnStatementIf(statement as es.IfStatement)
118+
} else if (isBlockStatement(statement) || isStatementSequence(statement)) {
119+
hasReturn = hasReturn && hasReturnStatement(statement)
120+
}
121+
}
122+
return hasReturn
123+
}
124+
125+
export const isReturnStatement = (node: es.Node): node is es.ReturnStatement => {
126+
return (node as es.ReturnStatement).type == 'ReturnStatement'
127+
}
128+
129+
export const isIfStatement = (node: es.Node): node is es.IfStatement => {
130+
return (node as es.IfStatement).type == 'IfStatement'
131+
}
132+
133+
export const hasReturnStatementIf = (statement: es.IfStatement): boolean => {
134+
let hasReturn = true
135+
// Parser enforces that if/else have braces (block statement)
136+
hasReturn = hasReturn && hasReturnStatement(statement.consequent as es.BlockStatement)
137+
if (statement.alternate) {
138+
if (isIfStatement(statement.alternate)) {
139+
hasReturn = hasReturn && hasReturnStatementIf(statement.alternate as es.IfStatement)
140+
} else if (isBlockStatement(statement.alternate) || isStatementSequence(statement.alternate)) {
141+
hasReturn = hasReturn && hasReturnStatement(statement.alternate)
142+
}
143+
}
144+
return hasReturn
145+
}
146+
147+
export const isStatementSequence = (node: ControlItem): node is StatementSequence => {
148+
return (node as StatementSequence).type == 'StatementSequence'
149+
}
150+
151+
export const literal = (
152+
value: string | number | boolean | null,
153+
loc?: es.SourceLocation | null
154+
): es.Literal => ({
155+
type: 'Literal',
156+
value,
157+
loc
158+
})

src/cse-machine/closure.ts

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import * as es from 'estree'
2+
import { Environment } from './environment'
3+
import { Context } from './context'
4+
import { StatementSequence } from './types'
5+
import { blockArrowFunction, blockStatement, hasReturnStatement, identifier, isBlockStatement, returnStatement } from './ast-helper'
6+
import { ControlItem } from './control'
7+
8+
export class Closure {
9+
public originalNode?: es.ArrowFunctionExpression
10+
11+
/** Unique ID defined for closure */
12+
//public readonly id: string
13+
14+
/** Name of the constant declaration that the closure is assigned to */
15+
public declaredName?: string
16+
17+
constructor(
18+
public node: es.ArrowFunctionExpression,
19+
public environment: Environment,
20+
public context: Context,
21+
public predefined: boolean = false
22+
) {
23+
this.originalNode = node
24+
}
25+
26+
static makeFromArrowFunction(
27+
node: es.ArrowFunctionExpression,
28+
environment: Environment,
29+
context: Context,
30+
// TODO: Consider implementing a mechanism that more closely mimics Python’s implicit return (i.e., automatically inserting "return None")
31+
dummyReturn: boolean = false,
32+
predefined: boolean = false
33+
): Closure {
34+
const functionBody: es.BlockStatement | StatementSequence =
35+
!isBlockStatement(node.body) && !isStatementSequence(node.body)
36+
? blockStatement([returnStatement(node.body, node.body.loc)], node.body.loc)
37+
: dummyReturn && !hasReturnStatement(node.body)
38+
? blockStatement(
39+
[
40+
...node.body.body,
41+
returnStatement(identifier('undefined', node.body.loc), node.body.loc)
42+
],
43+
node.body.loc
44+
)
45+
: node.body
46+
47+
const closure = new Closure(blockArrowFunction(node.params as es.Identifier[], functionBody, node.loc),
48+
environment, context, predefined)
49+
50+
closure.originalNode = node
51+
52+
return closure
53+
}
54+
}
55+
56+
export const isStatementSequence = (node: ControlItem): node is StatementSequence => {
57+
return (node as StatementSequence).type == 'StatementSequence'
58+
}

0 commit comments

Comments
 (0)