@gracexwho/model-card-generator
Version:
Tool for generating model cards for Jupyter Notebook.
648 lines (540 loc) • 14.2 kB
text/typescript
import { parse as python3Parse, parser } from './python3';
import { printNode } from './printNode';
/**
* Reset the lexer state after an error. Otherwise, parses after a failed parse can fail too.
*/
let yy = parser.yy as any;
let oldParseError = yy.parseError;
oldParseError = function (text: String, hash: any) {
this.indents = [0];
this.indent = 0;
this.dedents = 0;
this.brackets_count = 0;
oldParseError.call(this, text, hash);
};
/**
* This is the main interface for parsing code.
* Call this instead of the `parse` method in python3.js.
* If the `parse` method gets an error, all later calls will throw an error.
* This method resets the state of the `parse` method so that doesn't happen.
*/
export function parse(program: string): Module {
if (program.charCodeAt(0) === 65279) {
// eliminate byte order mark
program = program.slice(1);
}
// The parser is fussy about line endings, so remote
// carriage returns and make sure we end with a newline.
return python3Parse(program.replace(/\r/g, '') + '\n');
}
export type SyntaxNode =
| Module
| Import
| From
| Decorator
| Decorate
| Def
| Parameter
| Assignment
| Assert
| Pass
| Return
| Yield
| Raise
| Continue
| Break
| Global
| Nonlocal
| If
| Else
| While
| For
| Try
| With
| Call
| Index
| Slice
| Dot
| IfExpr
| CompFor
| CompIf
| Lambda
| UnaryOperator
| BinaryOperator
| Starred
| Tuple
| ListExpr
| SetExpr
| DictExpr
| Name
| Literal
| Class;
/// Must be consistent with Jison (Bison) naming conventions
interface JisonLocation {
first_line: number;
first_column: number;
last_line: number;
last_column: number;
}
export interface Location extends JisonLocation {
path?: string; // useful for error messages and other tracking
}
export function locationString(loc: Location) {
return `${loc.path}${loc.last_line}:${loc.first_column}-${loc.last_line}:${loc.last_column}`;
}
// loc2 is inside loc1
export function locationContains(loc1: Location, loc2: Location) {
function contains(loc: Location, line: number, col: number) {
return (
(loc.first_line < line ||
(loc.first_line === line && loc.first_column <= col)) &&
(line < loc.last_line ||
(line === loc.last_line && col <= loc.last_column))
);
}
return (
contains(loc1, loc2.first_line, loc2.first_column) &&
contains(loc1, loc2.last_line, loc2.last_column)
);
}
export interface Locatable {
location?: Location;
cellId?: string;
executionCount?: number;
}
const LocatableFields = ['location', 'cellId', 'executionCount'];
export const MODULE = 'module';
export interface Module extends Locatable {
type: typeof MODULE;
code: SyntaxNode[];
}
export const IMPORT = 'import';
export interface Import extends Locatable {
type: typeof IMPORT;
names: { path: string; name?: string; location: Location }[];
}
export const FROM = 'from';
export interface From extends Locatable {
type: typeof FROM;
base: string;
imports: { path: string; name: string; location: Location }[];
}
export const DECORATOR = 'decorator';
export interface Decorator extends Locatable {
type: typeof DECORATOR;
decorator: string;
args: SyntaxNode[];
}
export const DECORATE = 'decorate';
export interface Decorate extends Locatable {
type: typeof DECORATE;
decorators: Decorator[];
def: SyntaxNode;
}
export const DEF = 'def';
export interface Def extends Locatable {
type: typeof DEF;
name: string;
params: Parameter[];
code: SyntaxNode[];
}
export const PARAMETER = 'parameter';
export interface Parameter extends Locatable {
type: typeof PARAMETER;
name: string;
anno: SyntaxNode;
default_value: SyntaxNode;
star: boolean;
starstar: boolean;
}
export const ASSIGN = 'assign';
export interface Assignment extends Locatable {
type: typeof ASSIGN;
op: string | undefined; // defined for augment e.g. +=
targets: SyntaxNode[];
sources: SyntaxNode[];
}
export const ASSERT = 'assert';
export interface Assert extends Locatable {
type: typeof ASSERT;
cond: SyntaxNode;
err: SyntaxNode;
}
export const PASS = 'pass';
export interface Pass extends Locatable {
type: typeof PASS;
}
export const RETURN = 'return';
export interface Return extends Locatable {
type: typeof RETURN;
values: SyntaxNode[];
}
export const YIELD = 'yield';
export interface Yield extends Locatable {
type: typeof YIELD;
value: SyntaxNode[];
from?: SyntaxNode;
}
export const RAISE = 'raise';
export interface Raise extends Locatable {
type: typeof RAISE;
err: SyntaxNode;
}
export const BREAK = 'break';
export interface Break extends Locatable {
type: typeof BREAK;
}
export const CONTINUE = 'continue';
export interface Continue extends Locatable {
type: typeof CONTINUE;
}
export const GLOBAL = 'global';
export interface Global extends Locatable {
type: typeof GLOBAL;
names: string[];
}
export const NONLOCAL = 'nonlocal';
export interface Nonlocal extends Locatable {
type: typeof NONLOCAL;
names: string[];
}
export const IF = 'if';
export interface If extends Locatable {
type: typeof IF;
cond: SyntaxNode;
code: SyntaxNode[];
elif: { cond: SyntaxNode; code: SyntaxNode[] }[];
else: Else;
}
export const WHILE = 'while';
export interface While extends Locatable {
type: typeof WHILE;
cond: SyntaxNode;
code: SyntaxNode[];
else: SyntaxNode[];
}
export const ELSE = 'else';
export interface Else extends Locatable {
type: typeof ELSE;
code: SyntaxNode[];
}
export const FOR = 'for';
export interface For extends Locatable {
type: typeof FOR;
target: SyntaxNode[];
iter: SyntaxNode[];
code: SyntaxNode[];
else?: SyntaxNode[];
decl_location: Location;
}
export const COMPFOR = 'comp_for';
export interface CompFor extends Locatable {
type: typeof COMPFOR;
for: SyntaxNode[];
in: SyntaxNode;
}
export const COMPIF = 'comp_if';
export interface CompIf extends Locatable {
type: typeof COMPIF;
test: SyntaxNode;
}
export const TRY = 'try';
export interface Try extends Locatable {
type: typeof TRY;
code: SyntaxNode[];
excepts: { cond: SyntaxNode; name: string; code: SyntaxNode[] }[];
else: SyntaxNode[];
finally: SyntaxNode[];
}
export const WITH = 'with';
export interface With extends Locatable {
type: typeof WITH;
items: { with: SyntaxNode; as: SyntaxNode }[];
code: SyntaxNode[];
}
export const CALL = 'call';
export interface Call extends Locatable {
type: typeof CALL;
func: SyntaxNode;
args: Argument[];
}
export const ARG = 'arg';
export interface Argument extends Locatable {
type: typeof ARG;
actual: SyntaxNode;
keyword?: SyntaxNode;
loop?: CompFor;
varargs?: boolean;
kwargs?: boolean;
}
export const INDEX = 'index';
export interface Index extends Locatable {
type: typeof INDEX;
value: SyntaxNode;
args: SyntaxNode[];
}
export const SLICE = 'slice';
export interface Slice extends Locatable {
type: typeof SLICE;
start?: SyntaxNode;
stop?: SyntaxNode;
step?: SyntaxNode;
}
export const DOT = 'dot';
export interface Dot extends Locatable {
type: typeof DOT;
value: SyntaxNode;
name: string;
}
export const IFEXPR = 'ifexpr';
export interface IfExpr extends Locatable {
type: typeof IFEXPR;
test: SyntaxNode;
then: SyntaxNode;
else: SyntaxNode;
}
export const LAMBDA = 'lambda';
export interface Lambda extends Locatable {
type: typeof LAMBDA;
args: Parameter[];
code: SyntaxNode;
}
export const UNOP = 'unop';
export interface UnaryOperator extends Locatable {
type: typeof UNOP;
op: string;
operand: SyntaxNode;
}
export const BINOP = 'binop';
export interface BinaryOperator extends Locatable {
type: typeof BINOP;
op: string;
left: SyntaxNode;
right: SyntaxNode;
}
export const STARRED = 'starred';
export interface Starred extends Locatable {
type: typeof STARRED;
value: SyntaxNode;
}
export const TUPLE = 'tuple';
export interface Tuple extends Locatable {
type: typeof TUPLE;
items: SyntaxNode[];
}
export const LIST = 'list';
export interface ListExpr extends Locatable {
type: typeof LIST;
items: SyntaxNode[];
}
export const SET = 'set';
export interface SetExpr extends Locatable {
type: typeof SET;
entries: SyntaxNode[];
comp_for?: SyntaxNode[];
}
export const DICT = 'dict';
export interface DictExpr extends Locatable {
type: typeof DICT;
entries: { k: SyntaxNode; v: SyntaxNode }[];
comp_for?: SyntaxNode[];
}
export const NAME = 'name';
export interface Name extends Locatable {
type: typeof NAME;
id: string;
}
export const LITERAL = 'literal';
export interface Literal extends Locatable {
type: typeof LITERAL;
value: any;
}
export const CLASS = 'class';
export interface Class extends Locatable {
type: typeof CLASS;
name: string;
extends: SyntaxNode[];
code: SyntaxNode[];
}
/*
UTILITY FUNCTIONS
*/
/**
* returns whether two syntax nodes are semantically equivalent
*/
export function isEquivalent(node1: SyntaxNode, node2: SyntaxNode): boolean {
if (!node1 || !node2) {
return node1 === node2;
}
return printNode(node1) === printNode(node2);
}
export function flatten<T>(arrayArrays: T[][]): T[] {
return [].concat(...arrayArrays);
}
/**
* Listener for pre-order traversal of the parse tree.
*/
export interface WalkListener {
/**
* Called whenever a node is entered.
*/
onEnterNode?(node: SyntaxNode, ancestors: SyntaxNode[]): void;
/**
* Called whenever a node is exited.
*/
onExitNode?(node: SyntaxNode, ancestors: SyntaxNode[]): void;
}
/**
* Preorder tree traversal with optional listener.
*/
export function walk(
node: SyntaxNode,
walkListener?: WalkListener
): SyntaxNode[] {
return walkRecursive(node, [], walkListener);
}
/**
* Recursive implementation of pre-order tree walk.
*/
// tslint:disable-next-line: max-func-body-length
function walkRecursive(
node: SyntaxNode,
ancestors?: SyntaxNode[],
walkListener?: WalkListener
): SyntaxNode[] {
if (!node) {
console.error('Node undefined. Ancestors:', ancestors);
return [];
}
ancestors.push(node);
if (walkListener && walkListener.onEnterNode) {
walkListener.onEnterNode(node, ancestors);
}
let children: SyntaxNode[] = [];
switch (node.type) {
case MODULE:
case DEF:
case CLASS:
children = node.code;
break;
case IF:
children = [node.cond]
.concat(node.code)
.concat(
node.elif ? flatten(node.elif.map(e => [e.cond].concat(e.code))) : []
)
.concat(node.else ? [node.else] : []);
break;
case ELSE:
children = node.code;
break;
case WHILE:
children = [node.cond].concat(node.code);
break;
case WITH:
children = flatten(node.items.map(r => [r.with, r.as])).concat(node.code);
break;
case FOR:
children = node.iter.concat(node.target).concat(node.code);
break;
case TRY:
children = node.code
.concat(
flatten(
(node.excepts || []).map(e =>
(e.cond ? [e.cond] : []).concat(e.code)
)
)
)
.concat(node.else || [])
.concat(node.finally || []);
break;
case DECORATE:
children = [node.def];
break;
case LAMBDA:
children = [node.code];
break;
case CALL:
children = [node.func].concat(node.args.map(a => a.actual));
break;
case IFEXPR:
children = [node.test, node.then, node.else];
break;
case COMPFOR:
children = node.for.concat([node.in]);
break;
case UNOP:
children = [node.operand];
break;
case BINOP:
children = [node.left, node.right];
break;
case STARRED:
children = [node.value];
break;
case SET:
children = node.entries.concat(node.comp_for ? node.comp_for : []);
break;
case LIST:
children = node.items;
break;
case TUPLE:
children = node.items;
break;
case DICT:
children = flatten(node.entries.map(p => [p.k, p.v])).concat(
node.comp_for ? node.comp_for : []
);
break;
case ASSIGN:
if (!node.sources) console.log(node);
children = node.sources.concat(node.targets);
break;
case ASSERT:
children = [node.cond].concat(node.err ? [node.err] : []);
break;
case DOT:
children = [node.value];
break;
case INDEX:
children = [node.value].concat(node.args);
break;
case SLICE:
children = (node.start ? [node.start] : [])
.concat(node.stop ? [node.stop] : [])
.concat(node.step ? [node.step] : []);
break;
case COMPFOR:
children = node.for.concat([node.in]);
break;
case COMPIF:
children = [node.test];
break;
case YIELD:
children = node.value ? node.value : [];
break;
case RETURN:
children = node.values ? node.values : [];
break;
case RAISE:
children = node.err ? [node.err] : [];
break;
case IFEXPR:
children = [node.test, node.then, node.else];
break;
}
let nodes = [node];
if (children.some(c => !c)) {
console.log('BAD CHILDREN', node);
}
let subtreeNodes = flatten(
children.map(node => walkRecursive(node, ancestors, walkListener))
);
nodes = nodes.concat(subtreeNodes);
if (walkListener && walkListener.onExitNode) {
walkListener.onExitNode(node, ancestors);
}
ancestors.pop();
return nodes;
}