Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Py slang test #29

Merged
merged 6 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,15 @@ export function parsePythonToEstreeAst(code: string,
return translator.resolve(ast) as unknown as Program
}

const text = `
-1
`
console.dir(parsePythonToEstreeAst(text, 1, false));
const text = `def f(x):
if x == 0:
a = 3
else :
a = 4
print(a)

f(0)`;
console.dir(parsePythonToEstreeAst(text, 1, true));
export * from './errors';

// import {ParserErrors, ResolverErrors, TokenizerErrors} from "./errors";
Expand Down
49 changes: 37 additions & 12 deletions src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export class Resolver implements StmtNS.Visitor<void>, ExprNS.Visitor<void> {
ast: Stmt;
// change the environment to be suite scope as in python
environment: Environment | null;
functionScope: Environment | null;
constructor(source: string, ast: Stmt) {
this.source = source;
this.ast = ast;
Expand All @@ -137,6 +138,7 @@ export class Resolver implements StmtNS.Visitor<void>, ExprNS.Visitor<void> {
["stringify", new Token(TokenType.NAME, "stringify", 0, 0, 0)],
// @TODO add all the source pre-declared names here
]));
this.functionScope = null;
}
resolve(stmt: Stmt[] | Stmt | Expr[] | Expr | null) {
if (stmt === null) {
Expand All @@ -163,6 +165,27 @@ export class Resolver implements StmtNS.Visitor<void>, ExprNS.Visitor<void> {
return res.length === 0 ? null : res;
}

functionVarConstraint(identifier: Token): void {
if (this.functionScope == null) {
return;
}
let curr = this.environment;
while (curr !== this.functionScope) {
if (curr !== null && curr.names.has(identifier.lexeme)) {
const token = curr.names.get(identifier.lexeme);
if (token === undefined) {
throw new Error("placeholder error")
}
throw new ResolverErrors.NameReassignmentError(identifier.line, identifier.col,
this.source,
identifier.indexInSource,
identifier.indexInSource + identifier.lexeme.length,
token);
}
curr = curr?.enclosing ?? null;
}
}

//// STATEMENTS
visitFileInputStmt(stmt: StmtNS.FileInput): void {
// Create a new environment.
Expand All @@ -175,8 +198,7 @@ export class Resolver implements StmtNS.Visitor<void>, ExprNS.Visitor<void> {
}

visitIndentCreation(stmt: StmtNS.Indent): void {
// Create a new environment.
const oldEnv = this.environment;
// Create a new environment
this.environment = new Environment(this.source, this.environment, new Map());
}

Expand All @@ -190,19 +212,20 @@ export class Resolver implements StmtNS.Visitor<void>, ExprNS.Visitor<void> {
visitFunctionDefStmt(stmt: StmtNS.FunctionDef) {
this.environment?.declareName(stmt.name);
this.environment?.functions.add(stmt.name.lexeme);
// // Create a new environment.
// Create a new environment.
// const oldEnv = this.environment;
// // Assign the parameters to the new environment.
// const newEnv = new Map(
// stmt.parameters.map(param => [param.lexeme, param])
// );
// this.environment = new Environment(this.source, this.environment, newEnv);
const params = new Map(
// Assign the parameters to the new environment.
const newEnv = new Map(
stmt.parameters.map(param => [param.lexeme, param])
);
if (this.environment !== null) {
this.environment.names = params;
}
this.environment = new Environment(this.source, this.environment, newEnv);
// const params = new Map(
// stmt.parameters.map(param => [param.lexeme, param])
// );
// if (this.environment !== null) {
// this.environment.names = params;
// }
this.functionScope = this.environment;
this.resolve(stmt.body);
// Grab identifiers from that new environment. That are NOT functions.
// stmt.varDecls = this.varDeclNames(this.environment.names)
Expand All @@ -213,11 +236,13 @@ export class Resolver implements StmtNS.Visitor<void>, ExprNS.Visitor<void> {
visitAnnAssignStmt(stmt: StmtNS.AnnAssign): void {
this.resolve(stmt.ann);
this.resolve(stmt.value);
this.functionVarConstraint(stmt.name);
this.environment?.declareName(stmt.name);
}

visitAssignStmt(stmt: StmtNS.Assign): void {
this.resolve(stmt.value);
this.functionVarConstraint(stmt.name);
this.environment?.declareName(stmt.name);
}

Expand Down
216 changes: 204 additions & 12 deletions src/tests/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ else:
expect(toPythonAst(text)).toMatchObject({})
})
test('Nested ternary', () => {
const text = `1 if A else 2 if B else 3\n`;
const text = `1 if a else 2 if b else 3\n`;
expect(toPythonAst(text)).toMatchObject({})
})
});
Expand Down Expand Up @@ -113,6 +113,22 @@ def y(a, b, c):
`
expect(toPythonAst(text)).toMatchObject({});
});

test('Function with default arguments', () => {
const text = `
def my_function(a, b=2, c=3):
return a + b + c
`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Function with *args and **kwargs', () => {
const text = `
def my_function(*args, **kwargs):
pass
`;
expect(toPythonAst(text)).toMatchObject({})
});
// @TODO fix me
// test('Function definition empty lines', () => {
// const text = `\
Expand All @@ -136,18 +152,194 @@ def y(a, b, c):
});

describe('Conditional statements', () => {
test('If-elif-else', () => {
test('If statement', () => {
const text = `
if x:
pass
elif y:
pass
elif z:
pass
if x > 10:
print("x is greater than 10")
`;
expect(toPythonAst(text)).toMatchObject({})
});

test('If-else statement', () => {
const text = `
if x > 10:
print("x is greater than 10")
else:
pass
print("x is less than or equal to 10")
`;
expect(toPythonAst(text)).toMatchObject({})
})
})
})
});

test('If-elif-else statement', () => {
const text = `
if x > 10:
print("x is greater than 10")
elif x == 10:
print("x is equal to 10")
else:
print("x is less than 10")
`;
expect(toPythonAst(text)).toMatchObject({})
});
});

describe('N-base numbers', () => {
test('Binary number', () => {
const text = `0b101010\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Octal number', () => {
const text = `0o1234567\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Hexadecimal number', () => {
const text = `0xabcdef\n`;
expect(toPythonAst(text)).toMatchObject({})
});
});

describe('Binary operators', () => {
test('Addition', () => {
const text = `1 + 1\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Large Number Addition', () => {
const text = `100000000 ** 100000000 + 1\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Subtraction', () => {
const text = `1 - 1\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Multiplication', () => {
const text = `1 * 1\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Large Number Multiplication', () => {
const text = `100000000 ** 100000000 * 5\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Division', () => {
const text = `1 / 1\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Modulus', () => {
const text = `1 % 1\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Exponent', () => {
const text = `2 ** 2\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Less than', () => {
const text = `1 < 2\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Greater than', () => {
const text = `2 > 1\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Less than or equal to', () => {
const text = `1 <= 2\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Greater than or equal to', () => {
const text = `2 >= 1\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Equality', () => {
const text = `1 == 2\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Inequality', () => {
const text = `1 != 2\n`;
expect(toPythonAst(text)).toMatchObject({})
});
});

describe('Unary operators', () => {
test('Negation', () => {
const text = `-1\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Logical NOT', () => {
const text = `not 1\n`;
expect(toPythonAst(text)).toMatchObject({})
});
});

describe('Binary logical operators', () => {
test('Logical AND', () => {
const text = `1 and 2\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Logical OR', () => {
const text = `1 or 2\n`;
expect(toPythonAst(text)).toMatchObject({})
});
});

describe('Complex expressions', () => {
test('Nested function call', () => {
const text = `
def f1(x, y):
return 1

def f2(x, y):
return y

f1(f2(1, 2), 2)
`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Binary operation with parentheses', () => {
const text = `(1 + 2) * 3\n`;
expect(toPythonAst(text)).toMatchObject({})
});
});

describe('Primitive expressions', () => {
test('Number literal', () => {
const text = `42\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Large Number literal', () => {
const text = `1000000000 ** 100000000\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Boolean literal True', () => {
const text = `True\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('Boolean literal False', () => {
const text = `False\n`;
expect(toPythonAst(text)).toMatchObject({})
});

test('String literal', () => {
const text = `"Hello, World!"\n`;
expect(toPythonAst(text)).toMatchObject({})
});
});
});
15 changes: 15 additions & 0 deletions src/translator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
WhileStatement
} from "estree";
import { TranslatorErrors } from "./errors";
// import { isEmpty } from "lodash";

export interface EstreePosition {
line: number;
Expand Down Expand Up @@ -193,6 +194,20 @@ export class Translator implements StmtNS.Visitor<BaseNode>, ExprNS.Visitor<Base
};
}

visitIndentCreation(stmt: StmtNS.Indent): EmptyStatement {
return {
type: 'EmptyStatement',
loc: this.toEstreeLocation(stmt),
};
}

visitDedentCreation(stmt: StmtNS.Dedent): EmptyStatement {
return {
type: 'EmptyStatement',
loc: this.toEstreeLocation(stmt),
};
}

visitFunctionDefStmt(stmt: StmtNS.FunctionDef): FunctionDeclaration {
const newBody = this.resolveManyStmt(stmt.body);
// if (stmt.varDecls !== null && stmt.varDecls.length > 0) {
Expand Down
Loading