Skip to content

Commit

Permalink
Added support for binary operators, unary operators and math library (#…
Browse files Browse the repository at this point in the history
…18)

* Changes that implement the treatment of ints as BigInts.
TODO: explicit type conversions of BigInt back to Number for expressions and function calls.

* Added support for binary operators, unary operators and math library

* Prevent multiple assignments in the same function

* Changes that implement the treatment of ints as BigInts.
TODO: explicit type conversions of BigInt back to Number for expressions and function calls.

* Added support for binary operators, unary operators and math library

---------

Co-authored-by: yizhak <[email protected]>
  • Loading branch information
JJtan2002 and hyizhak authored Mar 23, 2024
1 parent 215754a commit 979f54d
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 41 deletions.
29 changes: 29 additions & 0 deletions src/ast-types.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/generate-ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export class AstWriter {
main() {
this.setup();
this.defineAst("Expr", [
"BigIntLiteral -> value: string",
"Binary -> left: Expr, operator: Token, right: Expr",
// Semantically different from Binary - for logical comparisons.
"Compare -> left: Expr, operator: Token, right: Expr",
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ export function parsePythonToEstreeAst(code: string,
return translator.resolve(ast) as unknown as Program
}

const text = `
-1
`
console.dir(parsePythonToEstreeAst(text, 1, false));
export * from './errors';

// import {ParserErrors, ResolverErrors, TokenizerErrors} from "./errors";
Expand Down
20 changes: 14 additions & 6 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ export class Parser {
const startToken = this.peek();
const statements: Stmt[] = [];
while (!this.isAtEnd()) {
if (this.match(TokenType.NEWLINE)) {
continue;
}
// if (this.match(TokenType.NEWLINE)) {
// continue;
// }
statements.push(this.stmt());
}
const endToken = this.peek();
Expand All @@ -154,9 +154,9 @@ export class Parser {
if (this.check(TokenType.DEF, TokenType.FOR, TokenType.IF, TokenType.WHILE)) {
return this.compound_stmt();
} else if (this.check(TokenType.NAME, ...PSEUD_NAMES, TokenType.NUMBER,
TokenType.PASS, TokenType.BREAK, TokenType.CONTINUE,
TokenType.PASS, TokenType.BREAK, TokenType.CONTINUE, TokenType.MINUS, TokenType.PLUS, TokenType.INDENT, TokenType.DEDENT,
TokenType.RETURN, TokenType.FROM, TokenType.GLOBAL, TokenType.NONLOCAL,
TokenType.ASSERT, TokenType.LPAR, TokenType.STRING, ...SPECIAL_IDENTIFIER_TOKENS)) {
TokenType.ASSERT, TokenType.LPAR, TokenType.STRING, TokenType.BIGINT, ...SPECIAL_IDENTIFIER_TOKENS)) {
return this.simple_stmt();
}
const startToken = this.peek();
Expand Down Expand Up @@ -239,6 +239,10 @@ export class Parser {
let res = null;
if (this.match(TokenType.NAME)) {
res = this.assign_stmt();
} else if (this.match(TokenType.INDENT)) {
res = new StmtNS.Indent(startToken, startToken);
} else if (this.match(TokenType.DEDENT)) {
res = new StmtNS.Dedent(startToken, startToken);
} else if (this.match(TokenType.PASS)) {
res = new StmtNS.Pass(startToken, startToken);
} else if (this.match(TokenType.BREAK)) {
Expand All @@ -255,7 +259,8 @@ export class Parser {
res = new StmtNS.NonLocal(startToken, startToken, this.advance());
} else if (this.match(TokenType.ASSERT)) {
res = new StmtNS.Assert(startToken, startToken, this.test());
} else if (this.check(TokenType.LPAR, TokenType.NUMBER, TokenType.STRING, ...SPECIAL_IDENTIFIER_TOKENS)) {
} else if (this.check(TokenType.LPAR, TokenType.NUMBER, TokenType.STRING,
TokenType.BIGINT, TokenType.MINUS, TokenType.PLUS, ...SPECIAL_IDENTIFIER_TOKENS)) {
res = new StmtNS.SimpleExpr(startToken, startToken, this.test());
} else {
throw new Error("Unreachable code path");
Expand Down Expand Up @@ -501,6 +506,9 @@ export class Parser {
if (this.match(TokenType.NUMBER)) {
return new ExprNS.Literal(startToken, this.previous(), Number(this.previous().lexeme));
}
if (this.match(TokenType.BIGINT)) {
return new ExprNS.BigIntLiteral(startToken, this.previous(), this.previous().lexeme);
}

if (this.match(TokenType.NAME, ...PSEUD_NAMES)) {
return new ExprNS.Variable(startToken, this.previous(), this.previous());
Expand Down
51 changes: 36 additions & 15 deletions src/resolver.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {StmtNS, ExprNS} from "./ast-types";
import { StmtNS, ExprNS } from "./ast-types";
type Expr = ExprNS.Expr;
type Stmt = StmtNS.Stmt;
import {Token} from "./tokenizer";
import {TokenType} from "./tokens";
import {ResolverErrors} from "./errors";
import { Token } from "./tokenizer";
import { TokenType } from "./tokens";
import { ResolverErrors } from "./errors";

const levenshtein = require('fast-levenshtein');

Expand Down Expand Up @@ -35,7 +35,7 @@ class Environment {
const name = identifier.lexeme;
let distance = 0;
let curr: Environment | null = this;
while(curr !== null) {
while (curr !== null) {
if (curr.names.has(name)) {
break;
}
Expand All @@ -49,7 +49,7 @@ class Environment {
lookupNameCurrentEnv(identifier: Token): Token | undefined {
return this.names.get(identifier.lexeme);
}
lookupNameCurrentEnvWithError(identifier: Token){
lookupNameCurrentEnvWithError(identifier: Token) {
if (this.lookupName(identifier) < 0) {
throw new ResolverErrors.NameNotFoundError(identifier.line, identifier.col,
this.source,
Expand Down Expand Up @@ -104,7 +104,7 @@ class Environment {
let minDistance = Infinity;
let minName = null;
let curr: Environment | null = this;
while(curr !== null) {
while (curr !== null) {
for (const declName of curr.names.keys()) {
const dist = levenshtein.get(name, declName);
if (dist < minDistance) {
Expand All @@ -125,12 +125,13 @@ class Environment {
export class Resolver implements StmtNS.Visitor<void>, ExprNS.Visitor<void> {
source: string;
ast: Stmt;
// change the environment to be suite scope as in python
environment: Environment | null;
constructor(source: string, ast: Stmt) {
this.source = source;
this.ast = ast;
// The global environment
this.environment = new Environment(source,null, new Map([
this.environment = new Environment(source, null, new Map([
["range", new Token(TokenType.NAME, "range", 0, 0, 0)],
["display", new Token(TokenType.NAME, "display", 0, 0, 0)],
["stringify", new Token(TokenType.NAME, "stringify", 0, 0, 0)],
Expand Down Expand Up @@ -173,21 +174,40 @@ export class Resolver implements StmtNS.Visitor<void>, ExprNS.Visitor<void> {
this.environment = oldEnv;
}

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

visitDedentCreation(stmt: StmtNS.Dedent): void {
// Switch to the previous environment.
if (this.environment?.enclosing !== undefined) {
this.environment = this.environment.enclosing;
}
}

visitFunctionDefStmt(stmt: StmtNS.FunctionDef) {
this.environment?.declareName(stmt.name);
this.environment?.functions.add(stmt.name.lexeme);
// Create a new environment.
const oldEnv = this.environment;
// Assign the parameters to the new environment.
const newEnv = new Map(
// // 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(
stmt.parameters.map(param => [param.lexeme, param])
);
this.environment = new Environment(this.source, this.environment, newEnv);
if (this.environment !== null) {
this.environment.names = params;
}
this.resolve(stmt.body);
// Grab identifiers from that new environment. That are NOT functions.
// stmt.varDecls = this.varDeclNames(this.environment.names)
// Restore old environment
this.environment = oldEnv;
// this.environment = oldEnv;
}

visitAnnAssignStmt(stmt: StmtNS.AnnAssign): void {
Expand Down Expand Up @@ -319,6 +339,7 @@ export class Resolver implements StmtNS.Visitor<void>, ExprNS.Visitor<void> {
}
visitLiteralExpr(expr: ExprNS.Literal): void {
}

visitBigIntLiteralExpr(expr: ExprNS.BigIntLiteral): void {
}

}
5 changes: 5 additions & 0 deletions src/tokenizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,11 @@ export class Tokenizer {
while (this.isDigit(this.peek())) {
this.advance();
}

if (this.peek() !== '.' && this.peek() !== 'e') {
this.addToken(TokenType.BIGINT);
return;
}
// Fractional part
if (this.peek() === '.') {
this.advance();
Expand Down
1 change: 1 addition & 0 deletions src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum TokenType {
ENDMARKER,
NAME,
NUMBER,
BIGINT,
STRING,
NEWLINE,
INDENT,
Expand Down
Loading

0 comments on commit 979f54d

Please sign in to comment.