Pythonic JavaScript that doesn't suck - a Python-to-JavaScript transpiler with clean syntax and performance
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Complete AST system with 50+ node classes representing all Python language constructs. The AST provides programmatic access to parsed RapydScript code for analysis, transformation, and code generation.
Foundation classes that all AST nodes inherit from.
/**
* Root base class for all AST nodes
*/
class AST {
constructor(props?: object);
/** Clone this node with optional property overrides */
clone(props?: object): AST;
/** Apply a transformation function to this node and its children */
transform(transformer: TreeTransformer): AST;
/** Walk through this node and its children with a visitor function */
walk(visitor: TreeWalker): void;
/** Get a string representation of this node */
toString(): string;
}
/**
* Token from lexical analysis
*/
class AST_Token extends AST {
type: string;
value: string;
line: number;
col: number;
pos: number;
endline: number;
endcol: number;
endpos: number;
nlb: boolean;
comments_before: AST_Token[];
comments_after: AST_Token[];
}
/**
* Base class for syntax tree nodes
*/
class AST_Node extends AST {
start: AST_Token;
end: AST_Token;
/** Print this node to an output stream */
print(stream: OutputStream, force_parens?: boolean): void;
}AST nodes representing executable statements.
/**
* Base class for all statements
*/
class AST_Statement extends AST_Node {}
/**
* Block of statements
*/
class AST_Block extends AST_Statement {
body: AST_Statement[];
}
/**
* Top-level program or module
*/
class AST_Toplevel extends AST_Block {
/** Print this toplevel to an output stream */
print(output: OutputStream): void;
}
/**
* Single statement
*/
class AST_SimpleStatement extends AST_Statement {}
/**
* Conditional statement (if/elif/else)
*/
class AST_If extends AST_Statement {
condition: AST_Node;
body: AST_Statement;
alternative: AST_Statement | null;
}
/**
* While loop
*/
class AST_While extends AST_Statement {
condition: AST_Node;
body: AST_Statement;
}
/**
* Do-while loop
*/
class AST_Do extends AST_Statement {
body: AST_Statement;
condition: AST_Node;
}
/**
* Python-style for loop (for item in iterable)
*/
class AST_ForIn extends AST_Statement {
init: AST_Node;
object: AST_Node;
body: AST_Statement;
}
/**
* JavaScript-style for loop
*/
class AST_ForJS extends AST_Statement {
init: AST_Node | null;
condition: AST_Node | null;
step: AST_Node | null;
body: AST_Statement;
}
/**
* Try block
*/
class AST_Try extends AST_Statement {
body: AST_Statement;
bcatch: AST_Catch | null;
bfinally: AST_Finally | null;
}
/**
* Catch clause (JavaScript-style)
*/
class AST_Catch extends AST_Block {
argname: AST_Symbol;
}
/**
* Except clause (Python-style)
*/
class AST_Except extends AST_Block {
errors: AST_Node[];
argname: AST_Symbol | null;
}
/**
* Finally block
*/
class AST_Finally extends AST_Block {}
/**
* Function definition
*/
class AST_Function extends AST_Statement {
name: AST_Symbol;
argnames: AST_Symbol[];
defaults: AST_Node[];
is_generator: boolean;
body: AST_Statement;
}
/**
* Class definition
*/
class AST_Class extends AST_Statement {
name: AST_Symbol;
parent: AST_Node | null;
body: AST_Statement;
}
/**
* Class method definition
*/
class AST_Method extends AST_Function {
static: boolean;
}
/**
* Return statement
*/
class AST_Return extends AST_Statement {
value: AST_Node | null;
}
/**
* Yield expression (for generators)
*/
class AST_Yield extends AST_Statement {
value: AST_Node | null;
is_yield_from: boolean;
}
/**
* Break statement
*/
class AST_Break extends AST_Statement {}
/**
* Continue statement
*/
class AST_Continue extends AST_Statement {}AST nodes representing expressions and values.
/**
* Base class for expressions
*/
class AST_Expression extends AST_Node {}
/**
* Function call
*/
class AST_Call extends AST_Expression {
expression: AST_Node;
args: AST_Node[];
kwargs: AST_Node[];
}
/**
* Binary operation (+, -, *, etc.)
*/
class AST_Binary extends AST_Expression {
operator: string;
left: AST_Node;
right: AST_Node;
}
/**
* Unary operation (-, not, etc.)
*/
class AST_Unary extends AST_Expression {
operator: string;
expression: AST_Node;
}
/**
* Ternary conditional operator (a if condition else b)
*/
class AST_Conditional extends AST_Expression {
condition: AST_Node;
consequent: AST_Node;
alternative: AST_Node;
}
/**
* Array literal [1, 2, 3]
*/
class AST_Array extends AST_Expression {
elements: AST_Node[];
}
/**
* Object literal {key: value}
*/
class AST_Object extends AST_Expression {
properties: AST_ObjectKeyVal[];
}
/**
* Object property (key: value pair)
*/
class AST_ObjectKeyVal extends AST_Node {
key: AST_Node;
value: AST_Node;
}
/**
* Set literal {1, 2, 3}
*/
class AST_Set extends AST_Expression {
elements: AST_Node[];
}
/**
* Property access (obj.prop)
*/
class AST_Dot extends AST_Expression {
expression: AST_Node;
property: string;
}
/**
* Subscript access (obj[key])
*/
class AST_Sub extends AST_Expression {
expression: AST_Node;
property: AST_Node;
}
/**
* Variable reference or identifier
*/
class AST_Symbol extends AST_Expression {
name: string;
scope: Scope;
thedef: SymbolDef;
}
/**
* String literal
*/
class AST_String extends AST_Expression {
value: string;
quote: string;
}
/**
* Number literal
*/
class AST_Number extends AST_Expression {
value: number;
}
/**
* Boolean literal (True/False)
*/
class AST_Boolean extends AST_Expression {
value: boolean;
}
/**
* Null literal (None)
*/
class AST_Null extends AST_Expression {}
/**
* Undefined literal
*/
class AST_Undefined extends AST_Expression {}AST nodes for Python-style comprehensions.
/**
* Base class for comprehensions
*/
class AST_Comprehension extends AST_Expression {
statement: AST_Node;
condition: AST_Node | null;
}
/**
* List comprehension [x for x in items]
*/
class AST_ListComprehension extends AST_Comprehension {}
/**
* Set comprehension {x for x in items}
*/
class AST_SetComprehension extends AST_Comprehension {}
/**
* Dictionary comprehension {k:v for k,v in items}
*/
class AST_DictComprehension extends AST_Comprehension {}
/**
* Generator expression (x for x in items)
*/
class AST_GeneratorComprehension extends AST_Comprehension {}
/**
* Decorator (@decorator syntax)
*/
class AST_Decorator extends AST_Node {
decorator: AST_Node;
args: AST_Node[];
}
/**
* Assert statement
*/
class AST_Assert extends AST_Statement {
condition: AST_Node;
message: AST_Node | null;
}
/**
* Import statement
*/
class AST_Import extends AST_Statement {
module: AST_Node;
names: AST_Node[];
alias: string | null;
}
/**
* Slice expression (a[1:5])
*/
class AST_Slice extends AST_Expression {
expression: AST_Node;
start: AST_Node | null;
end: AST_Node | null;
step: AST_Node | null;
}
/**
* Existential operator (?)
*/
class AST_Existential extends AST_Expression {
expression: AST_Node;
}Utilities for programmatically analyzing and modifying AST nodes.
/**
* Tree transformer for modifying AST nodes
*/
interface TreeTransformer {
(node: AST_Node, descend: () => AST_Node): AST_Node;
}
/**
* Tree walker for visiting AST nodes
*/
interface TreeWalker {
(node: AST_Node, descend: () => void): void;
}
/**
* Symbol definition in scope
*/
class SymbolDef {
name: string;
orig: AST_Symbol[];
scope: Scope;
references: AST_Symbol[];
global: boolean;
mangled_name: string;
}
/**
* Lexical scope
*/
class Scope {
names: {[name: string]: SymbolDef};
mangled_names: {[name: string]: boolean};
parent_scope: Scope | null;
enclosed: SymbolDef[];
cname: number;
}Basic AST Manipulation:
const { create_compiler } = require("rapydscript-ng");
const RapydScript = create_compiler();
// Parse code into AST
const ast = RapydScript.parse(`
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
`);
// Walk through AST nodes
ast.walk(function(node) {
if (node instanceof RapydScript.AST_Function) {
console.log("Found function:", node.name.name);
}
});AST Transformation:
// Transform all string literals to uppercase
const transformed = ast.transform(function(node, descend) {
if (node instanceof RapydScript.AST_String) {
return new RapydScript.AST_String({
value: node.value.toUpperCase(),
quote: node.quote,
start: node.start,
end: node.end
});
}
return descend();
});Finding Specific Nodes:
const functions = [];
ast.walk(function(node) {
if (node instanceof RapydScript.AST_Function) {
functions.push({
name: node.name.name,
args: node.argnames.map(arg => arg.name),
line: node.start.line
});
}
});Creating AST Nodes:
// Create a new function call node
const call = new RapydScript.AST_Call({
expression: new RapydScript.AST_Symbol({name: "print"}),
args: [
new RapydScript.AST_String({value: "Hello, World!", quote: '"'})
]
});
// Generate JavaScript from the node
const output = new RapydScript.OutputStream({ beautify: true });
call.print(output);
console.log(output.get()); // print("Hello, World!");Install with Tessl CLI
npx tessl i tessl/npm-rapydscript-ng